Refactor: Made backup system more advanced.

Signed-off-by: HeCodes2Much <wayne6324@gmail.com>
This commit is contained in:
HeCodes2Much
2024-06-11 19:17:34 +01:00
parent b4baa7c0de
commit 6533a3c0be
6 changed files with 126 additions and 71 deletions

View File

@@ -37,7 +37,7 @@ android {
"proguard-rules.pro" "proguard-rules.pro"
) )
resValue("string", "app_name", "Easy Launcher (Debug)") 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") { getByName("release") {
@@ -48,7 +48,7 @@ android {
"proguard-rules.pro" "proguard-rules.pro"
) )
resValue("string", "app_name", "Easy Launcher") resValue("string", "app_name", "Easy Launcher")
resValue("string", "settings_backups_file", "autoBackup.ini") // resValue("string", "settings_backups_file", "autoBackup.ini")
} }
} }

View File

@@ -22,6 +22,7 @@ import android.provider.CalendarContract
import android.provider.Settings import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.util.Xml
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View 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.data.entities.AppInfo
import com.github.droidworksstudio.launcher.helper.PreferenceHelper import com.github.droidworksstudio.launcher.helper.PreferenceHelper
import com.github.droidworksstudio.launcher.ui.activities.FakeHomeActivity 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.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.OutputStreamWriter
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
import kotlin.math.pow import kotlin.math.pow
@@ -340,94 +347,146 @@ fun Context.isWorkProfileEnabled(): Boolean {
} }
} }
fun Context.backupSharedPreferences(backupFileName: String) { fun Context.backupSharedPreferences(backupFileNames: Array<String>) {
val sharedPreferences: SharedPreferences =
this.getSharedPreferences(Constants.PREFS_FILENAME, Context.MODE_PRIVATE)
val allPrefs = sharedPreferences.all
// Check if external storage is writable // Check if external storage is writable
if (!isExternalStorageWritable()) { if (!isExternalStorageWritable()) {
showLongToast("External storage is not writable.") showLongToast("External storage is not writable.")
return return
} }
val backupDir = ContextCompat.getExternalFilesDirs(this, null) val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if (backupDir.isEmpty()) { val backupDir = File(downloadDir, Constants.PACKAGE_NAME)
showLongToast("No external storage directories found.")
return // 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 { val backupFile = File(backupDir, "$backupFileName.xml")
backupFile.bufferedWriter().use { writer ->
for ((key, value) in allPrefs) { try {
if (value != null) { backupFile.bufferedWriter().use { writer ->
val line = when (value) { writer.write("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n")
is Boolean, is Int, is Float, is Long, is String -> "$key=$value\n" writer.write("<map>\n")
is Set<*> -> "$key=${value.joinToString(",")}\n"
else -> null // Loop through all preferences
} for ((key, value) in allPrefs) {
line?.let { if (value != null) {
writer.write(it) val valueString = value.toString().replace("'", "&apos;")
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("</map>")
} }
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 { private fun isExternalStorageWritable(): Boolean {
return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
} }
fun Context.restoreSharedPreferences(backupFileName: String) { fun Context.restoreSharedPreferences(backupFileNames: Array<String>) {
val sharedPreferences: SharedPreferences =
this.getSharedPreferences(Constants.PREFS_FILENAME, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
// Check if external storage is readable // Check if external storage is readable
if (!isExternalStorageReadable()) { if (!isExternalStorageReadable()) {
showLongToast("External storage is not readable.") showLongToast("External storage is not readable.")
return return
} }
val backupDir = getExternalFilesDir(null) val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val backupFile = File(backupDir, backupFileName) val backupDir = File(downloadDir, Constants.PACKAGE_NAME)
Log.d("backupFile", "$backupFile")
if (backupFile.exists()) { for (backupFileName in backupFileNames) {
try { val backupFile = File(backupDir, "$backupFileName.xml")
backupFile.forEachLine { line ->
val (key, value) = line.split("=", limit = 2)
when {
value.toBooleanStrictOrNull() != null -> editor.putBoolean(
key,
value.toBoolean()
)
value.toIntOrNull() != null -> editor.putInt(key, value.toInt()) if (backupFile.exists()) {
value.toFloatOrNull() != null -> editor.putFloat(key, value.toFloat()) try {
value.toLongOrNull() != null -> editor.putLong(key, value.toLong()) FileInputStream(backupFile).use { fileInputStream ->
value.contains(",") -> editor.putStringSet(key, value.split(",").toSet()) val factory = XmlPullParserFactory.newInstance()
else -> editor.putString(key, value) 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() } else {
showLongToast("Restore completed successfully.") showLongToast("Backup file for $backupFileName does not exist.")
} catch (e: IOException) {
e.printStackTrace()
showLongToast("Failed to restore SharedPreferences: ${e.message}")
} }
} 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 { private fun isExternalStorageReadable(): Boolean {
val state = Environment.getExternalStorageState() val state = Environment.getExternalStorageState()
return Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state return Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state

View File

@@ -5,6 +5,7 @@ import android.content.Intent
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.github.droidworksstudio.launcher.ui.activities.MainActivity
fun Fragment.showKeyboard() { fun Fragment.showKeyboard() {
val view = view?.findFocus() ?: return val view = view?.findFocus() ?: return
@@ -30,11 +31,7 @@ fun Fragment.showShortToast(message: String) {
} }
fun Fragment.restartApp() { fun Fragment.restartApp() {
val packageManager = requireContext().packageManager val intent = Intent(requireContext(), MainActivity::class.java)
val intent = packageManager.getLaunchIntentForPackage(requireContext().packageName) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) requireContext().startActivity(intent)
if (intent != null) {
startActivity(intent)
}
requireActivity().finish()
} }

View File

@@ -194,11 +194,13 @@ class AppHelper @Inject constructor() {
} }
fun backupSharedPreferences(context: Context) { 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) { 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 { fun getActionType(actionType: Constants.Swipe): NavOptions {

View File

@@ -66,10 +66,6 @@ class MainActivity : AppCompatActivity(), LocationListener {
private lateinit var locationManager: LocationManager private lateinit var locationManager: LocationManager
companion object {
private const val REQUEST_LOCATION_PERMISSION_CODE = 1001
}
private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@@ -206,7 +202,7 @@ class MainActivity : AppCompatActivity(), LocationListener {
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
this, this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_LOCATION_PERMISSION_CODE Constants.REQUEST_LOCATION_PERMISSION_CODE
) )
} else { } else {
// Permission already granted, proceed with getting location // Permission already granted, proceed with getting location
@@ -220,7 +216,7 @@ class MainActivity : AppCompatActivity(), LocationListener {
grantResults: IntArray grantResults: IntArray
) { ) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) 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) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, proceed with getting location // Permission granted, proceed with getting location
getLocation() getLocation()

View File

@@ -75,6 +75,7 @@ object Constants {
const val APP_GOOGLE_PLAY_STORE = "market://search?c=apps&q" const val APP_GOOGLE_PLAY_STORE = "market://search?c=apps&q"
const val REQUEST_INSTALL_PERMISSION = 123 const val REQUEST_INSTALL_PERMISSION = 123
const val REQUEST_LOCATION_PERMISSION_CODE = 246
const val TRIPLE_TAP_DELAY_MS = 300 const val TRIPLE_TAP_DELAY_MS = 300
const val LONG_PRESS_DELAY_MS = 500 const val LONG_PRESS_DELAY_MS = 500