diff --git a/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt b/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt index ceeb221bf..00a3da32d 100644 --- a/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/AdvancedSettings.kt @@ -1,15 +1,40 @@ package com.github.libretube.preferences +import android.net.Uri import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.preference.Preference import com.github.libretube.R import com.github.libretube.activities.SettingsActivity +import com.github.libretube.util.BackupHelper import com.github.libretube.views.MaterialPreferenceFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder class AdvancedSettings : MaterialPreferenceFragment() { val TAG = "AdvancedSettings" + /** + * result listeners for importing and exporting subscriptions + */ + private lateinit var getContent: ActivityResultLauncher + private lateinit var createFile: ActivityResultLauncher + + override fun onCreate(savedInstanceState: Bundle?) { + getContent = + registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + BackupHelper(requireContext()).restoreSharedPreferences(uri) + } + createFile = registerForActivityResult( + ActivityResultContracts.CreateDocument() + ) { uri: Uri? -> + BackupHelper(requireContext()).backupSharedPreferences(uri) + } + super.onCreate(savedInstanceState) + } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.advanced_settings, rootKey) @@ -21,6 +46,20 @@ class AdvancedSettings : MaterialPreferenceFragment() { showResetDialog() true } + + val backupSettings = findPreference(PreferenceKeys.BACKUP_SETTINGS) + backupSettings?.setOnPreferenceClickListener { + createFile.launch("preferences.xml") + activity?.recreate() + true + } + + val restoreSettings = findPreference(PreferenceKeys.RESTORE_SETTINGS) + restoreSettings?.setOnPreferenceClickListener { + getContent.launch("*/*") + activity?.recreate() + true + } } private fun showResetDialog() { diff --git a/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt b/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt index 12c2a9381..170d1e12d 100644 --- a/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt +++ b/app/src/main/java/com/github/libretube/preferences/InstanceSettings.kt @@ -127,8 +127,7 @@ class InstanceSettings : MaterialPreferenceFragment() { val deleteAccount = findPreference(PreferenceKeys.DELETE_ACCOUNT) deleteAccount?.setOnPreferenceClickListener { - val token = PreferenceHelper.getToken() - if (token != "") { + if (PreferenceHelper.getToken() != "") { val newFragment = DeleteAccountDialog() newFragment.show(childFragmentManager, DeleteAccountDialog::class.java.name) } else { diff --git a/app/src/main/java/com/github/libretube/preferences/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/preferences/PreferenceKeys.kt index 9dd059dbe..4074128fd 100644 --- a/app/src/main/java/com/github/libretube/preferences/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/preferences/PreferenceKeys.kt @@ -88,6 +88,8 @@ object PreferenceKeys { const val CLEAR_WATCH_HISTORY = "clear_watch_history" const val CLEAR_WATCH_POSITIONS = "clear_watch_positions" const val SHARE_WITH_TIME_CODE = "share_with_time_code" + const val BACKUP_SETTINGS = "backup_settings" + const val RESTORE_SETTINGS = "restore_settings" /** * History diff --git a/app/src/main/java/com/github/libretube/util/BackupHelper.kt b/app/src/main/java/com/github/libretube/util/BackupHelper.kt new file mode 100644 index 000000000..5e4df4fff --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/BackupHelper.kt @@ -0,0 +1,87 @@ +package com.github.libretube.util + +import android.content.Context +import android.content.SharedPreferences +import android.net.Uri +import androidx.preference.PreferenceManager +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +/** + * Backup and restore the preferences + */ +class BackupHelper( + private val context: Context +) { + private val TAG = this::class.java.name + + /** + * Backup the default shared preferences to a file + */ + fun backupSharedPreferences(uri: Uri?) { + if (uri == null) return + var output: ObjectOutputStream? = null + try { + val fileDescriptor = context.contentResolver.openFileDescriptor(uri, "w")?.fileDescriptor + output = ObjectOutputStream(FileOutputStream(fileDescriptor)) + val pref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + // write all preference objects to the output file + output.writeObject(pref.all) + } catch (e: Exception) { + e.printStackTrace() + } finally { + try { + // close the outputStream + if (output != null) { + output.flush() + output.close() + } + } catch (ex: IOException) { + ex.printStackTrace() + } + } + } + + /** + * restore the default shared preferences from a file + */ + @Suppress("UNCHECKED_CAST") + fun restoreSharedPreferences(uri: Uri?) { + if (uri == null) return + var input: ObjectInputStream? = null + try { + val fileDescriptor = context.contentResolver.openFileDescriptor(uri, "r")?.fileDescriptor + input = ObjectInputStream(FileInputStream(fileDescriptor)) + val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() + + // clear the previous settings + editor.clear() + + // map all the preference keys and their values + val entries = input.readObject() as Map + + // decide for each preference which type it is and save it to the preferences + for ((key, value) in entries) { + if (value is Boolean) editor.putBoolean(key, value) + else if (value is Float) editor.putFloat(key, value) + else if (value is Int) editor.putInt(key, value) + else if (value is Long) editor.putLong(key, value) + else if (value is String) editor.putString(key, value) + } + editor.commit() + } catch (e: Exception) { + e.printStackTrace() + } finally { + try { + if (input != null) { + input.close() + } + } catch (ex: IOException) { + ex.printStackTrace() + } + } + } +} diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml new file mode 100644 index 000000000..482848349 --- /dev/null +++ b/app/src/main/res/drawable/ic_backup.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 000000000..c280db227 --- /dev/null +++ b/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 534add96f..6f3ccd18c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -310,4 +310,6 @@ Zoom None Current + Backup & restore + Backup diff --git a/app/src/main/res/xml/advanced_settings.xml b/app/src/main/res/xml/advanced_settings.xml index 241e2c19b..59954a0c7 100644 --- a/app/src/main/res/xml/advanced_settings.xml +++ b/app/src/main/res/xml/advanced_settings.xml @@ -47,4 +47,18 @@ + + + + + + + + \ No newline at end of file