From 6533a3c0bebca63396f2ccc223cecc34200c384d Mon Sep 17 00:00:00 2001 From: HeCodes2Much Date: Tue, 11 Jun 2024 19:17:34 +0100 Subject: [PATCH] Refactor: Made backup system more advanced. Signed-off-by: HeCodes2Much --- app/build.gradle.kts | 4 +- .../common/ContextExtensions.kt | 167 ++++++++++++------ .../common/FragmentExtensions.kt | 11 +- .../launcher/helper/AppHelper.kt | 6 +- .../launcher/ui/activities/MainActivity.kt | 8 +- .../launcher/utils/Constants.kt | 1 + 6 files changed, 126 insertions(+), 71 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 50238ff..071a9f9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -37,7 +37,7 @@ android { "proguard-rules.pro" ) resValue("string", "app_name", "Easy Launcher (Debug)") - resValue("string", "settings_backups_file", "autoBackup.debug.ini") +// resValue("string", "settings_backups_file", "autoBackup.debug.ini") } getByName("release") { @@ -48,7 +48,7 @@ android { "proguard-rules.pro" ) resValue("string", "app_name", "Easy Launcher") - resValue("string", "settings_backups_file", "autoBackup.ini") +// resValue("string", "settings_backups_file", "autoBackup.ini") } } diff --git a/app/src/main/java/com/github/droidworksstudio/common/ContextExtensions.kt b/app/src/main/java/com/github/droidworksstudio/common/ContextExtensions.kt index ad3f743..64a0308 100644 --- a/app/src/main/java/com/github/droidworksstudio/common/ContextExtensions.kt +++ b/app/src/main/java/com/github/droidworksstudio/common/ContextExtensions.kt @@ -22,6 +22,7 @@ import android.provider.CalendarContract import android.provider.Settings import android.util.DisplayMetrics import android.util.Log +import android.util.Xml import android.view.ContextThemeWrapper import android.view.LayoutInflater import android.view.View @@ -40,8 +41,14 @@ import com.github.droidworksstudio.launcher.utils.Constants import com.github.droidworksstudio.launcher.data.entities.AppInfo import com.github.droidworksstudio.launcher.helper.PreferenceHelper import com.github.droidworksstudio.launcher.ui.activities.FakeHomeActivity +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserFactory +import org.xmlpull.v1.XmlSerializer import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream import java.io.IOException +import java.io.OutputStreamWriter import java.util.Calendar import java.util.Date import kotlin.math.pow @@ -340,94 +347,146 @@ fun Context.isWorkProfileEnabled(): Boolean { } } -fun Context.backupSharedPreferences(backupFileName: String) { - val sharedPreferences: SharedPreferences = - this.getSharedPreferences(Constants.PREFS_FILENAME, Context.MODE_PRIVATE) - val allPrefs = sharedPreferences.all - +fun Context.backupSharedPreferences(backupFileNames: Array) { // Check if external storage is writable if (!isExternalStorageWritable()) { showLongToast("External storage is not writable.") return } - val backupDir = ContextCompat.getExternalFilesDirs(this, null) - if (backupDir.isEmpty()) { - showLongToast("No external storage directories found.") - return + val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val backupDir = File(downloadDir, Constants.PACKAGE_NAME) + + // Ensure the directory exists, create it if necessary + if (!backupDir.exists()) { + backupDir.mkdirs() } - val backupFile = File(backupDir[0], backupFileName) + for (backupFileName in backupFileNames) { + val sharedPreferences: SharedPreferences = + this.getSharedPreferences(backupFileName, Context.MODE_PRIVATE) + val allPrefs = sharedPreferences.all - try { - backupFile.bufferedWriter().use { writer -> - for ((key, value) in allPrefs) { - if (value != null) { - val line = when (value) { - is Boolean, is Int, is Float, is Long, is String -> "$key=$value\n" - is Set<*> -> "$key=${value.joinToString(",")}\n" - else -> null - } - line?.let { - writer.write(it) + val backupFile = File(backupDir, "$backupFileName.xml") + + try { + backupFile.bufferedWriter().use { writer -> + writer.write("\n") + writer.write("\n") + + // Loop through all preferences + for ((key, value) in allPrefs) { + if (value != null) { + val valueString = value.toString().replace("'", "'") + val line = when (value) { + is Boolean, is Int, is Float, is Long, is String -> "\t<${value::class.simpleName!!.lowercase()} name='$key' value='$valueString' />\n" + else -> null + } + line?.let { + writer.write(it) + } } } + + writer.write("") } + showLongToast("Backup for $backupFileName completed successfully.") + } catch (e: IOException) { + e.printStackTrace() + showLongToast("Failed to backup SharedPreferences $backupFileName: ${e.message}") } - showLongToast("Backup completed successfully.") - } catch (e: IOException) { - e.printStackTrace() - showLongToast("Failed to backup SharedPreferences: ${e.message}") } } + private fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } -fun Context.restoreSharedPreferences(backupFileName: String) { - val sharedPreferences: SharedPreferences = - this.getSharedPreferences(Constants.PREFS_FILENAME, Context.MODE_PRIVATE) - val editor = sharedPreferences.edit() - +fun Context.restoreSharedPreferences(backupFileNames: Array) { // Check if external storage is readable if (!isExternalStorageReadable()) { showLongToast("External storage is not readable.") return } - val backupDir = getExternalFilesDir(null) - val backupFile = File(backupDir, backupFileName) - Log.d("backupFile", "$backupFile") + val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val backupDir = File(downloadDir, Constants.PACKAGE_NAME) - if (backupFile.exists()) { - try { - backupFile.forEachLine { line -> - val (key, value) = line.split("=", limit = 2) - when { - value.toBooleanStrictOrNull() != null -> editor.putBoolean( - key, - value.toBoolean() - ) + for (backupFileName in backupFileNames) { + val backupFile = File(backupDir, "$backupFileName.xml") - value.toIntOrNull() != null -> editor.putInt(key, value.toInt()) - value.toFloatOrNull() != null -> editor.putFloat(key, value.toFloat()) - value.toLongOrNull() != null -> editor.putLong(key, value.toLong()) - value.contains(",") -> editor.putStringSet(key, value.split(",").toSet()) - else -> editor.putString(key, value) + if (backupFile.exists()) { + try { + FileInputStream(backupFile).use { fileInputStream -> + val factory = XmlPullParserFactory.newInstance() + factory.isNamespaceAware = true + val xpp = factory.newPullParser() + xpp.setInput(fileInputStream, null) + + var eventType = xpp.eventType + var key: String? = null + var value: Any? = null + + while (eventType != XmlPullParser.END_DOCUMENT) { + when (eventType) { + XmlPullParser.START_TAG -> { + when (xpp.name) { + "boolean", "int", "float", "long", "string" -> { + key = xpp.getAttributeValue(null, "name") + val valueString = xpp.getAttributeValue(null, "value") + value = when (xpp.name) { + "boolean" -> valueString.toBoolean() + "int" -> valueString.toInt() + "float" -> valueString.toFloat() + "long" -> valueString.toLong() + else -> valueString + } + } + } + } + + XmlPullParser.END_TAG -> { + when (xpp.name) { + "boolean", "int", "float", "long", "string" -> { + if (key != null && value != null) { + saveToSharedPreferences(backupFileName, key, value) + key = null + value = null + } + } + } + } + } + eventType = xpp.next() + } } + showLongToast("Restore for $backupFileName completed successfully.") + } catch (e: Exception) { + Log.e("Exception", e.toString()) + showLongToast("Failed to restore SharedPreferences $backupFileName: ${e.message}") } - editor.apply() - showLongToast("Restore completed successfully.") - } catch (e: IOException) { - e.printStackTrace() - showLongToast("Failed to restore SharedPreferences: ${e.message}") + } else { + showLongToast("Backup file for $backupFileName does not exist.") } - } else { - showLongToast("Backup file does not exist.") } } +private fun Context.saveToSharedPreferences(prefsFileName: String, key: String, value: Any) { + val sharedPreferences: SharedPreferences = this.getSharedPreferences(prefsFileName, Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + + when (value) { + is Boolean -> editor.putBoolean(key, value) + is Int -> editor.putInt(key, value) + is Float -> editor.putFloat(key, value) + is Long -> editor.putLong(key, value) + is String -> editor.putString(key, value) + } + + editor.apply() +} + private fun isExternalStorageReadable(): Boolean { val state = Environment.getExternalStorageState() return Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state diff --git a/app/src/main/java/com/github/droidworksstudio/common/FragmentExtensions.kt b/app/src/main/java/com/github/droidworksstudio/common/FragmentExtensions.kt index 8028778..5814d44 100644 --- a/app/src/main/java/com/github/droidworksstudio/common/FragmentExtensions.kt +++ b/app/src/main/java/com/github/droidworksstudio/common/FragmentExtensions.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.fragment.app.Fragment +import com.github.droidworksstudio.launcher.ui.activities.MainActivity fun Fragment.showKeyboard() { val view = view?.findFocus() ?: return @@ -30,11 +31,7 @@ fun Fragment.showShortToast(message: String) { } fun Fragment.restartApp() { - val packageManager = requireContext().packageManager - val intent = packageManager.getLaunchIntentForPackage(requireContext().packageName) - intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - if (intent != null) { - startActivity(intent) - } - requireActivity().finish() + val intent = Intent(requireContext(), MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + requireContext().startActivity(intent) } \ No newline at end of file diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/helper/AppHelper.kt b/app/src/main/java/com/github/droidworksstudio/launcher/helper/AppHelper.kt index f6d3dff..a7ba2a9 100644 --- a/app/src/main/java/com/github/droidworksstudio/launcher/helper/AppHelper.kt +++ b/app/src/main/java/com/github/droidworksstudio/launcher/helper/AppHelper.kt @@ -194,11 +194,13 @@ class AppHelper @Inject constructor() { } fun backupSharedPreferences(context: Context) { - context.backupSharedPreferences(context.getString(R.string.settings_backups_file)) + val backupFileNames = arrayOf(Constants.PREFS_FILENAME, Constants.WEATHER_PREFS) + context.backupSharedPreferences(backupFileNames) } fun restoreSharedPreferences(context: Context) { - context.restoreSharedPreferences(context.getString(R.string.settings_backups_file)) + val backupFileNames = arrayOf(Constants.PREFS_FILENAME, Constants.WEATHER_PREFS) + context.restoreSharedPreferences(backupFileNames) } fun getActionType(actionType: Constants.Swipe): NavOptions { diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt index 92e8aec..119a70b 100644 --- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt @@ -66,10 +66,6 @@ class MainActivity : AppCompatActivity(), LocationListener { private lateinit var locationManager: LocationManager - companion object { - private const val REQUEST_LOCATION_PERMISSION_CODE = 1001 - } - private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var binding: ActivityMainBinding @@ -206,7 +202,7 @@ class MainActivity : AppCompatActivity(), LocationListener { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), - REQUEST_LOCATION_PERMISSION_CODE + Constants.REQUEST_LOCATION_PERMISSION_CODE ) } else { // Permission already granted, proceed with getting location @@ -220,7 +216,7 @@ class MainActivity : AppCompatActivity(), LocationListener { grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) { + if (requestCode == Constants.REQUEST_LOCATION_PERMISSION_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission granted, proceed with getting location getLocation() diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt b/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt index 0dbe118..bf8330b 100644 --- a/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt +++ b/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt @@ -75,6 +75,7 @@ object Constants { const val APP_GOOGLE_PLAY_STORE = "market://search?c=apps&q" const val REQUEST_INSTALL_PERMISSION = 123 + const val REQUEST_LOCATION_PERMISSION_CODE = 246 const val TRIPLE_TAP_DELAY_MS = 300 const val LONG_PRESS_DELAY_MS = 500