From f61e969d636d653ad07de48168dacc30ef0703fa Mon Sep 17 00:00:00 2001 From: Saalim Quadri Date: Thu, 4 Sep 2025 16:52:29 +0530 Subject: [PATCH 1/3] feat: Remove "daily" choice for automatic update and weekly as default * Migrate Anonymous users to use weekly as default Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/3554 Signed-off-by: Saalim Quadri --- .../data/preference/AppLoungePreference.kt | 21 +++++++++++---- .../updates/UpdatesBroadcastReceiver.kt | 7 ++++- .../e/apps/ui/settings/SettingsFragment.kt | 26 +++++++++++++++++-- app/src/main/res/values/arrays.xml | 10 +++++++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/settings_preferences.xml | 11 ++++++++ 6 files changed, 69 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt b/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt index 1a67ad7ea..ee0b3581a 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt @@ -28,6 +28,7 @@ import foundation.e.apps.R import foundation.e.apps.data.Constants.PREFERENCE_SHOW_FOSS import foundation.e.apps.data.Constants.PREFERENCE_SHOW_GPLAY import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA +import foundation.e.apps.data.enums.User import javax.inject.Inject import javax.inject.Singleton @@ -35,7 +36,8 @@ import javax.inject.Singleton @Singleton @OpenForTesting class AppLoungePreference @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + private val appLoungeDataStore: AppLoungeDataStore ) { private val preferenceManager = PreferenceManager.getDefaultSharedPreferences(context) @@ -63,10 +65,19 @@ class AppLoungePreference @Inject constructor( fun enableOpenSource() = preferenceManager.edit { putBoolean(PREFERENCE_SHOW_FOSS, true) } fun enablePwa() = preferenceManager.edit { putBoolean(PREFERENCE_SHOW_PWA, true) } - fun getUpdateInterval() = preferenceManager.getString( - context.getString(R.string.update_check_intervals), - context.getString(R.string.preference_update_interval_default) - )!!.toLong() + fun getUpdateInterval(): Long { + val currentUser = appLoungeDataStore.getUserType() + return when (currentUser) { + User.ANONYMOUS -> preferenceManager.getString( + context.getString(R.string.update_check_intervals_anonymous), + context.getString(R.string.preference_update_interval_default_anonymous) + )!!.toLong() + else -> preferenceManager.getString( + context.getString(R.string.update_check_intervals), + context.getString(R.string.preference_update_interval_default) + )!!.toLong() + } + } fun shouldUpdateAppsFromOtherStores() = preferenceManager.getBoolean( context.getString(R.string.update_apps_from_other_stores), diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesBroadcastReceiver.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesBroadcastReceiver.kt index 36e89a247..7cebb848d 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesBroadcastReceiver.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesBroadcastReceiver.kt @@ -21,15 +21,20 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import androidx.work.ExistingPeriodicWorkPolicy +import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.preference.AppLoungePreference import timber.log.Timber +import javax.inject.Inject +@AndroidEntryPoint class UpdatesBroadcastReceiver : BroadcastReceiver() { + @Inject + lateinit var appLoungePreference: AppLoungePreference + override fun onReceive(context: Context, intent: Intent) { Timber.d("onReceive: ${intent.action}") if (intent.action in listOf(Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED)) { - val appLoungePreference = AppLoungePreference(context) val interval = appLoungePreference.getUpdateInterval() UpdatesWorkManager.enqueueWork(context, interval, ExistingPeriodicWorkPolicy.REPLACE) } diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index 0fd753a7a..07c645b2e 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -30,6 +30,7 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import androidx.preference.CheckBoxPreference +import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.Preference.OnPreferenceChangeListener import androidx.preference.PreferenceFragmentCompat @@ -99,8 +100,10 @@ class SettingsFragment : PreferenceFragmentCompat() { troubleShootPreference = findPreference(getString(R.string.having_troubles)) val updateCheckInterval = - preferenceManager.findPreference(getString(R.string.update_check_intervals)) - updateCheckInterval?.setOnPreferenceChangeListener { _, newValue -> + preferenceManager.findPreference(getString(R.string.update_check_intervals)) + val updateCheckIntervalAnonymous = + preferenceManager.findPreference(getString(R.string.update_check_intervals_anonymous)) + val updateChangeListener = { _: Preference, newValue: Any -> Timber.d("onCreatePreferences: updated Value: $newValue") context?.let { UpdatesWorkManager.enqueueWork( @@ -111,6 +114,9 @@ class SettingsFragment : PreferenceFragmentCompat() { } true } + updateCheckInterval?.setOnPreferenceChangeListener(updateChangeListener) + updateCheckIntervalAnonymous?.setOnPreferenceChangeListener(updateChangeListener) + configureUpdatePreferencesForUser(updateCheckInterval, updateCheckIntervalAnonymous) val versionInfo = findPreference("versionInfo") versionInfo?.apply { @@ -318,4 +324,20 @@ class SettingsFragment : PreferenceFragmentCompat() { stores.disableStore(source) } } + + private fun configureUpdatePreferencesForUser( + updateCheckInterval: ListPreference?, + updateCheckIntervalAnonymous: ListPreference? + ) { + when (user) { + User.ANONYMOUS -> { + updateCheckInterval?.isVisible = false + updateCheckIntervalAnonymous?.isVisible = true + } + else -> { + updateCheckInterval?.isVisible = true + updateCheckIntervalAnonymous?.isVisible = false + } + } + } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3707b7b8b..1dfa7f893 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -30,4 +30,14 @@ 720 + + @string/weekly + @string/monthly + + + + 168 + 720 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5cc09a634..4ef9a7b1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,6 +51,7 @@ Updates 24 + 168 Update check interval Only on un-metered networks Update apps automatically only on un-metered networks such as Wi-Fi @@ -164,6 +165,7 @@ Update of %1$s apps completed on %2$s. updateCheckIntervals + updateCheckIntervalsAnonymous updateNotify updateInstallAuto updateUnmeteredOnly diff --git a/app/src/main/res/xml/settings_preferences.xml b/app/src/main/res/xml/settings_preferences.xml index e00efe484..be838f899 100644 --- a/app/src/main/res/xml/settings_preferences.xml +++ b/app/src/main/res/xml/settings_preferences.xml @@ -45,6 +45,17 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + Date: Thu, 4 Sep 2025 17:09:03 +0530 Subject: [PATCH 2/3] feat: Migrate current anonymous users to use weekly interval Signed-off-by: Saalim Quadri --- .../foundation/e/apps/AppLoungeApplication.kt | 1 + .../data/preference/AppLoungePreference.kt | 37 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 39 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt index b73bdd45a..6adbcb225 100644 --- a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +++ b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt @@ -103,6 +103,7 @@ class AppLoungeApplication : Application(), Configuration.Provider { }) } + appLoungePreference.migrateAnonymousUserUpdateInterval() UpdatesWorkManager.enqueueWork( this, appLoungePreference.getUpdateInterval(), diff --git a/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt b/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt index ee0b3581a..66c62314b 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/AppLoungePreference.kt @@ -83,4 +83,41 @@ class AppLoungePreference @Inject constructor( context.getString(R.string.update_apps_from_other_stores), true ) + + fun migrateAnonymousUserUpdateInterval() { + val updateIntervals = context.resources.getStringArray(R.array.update_interval_values) + val daily = updateIntervals[0] // 24 + val weekly = updateIntervals[1] // 168 + val monthly = updateIntervals[2] // 720 + val migrationCompleted = preferenceManager.getBoolean( + context.getString(R.string.anonymous_update_migration_completed), + false + ) + + if (migrationCompleted) return + + if (appLoungeDataStore.getUserType() == User.ANONYMOUS) { + val currentInterval = preferenceManager.getString( + context.getString(R.string.update_check_intervals), + null + ) + currentInterval?.let { interval -> + val newVal = when (interval) { + daily -> weekly + weekly, monthly -> interval + else -> context.getString(R.string.preference_update_interval_default_anonymous) + } + preferenceManager.edit { + putString( + context.getString(R.string.update_check_intervals_anonymous), + newVal + ) + } + } + } + + preferenceManager.edit { + putBoolean(context.getString(R.string.anonymous_update_migration_completed), true) + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ef9a7b1f..756bce911 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -166,6 +166,7 @@ updateCheckIntervals updateCheckIntervalsAnonymous + anonymousUpdateMigrationCompleted updateNotify updateInstallAuto updateUnmeteredOnly -- GitLab From b0cd839d04deb08387bcf6714b38fda0cd2cf027 Mon Sep 17 00:00:00 2001 From: Saalim Quadri Date: Thu, 4 Sep 2025 17:17:36 +0530 Subject: [PATCH 3/3] test: Pass appLoungeDataStore param Signed-off-by: Saalim Quadri --- .../e/apps/FakeAppLoungePreference.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/foundation/e/apps/FakeAppLoungePreference.kt b/app/src/test/java/foundation/e/apps/FakeAppLoungePreference.kt index 00e3c57b3..240f9b744 100644 --- a/app/src/test/java/foundation/e/apps/FakeAppLoungePreference.kt +++ b/app/src/test/java/foundation/e/apps/FakeAppLoungePreference.kt @@ -19,9 +19,20 @@ package foundation.e.apps import android.content.Context +import foundation.e.apps.data.enums.User +import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference +import io.mockk.every +import io.mockk.mockk -class FakeAppLoungePreference(context: Context) : AppLoungePreference(context) { +class FakeAppLoungePreference(private val context: Context) : AppLoungePreference(context, createMockDataStore()) { + companion object { + private fun createMockDataStore(): AppLoungeDataStore { + val mockDataStore = mockk(relaxed = true) + every { mockDataStore.getUserType() } returns User.ANONYMOUS + return mockDataStore + } + } var isPWASelectedFake = false var isOpenSourceelectedFake = false var isGplaySelectedFake = false @@ -50,4 +61,10 @@ class FakeAppLoungePreference(context: Context) : AppLoungePreference(context) { override fun shouldUpdateAppsFromOtherStores(): Boolean { return shouldUpdateFromOtherStores } + + override fun getUpdateInterval(): Long { + val updateIntervals = context.resources.getStringArray(R.array.update_interval_values) + + return updateIntervals[1].toLong() // 168 (Weekly Interval) + } } -- GitLab