From b874d5556965d6772ddd1428dfd79a2859001c99 Mon Sep 17 00:00:00 2001 From: HeCodes2Much Date: Mon, 2 Dec 2024 10:42:45 +0000 Subject: [PATCH] Feature: Added the ability to backup and restore apps. --- .../launcher/data/dao/AppInfoDAO.kt | 6 ++ .../launcher/helper/AppHelper.kt | 67 ++++++++++++++++++- .../launcher/ui/activities/MainActivity.kt | 26 +++++++ .../ui/settings/SettingsAdvancedFragment.kt | 17 ++++- .../launcher/utils/Constants.kt | 6 +- app/src/main/res/values/strings.xml | 8 ++- 6 files changed, 122 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/data/dao/AppInfoDAO.kt b/app/src/main/java/com/github/droidworksstudio/launcher/data/dao/AppInfoDAO.kt index 9b79fc5..cfd2d02 100644 --- a/app/src/main/java/com/github/droidworksstudio/launcher/data/dao/AppInfoDAO.kt +++ b/app/src/main/java/com/github/droidworksstudio/launcher/data/dao/AppInfoDAO.kt @@ -19,6 +19,12 @@ interface AppInfoDAO { @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertAll(apps: List) + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun restoreAll(apps: List) + + @Query("DELETE FROM app") + suspend fun clearAll() + @Update suspend fun update(app: AppInfo) 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 28f5e16..e060571 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 @@ -31,10 +31,14 @@ import com.github.droidworksstudio.common.showLongToast import com.github.droidworksstudio.launcher.BuildConfig import com.github.droidworksstudio.launcher.R import com.github.droidworksstudio.launcher.accessibility.ActionService +import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO +import com.github.droidworksstudio.launcher.data.entities.AppInfo import com.github.droidworksstudio.launcher.helper.weather.WeatherResponse import com.github.droidworksstudio.launcher.utils.Constants import com.github.droidworksstudio.launcher.utils.WeatherApiService import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import kotlinx.coroutines.flow.first import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.net.UnknownHostException @@ -316,7 +320,7 @@ class AppHelper @Inject constructor() { fun storeFile(activity: Activity) { // Generate a unique filename with a timestamp val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) - val fileName = "backup_$timeStamp.json" + val fileName = "Settings_backup_$timeStamp.json" val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) @@ -334,6 +338,67 @@ class AppHelper @Inject constructor() { activity.startActivityForResult(intent, Constants.BACKUP_READ, null) } + fun storeFileApps(activity: Activity) { + // Generate a unique filename with a timestamp + val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val fileName = "Apps_backup_$timeStamp.json" + + val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/json" + putExtra(Intent.EXTRA_TITLE, fileName) + } + activity.startActivityForResult(intent, Constants.BACKUP_WRITE_APPS, null) + } + + fun loadFileApps(activity: Activity) { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/json" + } + activity.startActivityForResult(intent, Constants.BACKUP_READ_APPS, null) + } + + suspend fun backupAppInfo(context: Context, dao: AppInfoDAO, uri: Uri) { + try { + dao.getAllAppsFlow() + .first() // Get the first emission from the Flow + .let { allApps -> + val gson = Gson() + val jsonString = gson.toJson(allApps) + + // Write JSON to the selected URI + context.contentResolver.openOutputStream(uri)?.use { outputStream -> + outputStream.write(jsonString.toByteArray()) + } ?: throw Exception("Failed to open output stream") + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + suspend fun restoreAppInfo(context: Context, dao: AppInfoDAO, uri: Uri) { + try { + // Open an InputStream from the selected Uri + context.contentResolver.openInputStream(uri)?.use { inputStream -> + // Read the content from the InputStream + val jsonString = inputStream.bufferedReader().use { it.readText() } + + // Convert JSON to List + val gson = Gson() + val type = object : TypeToken>() {}.type + val appInfoList: List = gson.fromJson(jsonString, type) + + // Reinsert data into the database + dao.restoreAll(appInfoList) + } ?: throw Exception("Failed to open input stream from URI") + + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun getActionType(actionType: Constants.Swipe): NavOptions { return when (actionType) { Constants.Swipe.DoubleTap -> { 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 fa298d9..bbf4318 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 @@ -34,10 +34,12 @@ import com.github.droidworksstudio.common.isTablet import com.github.droidworksstudio.common.showLongToast import com.github.droidworksstudio.common.showShortToast import com.github.droidworksstudio.launcher.R +import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO import com.github.droidworksstudio.launcher.databinding.ActivityMainBinding import com.github.droidworksstudio.launcher.helper.AppHelper import com.github.droidworksstudio.launcher.helper.AppReloader import com.github.droidworksstudio.launcher.helper.PreferenceHelper +import com.github.droidworksstudio.launcher.repository.AppInfoRepository import com.github.droidworksstudio.launcher.utils.Constants import com.github.droidworksstudio.launcher.viewmodel.AppViewModel import com.github.droidworksstudio.launcher.viewmodel.PreferenceViewModel @@ -65,9 +67,15 @@ class MainActivity : AppCompatActivity() { @Inject lateinit var preferenceHelper: PreferenceHelper + @Inject + lateinit var appInfoRepository: AppInfoRepository + @Inject lateinit var appHelper: AppHelper + @Inject + lateinit var appDao: AppInfoDAO + private lateinit var sharedPreferences: SharedPreferences private lateinit var handler: Handler @@ -96,6 +104,7 @@ class MainActivity : AppCompatActivity() { setupNavController() setupOrientation() setupLocationManager() + setLanguage() } @Suppress("DEPRECATION") @@ -406,6 +415,23 @@ class MainActivity : AppCompatActivity() { } applicationContext.showShortToast(getString(R.string.settings_reload_app_backup)) } + + Constants.BACKUP_WRITE_APPS -> { + data?.data?.also { uri -> + lifecycleScope.launch { + appHelper.backupAppInfo(applicationContext, appDao, uri) + } + } + } + + Constants.BACKUP_READ_APPS -> { + data?.data?.also { uri -> + lifecycleScope.launch { + appHelper.restoreAppInfo(applicationContext, appDao, uri) + } + } + AppReloader.restartApp(applicationContext) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/settings/SettingsAdvancedFragment.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/settings/SettingsAdvancedFragment.kt index 234305d..8201ea2 100644 --- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/settings/SettingsAdvancedFragment.kt +++ b/app/src/main/java/com/github/droidworksstudio/launcher/ui/settings/SettingsAdvancedFragment.kt @@ -12,10 +12,12 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.github.droidworksstudio.common.resetDefaultLauncher import com.github.droidworksstudio.launcher.R +import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO import com.github.droidworksstudio.launcher.databinding.FragmentSettingsAdvancedBinding import com.github.droidworksstudio.launcher.helper.AppHelper import com.github.droidworksstudio.launcher.helper.AppReloader @@ -23,6 +25,7 @@ import com.github.droidworksstudio.launcher.helper.PreferenceHelper import com.github.droidworksstudio.launcher.listener.ScrollEventListener import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -38,6 +41,9 @@ class SettingsAdvancedFragment : Fragment(), @Inject lateinit var appHelper: AppHelper + @Inject + lateinit var appDAO: AppInfoDAO + private lateinit var navController: NavController private lateinit var context: Context @@ -125,8 +131,10 @@ class SettingsAdvancedFragment : Fragment(), // Define the items for the dialog (Backup, Restore, Clear Data) val items = arrayOf( - getString(R.string.advanced_settings_backup_restore_backup), - getString(R.string.advanced_settings_backup_restore_restore), + getString(R.string.advanced_settings_backup_restore_backup_prefs), + getString(R.string.advanced_settings_backup_restore_restore_prefs), + getString(R.string.advanced_settings_backup_restore_backup_apps), + getString(R.string.advanced_settings_backup_restore_restore_apps), getString(R.string.advanced_settings_backup_restore_clear) ) @@ -136,6 +144,8 @@ class SettingsAdvancedFragment : Fragment(), when (which) { 0 -> appHelper.storeFile(requireActivity()) 1 -> appHelper.loadFile(requireActivity()) + 2 -> appHelper.storeFileApps(requireActivity()) + 3 -> appHelper.loadFileApps(requireActivity()) else -> confirmClearData() } } @@ -159,6 +169,9 @@ class SettingsAdvancedFragment : Fragment(), private fun clearData() { preferenceHelper.clearAll(context) + lifecycleScope.launch { + appDAO.clearAll() + } Handler(Looper.getMainLooper()).postDelayed({ AppReloader.restartApp(context) }, 500) 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 28537a9..a7a4d68 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 @@ -25,6 +25,8 @@ object Constants { const val LATITUDE = "LATITUDE" const val LONGITUDE = "LONGITUDE" + const val LOCATION_DENIED = "LOCATION_DENIED" + const val FIRST_LAUNCH = "FIRST_LAUNCH" const val SHOW_DATE = "SHOW_DATE" const val SHOW_TIME = "SHOW_TIME" @@ -34,7 +36,6 @@ object Constants { const val SHOW_STATUS_BAR = "SHOW_STATUS_BAR" const val SHOW_NAVIGATION_BAR = "SHOW_NAVIGATION_BAR" - const val SHOW_WEATHER_WIDGET = "SHOW_WEATHER_WIDGET" const val SHOW_WEATHER_WIDGET_SUN_SET_RISE = "SHOW_WEATHER_WIDGET_SUN_SET_RISE" const val SHOW_BATTERY_WIDGET = "SHOW_BATTERY_WIDGET" @@ -114,7 +115,8 @@ object Constants { const val BACKUP_WRITE = 987 const val BACKUP_READ = 876 - const val LOCATION_DENIED = "LOCATION_DENIED" + const val BACKUP_WRITE_APPS = 456 + const val BACKUP_READ_APPS = 654 const val TRIPLE_TAP_DELAY_MS = 300 const val LONG_PRESS_DELAY_MS = 500 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index af6b2a5..380ad8f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,9 +41,11 @@ Backup/Restore Please be careful what you wish for? - Backup Preferences - Restore Preferences - Clear Preferences + Backup Preferences + Restore Preferences + Backup Apps + Restore Apps + Clear All Data Clear Preferences Are you sure you want to do this?