diff --git a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.kt b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.kt index eb972cca..ee7a7e2c 100644 --- a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.kt +++ b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.kt @@ -36,6 +36,8 @@ class SettingsActivity : BaseActivity() { val navController = findNavController(R.id.fragment_container) appBarConfiguration = AppBarConfiguration(navController.graph) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setDisplayHomeAsUpEnabled(true) setupActionBarWithNavController(navController, appBarConfiguration) } diff --git a/app/src/main/java/app/fedilab/android/helper/SettingsStorage.java b/app/src/main/java/app/fedilab/android/helper/SettingsStorage.java new file mode 100644 index 00000000..677e42fe --- /dev/null +++ b/app/src/main/java/app/fedilab/android/helper/SettingsStorage.java @@ -0,0 +1,134 @@ +package app.fedilab.android.helper; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import static app.fedilab.android.BaseMainActivity.currentAccount; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Environment; + +import androidx.preference.PreferenceManager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Date; +import java.util.Map; + +import app.fedilab.android.R; + + +//From https://stackoverflow.com/a/10864463 + +public class SettingsStorage { + + + public static boolean saveSharedPreferencesToFile(Context context) { + boolean res = false; + ObjectOutputStream output = null; + String fileName = "Fedilab_settings_export_" + Helper.dateFileToString(context, new Date()) + ".txt"; + String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); + String fullPath = filePath + "/" + fileName; + File dst = new File(fullPath); + try { + output = new ObjectOutputStream(new FileOutputStream(dst)); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + output.writeObject(sharedpreferences.getAll()); + res = true; + String message = context.getString(R.string.data_export_theme_success); + Intent intentOpen = new Intent(); + intentOpen.setAction(android.content.Intent.ACTION_VIEW); + Uri uri = Uri.parse("file://" + fullPath); + intentOpen.setDataAndType(uri, "text/txt"); + String title = context.getString(R.string.data_export_settings); + Helper.notify_user(context, currentAccount, intentOpen, BitmapFactory.decodeResource(context.getResources(), + R.mipmap.ic_launcher), Helper.NotifType.BACKUP, title, message); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (output != null) { + output.flush(); + output.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return res; + } + + @SuppressLint("ApplySharedPref") + @SuppressWarnings({"unchecked", "UnnecessaryUnboxing"}) + public static boolean loadSharedPreferencesFromFile(Context context, File src) { + boolean res = false; + ObjectInputStream input = null; + try { + input = new ObjectInputStream(new FileInputStream(src)); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor prefEdit = sharedpreferences.edit(); + prefEdit.clear(); + Map entries = (Map) input.readObject(); + for (Map.Entry entry : entries.entrySet()) { + Object v = entry.getValue(); + String key = entry.getKey(); + //We skip some values + if (key.compareTo(Helper.PREF_USER_ID) == 0) { + continue; + } + if (key.compareTo(Helper.PREF_INSTANCE) == 0) { + continue; + } + if (key.compareTo(Helper.PREF_USER_INSTANCE) == 0) { + continue; + } + if (key.compareTo(Helper.PREF_USER_TOKEN) == 0) { + continue; + } + if (v instanceof Boolean) + prefEdit.putBoolean(key, ((Boolean) v).booleanValue()); + else if (v instanceof Float) + prefEdit.putFloat(key, ((Float) v).floatValue()); + else if (v instanceof Integer) + prefEdit.putInt(key, ((Integer) v).intValue()); + else if (v instanceof Long) + prefEdit.putLong(key, ((Long) v).longValue()); + else if (v instanceof String) + prefEdit.putString(key, ((String) v)); + } + prefEdit.commit(); + res = true; + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return res; + } +} diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentSettingsCategories.kt b/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentSettingsCategories.kt index 4205449a..fcb96c74 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentSettingsCategories.kt +++ b/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentSettingsCategories.kt @@ -14,15 +14,23 @@ package app.fedilab.android.ui.fragment.settings * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.Manifest +import android.content.pm.PackageManager import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.navigation.fragment.findNavController import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import app.fedilab.android.BaseMainActivity.currentAccount import app.fedilab.android.R +import app.fedilab.android.helper.SettingsStorage + class FragmentSettingsCategories : PreferenceFragmentCompat() { + private val REQUEST_CODE = 5412 + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.pref_categories, rootKey) @@ -61,6 +69,25 @@ class FragmentSettingsCategories : PreferenceFragmentCompat() { false } + findPreference(getString(R.string.pref_export_settings))?.setOnPreferenceClickListener { + val permissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + if (isGranted) { + SettingsStorage.saveSharedPreferencesToFile(context) + } else { + requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_CODE) + } + } + permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + false + } + + findPreference(getString(R.string.pref_import_settings))?.setOnPreferenceClickListener { + + false + } + val adminPreference = findPreference(getString(R.string.pref_category_key_administration)) adminPreference?.isVisible = currentAccount.admin adminPreference?.setOnPreferenceClickListener { false } @@ -70,4 +97,16 @@ class FragmentSettingsCategories : PreferenceFragmentCompat() { false } } + + @Deprecated("Deprecated in Java") + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + when (requestCode) { + REQUEST_CODE -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + SettingsStorage.saveSharedPreferencesToFile(context) + } else { + Toast.makeText(context, getString(R.string.permission_missing), Toast.LENGTH_SHORT).show() + } + else -> {} + } + } } diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml new file mode 100644 index 00000000..8afaa1b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml new file mode 100644 index 00000000..6315ba8f --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b0845cab..c58d7a91 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -616,6 +616,7 @@ Use a custom theme Theming The theme was exported + The settings were exported The theme has been successfully exported in CSV Import a theme Tap here to import a theme from a previous export @@ -1428,4 +1429,10 @@ pref_category_theming pref_category_administration pref_category_languages + pref_export_settings + pref_import_settings + + Export settings + Import settings + Permission not granted! diff --git a/app/src/main/res/xml/pref_categories.xml b/app/src/main/res/xml/pref_categories.xml index a1a10073..a7e6dfb4 100644 --- a/app/src/main/res/xml/pref_categories.xml +++ b/app/src/main/res/xml/pref_categories.xml @@ -67,4 +67,19 @@ app:icon="@drawable/ic_language" app:key="@string/pref_category_key_languages" /> + + + + +