Added backup and restore options

This commit is contained in:
ottoptj 2024-09-07 03:22:02 +03:00
commit 93835821e4
3 changed files with 166 additions and 1 deletions

View file

@ -1,18 +1,35 @@
package eu.ottop.yamlauncher.settings 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.os.Bundle
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import eu.ottop.yamlauncher.R import eu.ottop.yamlauncher.R
import eu.ottop.yamlauncher.databinding.ActivitySettingsBinding import eu.ottop.yamlauncher.databinding.ActivitySettingsBinding
import org.json.JSONObject
import java.io.IOException
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
private lateinit var preferences: SharedPreferences
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
private lateinit var performBackup: ActivityResultLauncher<Intent>
private lateinit var performRestore: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
preferences = PreferenceManager.getDefaultSharedPreferences(this@SettingsActivity)
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = "Launcher Settings" supportActionBar?.title = "Launcher Settings"
supportActionBar?.setDisplayShowTitleEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(true)
@ -25,10 +42,25 @@ class SettingsActivity : AppCompatActivity() {
supportFragmentManager.addOnBackStackChangedListener { supportFragmentManager.addOnBackStackChangedListener {
updateActionBarTitle() 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 { override fun onSupportNavigateUp(): Boolean {
// Handle the back arrow button in the ActionBar
if (supportFragmentManager.backStackEntryCount > 0) { if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack() supportFragmentManager.popBackStack()
} else { } 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
}
}
} }

View file

@ -27,6 +27,8 @@ class SettingsFragment : PreferenceFragmentCompat(), TitleProvider {
val appMenuSettings = findPreference<Preference>("appMenuSettings") val appMenuSettings = findPreference<Preference>("appMenuSettings")
val hiddenPref = findPreference<Preference>("hiddenApps") val hiddenPref = findPreference<Preference>("hiddenApps")
val backupPref = findPreference<Preference>("backup")
val restorePref = findPreference<Preference>("restore")
val aboutPref = findPreference<Preference>("aboutPage") val aboutPref = findPreference<Preference>("aboutPage")
val resetPref = findPreference<Preference>("resetAll") val resetPref = findPreference<Preference>("resetAll")
@ -60,6 +62,16 @@ class SettingsFragment : PreferenceFragmentCompat(), TitleProvider {
uiUtils.switchFragment(requireActivity(), HiddenAppsFragment()) uiUtils.switchFragment(requireActivity(), HiddenAppsFragment())
true } true }
backupPref?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
(requireActivity() as SettingsActivity).createBackup()
true }
restorePref?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
(requireActivity() as SettingsActivity).restoreBackup()
true }
aboutPref?.onPreferenceClickListener = aboutPref?.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
uiUtils.switchFragment(requireActivity(), AboutFragment()) uiUtils.switchFragment(requireActivity(), AboutFragment())

View file

@ -49,6 +49,26 @@
app:summary="Unhide Apps" app:summary="Unhide Apps"
app:title="Manage Hidden Apps" /> app:title="Manage Hidden Apps" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:allowDividerAbove="false"
app:title="Backup and Restore">
<Preference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:key="backup"
app:selectable="true"
app:summary="Backup your settings"
app:title="Backup" />
<Preference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:key="restore"
app:selectable="true"
app:summary="Restore a backup"
app:title="Restore" />
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"