diff --git a/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsActivity.kt b/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsActivity.kt index b2935a0..0dd9d4a 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsActivity.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsActivity.kt @@ -1,18 +1,35 @@ package eu.ottop.yamlauncher.settings +import android.app.Activity +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.preference.PreferenceManager import eu.ottop.yamlauncher.R import eu.ottop.yamlauncher.databinding.ActivitySettingsBinding +import org.json.JSONObject +import java.io.IOException class SettingsActivity : AppCompatActivity() { + private lateinit var preferences: SharedPreferences private lateinit var binding: ActivitySettingsBinding + private lateinit var performBackup: ActivityResultLauncher + private lateinit var performRestore: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + preferences = PreferenceManager.getDefaultSharedPreferences(this@SettingsActivity) + binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) + supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.title = "Launcher Settings" supportActionBar?.setDisplayShowTitleEnabled(true) @@ -25,10 +42,25 @@ class SettingsActivity : AppCompatActivity() { supportFragmentManager.addOnBackStackChangedListener { updateActionBarTitle() } + + performBackup = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + result.data?.data?.let { uri -> + saveSharedPreferencesToFile(uri) + } + } + } + + performRestore = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + result.data?.data?.let { uri -> + restoreSharedPreferencesFromFile(uri) + } + } + } } override fun onSupportNavigateUp(): Boolean { - // Handle the back arrow button in the ActionBar if (supportFragmentManager.backStackEntryCount > 0) { supportFragmentManager.popBackStack() } else { @@ -44,6 +76,107 @@ class SettingsActivity : AppCompatActivity() { } } + fun createBackup() { + val createFileIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/json" + putExtra(Intent.EXTRA_TITLE, "yamlauncher_backup.json") + } + performBackup.launch(createFileIntent) + } + private fun saveSharedPreferencesToFile(uri: Uri) { + val sharedPreferences = preferences + val allEntries = sharedPreferences.all + + val backupData = JSONObject().apply { + put("app_id", application.packageName) + val data = JSONObject() + for ((key, value) in allEntries) { + val entry = JSONObject().apply { + when (value) { + is String -> put("value", value).put("type", "String") + is Int -> put("value", value).put("type", "Int") + is Boolean -> put("value", value).put("type", "Boolean") + is Long -> put("value", value).put("type", "Long") + is Float -> put("value", value).put("type", "Float") + } + } + data.put(key, entry) + } + put("data", data) + } + + val sharedPreferencesText = backupData.toString(4) + + try { + contentResolver.openOutputStream(uri)?.use { outputStream -> + outputStream.write(sharedPreferencesText.toByteArray()) + } + Toast.makeText(this, "Backup successful :)", Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(this, "Backup failed :(", Toast.LENGTH_SHORT).show() + } + } + + fun restoreBackup() { + val openFileIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/json" + } + performRestore.launch(openFileIntent) + } + + private fun restoreSharedPreferencesFromFile(uri: Uri) { + val jsonData = readJsonFile(uri) + if (jsonData != null) { + try { + val backupData = JSONObject(jsonData) + if (backupData.getString("app_id") != application.packageName) { + throw IllegalArgumentException("Not a YAM Launcher backup") + } + val data = backupData.getJSONObject("data") + + val editor = preferences.edit() + + val keys = data.keys() + + while (keys.hasNext()){ + val key = keys.next() + val entry = data.getJSONObject(key) + val type = entry.getString("type") + + when (type) { + "String" -> editor.putString(key, entry.getString("value")) + "Int" -> editor.putInt(key, entry.getInt("value")) + "Boolean" -> editor.putBoolean(key, entry.getBoolean("value")) + "Long" -> editor.putLong(key, entry.getLong("value")) + "Float" -> editor.putFloat(key, entry.getDouble("value").toFloat()) + } + } + + editor.apply() + + Toast.makeText(this, "Restore successful :)", Toast.LENGTH_SHORT).show() + } catch(e: IllegalArgumentException) { + Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Toast.makeText(this, "Restore failed :( (corrupt file?)", Toast.LENGTH_SHORT).show() + } + } else { + Toast.makeText(this, "Failed to read file", Toast.LENGTH_SHORT).show() + } + } + + private fun readJsonFile(uri: Uri): String? { + return try { + contentResolver.openInputStream(uri)?.bufferedReader().use { reader -> + reader?.readText() + } + } catch (e: Exception) { + null + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsFragment.kt b/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsFragment.kt index ab5e7fe..6cde41c 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsFragment.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/settings/SettingsFragment.kt @@ -27,6 +27,8 @@ class SettingsFragment : PreferenceFragmentCompat(), TitleProvider { val appMenuSettings = findPreference("appMenuSettings") val hiddenPref = findPreference("hiddenApps") + val backupPref = findPreference("backup") + val restorePref = findPreference("restore") val aboutPref = findPreference("aboutPage") val resetPref = findPreference("resetAll") @@ -60,6 +62,16 @@ class SettingsFragment : PreferenceFragmentCompat(), TitleProvider { uiUtils.switchFragment(requireActivity(), HiddenAppsFragment()) true } + backupPref?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + (requireActivity() as SettingsActivity).createBackup() + true } + + restorePref?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + (requireActivity() as SettingsActivity).restoreBackup() + true } + aboutPref?.onPreferenceClickListener = Preference.OnPreferenceClickListener { uiUtils.switchFragment(requireActivity(), AboutFragment()) diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 29b6be8..c4bd95a 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -49,6 +49,26 @@ app:summary="Unhide Apps" app:title="Manage Hidden Apps" /> + + + +