From 82cd8ea239eb49f148233e4acd85359c26643ecb Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 13 Nov 2024 13:58:52 +0100 Subject: [PATCH] Add option to import and option to export app preferences --- .../settings/BackupSettingsFragment.java | 145 ++++++++++++++++++ .../res/drawable/outline_reset_wrench_24.xml | 5 + .../drawable/rounded_settings_b_roll_24.xml | 5 + .../rounded_settings_backup_restore_24.xml | 5 + src/main/res/values/strings.xml | 7 + src/main/res/xml/preferences_backup.xml | 14 ++ 6 files changed, 181 insertions(+) create mode 100644 src/main/res/drawable/outline_reset_wrench_24.xml create mode 100644 src/main/res/drawable/rounded_settings_b_roll_24.xml create mode 100644 src/main/res/drawable/rounded_settings_backup_restore_24.xml diff --git a/src/main/java/eu/siacs/conversations/ui/fragment/settings/BackupSettingsFragment.java b/src/main/java/eu/siacs/conversations/ui/fragment/settings/BackupSettingsFragment.java index 18fc724c8..2ca66d54e 100644 --- a/src/main/java/eu/siacs/conversations/ui/fragment/settings/BackupSettingsFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/fragment/settings/BackupSettingsFragment.java @@ -1,9 +1,11 @@ package eu.siacs.conversations.ui.fragment.settings; import android.Manifest; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; +import androidx.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; @@ -30,14 +32,27 @@ import com.google.common.primitives.Longs; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.ui.activity.SettingsActivity; +import eu.siacs.conversations.utils.ChatBackgroundHelper; import eu.siacs.conversations.worker.ExportBackupWorker; +import me.drakeet.support.toast.ToastCompat; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Map; import java.util.concurrent.TimeUnit; public class BackupSettingsFragment extends XmppPreferenceFragment { public static final String CREATE_ONE_OFF_BACKUP = "create_one_off_backup"; private static final String RECURRING_BACKUP = "recurring_backup"; + public static final int REQUEST_EXPORT_SETTINGS = 0xbf8701; + public static final int REQUEST_IMPORT_SETTINGS = 0xbf8703; private final ActivityResultLauncher requestStorageForBackupLauncher = registerForActivityResult( @@ -75,6 +90,26 @@ public class BackupSettingsFragment extends XmppPreferenceFragment { recurringBackup, R.array.recurring_backup_values, value -> timeframeValueToName(requireContext(), value)); + + final var importSettingsPreference = findPreference("import_settings"); + if (importSettingsPreference != null) { + importSettingsPreference.setOnPreferenceClickListener(preference -> { + if (requireSettingsActivity().hasStoragePermission(REQUEST_IMPORT_SETTINGS)) { + importSettings(); + } + return true; + }); + } + + final var exportSettingsPreference = findPreference("export_settings"); + if (exportSettingsPreference != null) { + exportSettingsPreference.setOnPreferenceClickListener(preference -> { + if (requireSettingsActivity().hasStoragePermission(REQUEST_EXPORT_SETTINGS)) { + exportSettings(); + } + return true; + }); + } } @Override @@ -128,6 +163,27 @@ public class BackupSettingsFragment extends XmppPreferenceFragment { requireActivity().setTitle(R.string.backup); } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (grantResults.length > 0) { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == REQUEST_EXPORT_SETTINGS) { + exportSettings(); + } + if (requestCode == REQUEST_IMPORT_SETTINGS) { + importSettings(); + } + } else { + ToastCompat.makeText( + requireActivity(), + + R.string.no_storage_permission, + ToastCompat.LENGTH_SHORT).show(); + } + } + } + private boolean onBackupPreferenceClicked(final Preference preference) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission( @@ -157,4 +213,93 @@ public class BackupSettingsFragment extends XmppPreferenceFragment { builder.setPositiveButton(R.string.ok, null); builder.create().show(); } + + @SuppressWarnings({ "unchecked" }) + private boolean importSettings() { + boolean success; + ObjectInputStream input = null; + try { + final File file = new File(FileBackend.getBackupDirectory(requireContext()).getAbsolutePath() + "/settings.dat"); + input = new ObjectInputStream(new FileInputStream(file)); + SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(requireSettingsActivity()).edit(); + prefEdit.clear(); + Map entries = (Map) input.readObject(); + for (Map.Entry entry : entries.entrySet()) { + Object value = entry.getValue(); + String key = entry.getKey(); + + if (value instanceof Boolean) + prefEdit.putBoolean(key, ((Boolean) value).booleanValue()); + else if (value instanceof Float) + prefEdit.putFloat(key, ((Float) value).floatValue()); + else if (value instanceof Integer) + prefEdit.putInt(key, ((Integer) value).intValue()); + else if (value instanceof Long) + prefEdit.putLong(key, ((Long) value).longValue()); + else if (value instanceof String) + prefEdit.putString(key, ((String) value)); + } + prefEdit.commit(); + success = true; + } catch (Exception e) { + success = false; + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + if (success) { + new Thread(() -> runOnUiThread(() -> requireActivity().recreate())).start(); + ToastCompat.makeText(requireActivity(), R.string.success_import_settings, ToastCompat.LENGTH_SHORT).show(); + } else { + ToastCompat.makeText(requireActivity(), R.string.error_import_settings, ToastCompat.LENGTH_SHORT).show(); + } + return success; + } + + private boolean exportSettings() { + boolean success = false; + ObjectOutputStream output = null; + try { + final File file = new File(FileBackend.getBackupDirectory(requireContext()).getAbsolutePath(), "settings.dat"); + final File directory = file.getParentFile(); + if (directory != null && directory.mkdirs()) { + Log.d(Config.LOGTAG, "created backup directory " + directory.getAbsolutePath()); + } + output = new ObjectOutputStream(new FileOutputStream(file)); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(requireSettingsActivity()); + output.writeObject(pref.getAll()); + success = true; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (output != null) { + output.flush(); + output.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return success; + } + + public SettingsActivity requireSettingsActivity() { + final var activity = requireActivity(); + if (activity instanceof SettingsActivity settingsActivity) { + return settingsActivity; + } + throw new IllegalStateException( + String.format( + "%s is not %s", + activity.getClass().getName(), SettingsActivity.class.getName())); + } } diff --git a/src/main/res/drawable/outline_reset_wrench_24.xml b/src/main/res/drawable/outline_reset_wrench_24.xml new file mode 100644 index 000000000..02e54108c --- /dev/null +++ b/src/main/res/drawable/outline_reset_wrench_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/rounded_settings_b_roll_24.xml b/src/main/res/drawable/rounded_settings_b_roll_24.xml new file mode 100644 index 000000000..25037030a --- /dev/null +++ b/src/main/res/drawable/rounded_settings_b_roll_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/rounded_settings_backup_restore_24.xml b/src/main/res/drawable/rounded_settings_backup_restore_24.xml new file mode 100644 index 000000000..719168091 --- /dev/null +++ b/src/main/res/drawable/rounded_settings_backup_restore_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 08fbe77c8..5b0e0af85 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1320,4 +1320,11 @@ Reset the default DNS servers Reset DNS DNS reset + Settings + Import settings + Import the settings.dat from the backup folder + Export settings + Export the monocles chat app settings to the backup folder + Settings successfully imported + Error while importing the settings \ No newline at end of file diff --git a/src/main/res/xml/preferences_backup.xml b/src/main/res/xml/preferences_backup.xml index b0362c785..55e8a6d00 100644 --- a/src/main/res/xml/preferences_backup.xml +++ b/src/main/res/xml/preferences_backup.xml @@ -17,4 +17,18 @@ android:key="backup_directory" android:summary="@string/pref_create_backup_summary" /> + + + + + \ No newline at end of file