diff --git a/apps/flipcash/app/src/main/AndroidManifest.xml b/apps/flipcash/app/src/main/AndroidManifest.xml index d2da9715c..215bf8f4e 100644 --- a/apps/flipcash/app/src/main/AndroidManifest.xml +++ b/apps/flipcash/app/src/main/AndroidManifest.xml @@ -43,6 +43,8 @@ + + + + + diff --git a/apps/flipcash/app/src/main/res/xml/data_extraction_rules.xml b/apps/flipcash/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 000000000..479f3be74 --- /dev/null +++ b/apps/flipcash/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayReleaseStageProvider.kt b/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayReleaseStageProvider.kt index 4be4a61a2..e90db3abd 100644 --- a/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayReleaseStageProvider.kt +++ b/apps/flipcash/shared/appupdates/src/main/kotlin/com/flipcash/app/updates/internal/GooglePlayReleaseStageProvider.kt @@ -11,6 +11,7 @@ import com.flipcash.app.updates.resolveStage import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.serialization.json.Json +import java.io.File import okhttp3.OkHttpClient import okhttp3.Request import timber.log.Timber @@ -31,6 +32,14 @@ internal class GooglePlayReleaseStageProvider(private val context: Context) : Re private set override suspend fun loadCachedStage(versionCode: Int): ReleaseStage? { + // Clear cached manifest restored from backup on fresh install. + val marker = File(context.noBackupFilesDir, "release-stage-initialized") + if (!marker.exists()) { + context.releaseStageDataStore.edit { it.clear() } + marker.createNewFile() + return null + } + val cached = context.releaseStageDataStore.data .map { prefs -> prefs[MANIFEST_KEY] } .firstOrNull() diff --git a/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/internal/InternalFeatureFlagController.kt b/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/internal/InternalFeatureFlagController.kt index 337cdc44b..02ffe72af 100644 --- a/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/internal/InternalFeatureFlagController.kt +++ b/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/internal/InternalFeatureFlagController.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import java.io.File import javax.inject.Inject internal class InternalFeatureFlagController @Inject constructor( @@ -49,6 +50,15 @@ internal class InternalFeatureFlagController @Inject constructor( FeatureFlag.entries .filter { it.launched } .onEach { reset(it) } + + // Clear beta flags restored from backup on fresh install. + // noBackupFilesDir is never included in Auto Backup, so the marker + // file won't exist after a restore — triggering a full reset. + val marker = File(context.noBackupFilesDir, "beta-flags-initialized") + if (!marker.exists()) { + reset() + marker.createNewFile() + } } override fun enableBetaFeatures() { diff --git a/apps/flipcash/shared/userflags/src/main/kotlin/com/flipcash/app/userflags/UserFlagsCoordinator.kt b/apps/flipcash/shared/userflags/src/main/kotlin/com/flipcash/app/userflags/UserFlagsCoordinator.kt index 6ad583a2d..4895d29c6 100644 --- a/apps/flipcash/shared/userflags/src/main/kotlin/com/flipcash/app/userflags/UserFlagsCoordinator.kt +++ b/apps/flipcash/shared/userflags/src/main/kotlin/com/flipcash/app/userflags/UserFlagsCoordinator.kt @@ -13,6 +13,7 @@ import com.flipcash.services.models.UserFlags import com.flipcash.services.user.UserManager import com.getcode.opencode.model.financial.Fiat import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.File import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.SharingStarted @@ -58,6 +59,16 @@ class UserFlagsCoordinator @Inject constructor( private val scope = CoroutineScope(SupervisorJob() + dispatchers.IO) + init { + // Delete the backing file before DataStore reads it to avoid a race + // where stale overrides restored from backup are briefly visible. + val marker = File(context.noBackupFilesDir, "user-flag-overrides-initialized") + if (!marker.exists()) { + context.preferencesDataStoreFile("user-flag-overrides").delete() + marker.createNewFile() + } + } + private val dataStore = PreferenceDataStoreFactory.create( corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() }, scope = scope,