diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt index 369a551dd36..bc511f51aa2 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt @@ -5,7 +5,7 @@ import androidx.core.content.edit import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.core.data.util.decodeFromStringOrNull import com.bitwarden.data.datasource.disk.BaseDiskSource -import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import com.x8bit.bitwarden.data.platform.manager.model.AppResumeScreenData import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType @@ -47,7 +47,6 @@ private const val CREATE_ACTION_COUNT = "createActionCount" private const val SHOULD_SHOW_ADD_LOGIN_COACH_MARK = "shouldShowAddLoginCoachMark" private const val SHOULD_SHOW_GENERATOR_COACH_MARK = "shouldShowGeneratorCoachMark" private const val RESUME_SCREEN = "resumeScreen" -private const val FLIGHT_RECORDER_KEY = "flightRecorderData" private const val IS_DYNAMIC_COLORS_ENABLED = "isDynamicColorsEnabled" private const val BROWSER_AUTOFILL_DIALOG_RESHOW_TIME = "browserAutofillDialogReshowTime" @@ -58,8 +57,10 @@ private const val BROWSER_AUTOFILL_DIALOG_RESHOW_TIME = "browserAutofillDialogRe class SettingsDiskSourceImpl( private val sharedPreferences: SharedPreferences, private val json: Json, + flightRecorderDiskSource: FlightRecorderDiskSource, ) : BaseDiskSource(sharedPreferences = sharedPreferences), - SettingsDiskSource { + SettingsDiskSource, + FlightRecorderDiskSource by flightRecorderDiskSource { private val mutableAppLanguageFlow = bufferedMutableSharedFlow(replay = 1) private val mutableAppThemeFlow = bufferedMutableSharedFlow(replay = 1) @@ -92,8 +93,6 @@ class SettingsDiskSourceImpl( private val mutableHasUserLoggedInOrCreatedAccountFlow = bufferedMutableSharedFlow() - private val mutableFlightRecorderDataFlow = bufferedMutableSharedFlow() - private val mutableHasSeenAddLoginCoachMarkFlow = bufferedMutableSharedFlow() private val mutableHasSeenGeneratorCoachMarkFlow = bufferedMutableSharedFlow() @@ -214,20 +213,6 @@ class SettingsDiskSourceImpl( get() = mutableHasUserLoggedInOrCreatedAccountFlow .onSubscription { emit(getBoolean(HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY)) } - override var flightRecorderData: FlightRecorderDataSet? - get() = getString(key = FLIGHT_RECORDER_KEY) - ?.let { json.decodeFromStringOrNull(it) } - set(value) { - putString( - key = FLIGHT_RECORDER_KEY, - value = value?.let { json.encodeToString(it) }, - ) - mutableFlightRecorderDataFlow.tryEmit(value) - } - - override val flightRecorderDataFlow: Flow - get() = mutableFlightRecorderDataFlow.onSubscription { emit(flightRecorderData) } - override var browserAutofillDialogReshowTime: Instant? get() = getLong(key = BROWSER_AUTOFILL_DIALOG_RESHOW_TIME)?.let { Instant.ofEpochMilli(it) } set(value) { diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/di/PlatformDiskModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/di/PlatformDiskModule.kt index b9aca699905..a06daf2f57a 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/di/PlatformDiskModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/di/PlatformDiskModule.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.SharedPreferences import androidx.room.Room import com.bitwarden.core.data.manager.dispatcher.DispatcherManager +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.datasource.disk.di.EncryptedPreferences import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource @@ -139,10 +140,12 @@ object PlatformDiskModule { fun provideSettingsDiskSource( @UnencryptedPreferences sharedPreferences: SharedPreferences, json: Json, + flightRecorderDiskSource: FlightRecorderDiskSource, ): SettingsDiskSource = SettingsDiskSourceImpl( sharedPreferences = sharedPreferences, json = json, + flightRecorderDiskSource = flightRecorderDiskSource, ) @Provides diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt index d456cc84fa2..1dff3bae484 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt @@ -12,8 +12,6 @@ import com.bitwarden.core.data.manager.toast.ToastManagerImpl import com.bitwarden.cxf.registry.CredentialExchangeRegistry import com.bitwarden.cxf.registry.dsl.credentialExchangeRegistry import com.bitwarden.data.manager.NativeLibraryManager -import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager -import com.bitwarden.data.manager.flightrecorder.FlightRecorderWriter import com.bitwarden.data.repository.ServerConfigRepository import com.bitwarden.network.BitwardenServiceClient import com.bitwarden.network.service.EventService @@ -106,22 +104,6 @@ object PlatformManagerModule { application: Application, ): AppStateManager = AppStateManagerImpl(application = application) - @Provides - @Singleton - fun provideFlightRecorderManager( - @ApplicationContext context: Context, - clock: Clock, - dispatcherManager: DispatcherManager, - settingsDiskSource: SettingsDiskSource, - flightRecorderWriter: FlightRecorderWriter, - ): FlightRecorderManager = FlightRecorderManager.create( - context = context, - clock = clock, - dispatcherManager = dispatcherManager, - flightRecorderDiskSource = settingsDiskSource, - flightRecorderWriter = flightRecorderWriter, - ) - @Provides @Singleton fun provideAuthenticatorBridgeProcessor( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index 6a54aa1a264..573837bf189 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -9,9 +9,9 @@ import com.bitwarden.network.model.OrganizationType import com.bitwarden.network.model.PolicyTypeJson import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson -import com.bitwarden.network.model.createMockPolicy import com.bitwarden.network.model.UserDecryptionJson import com.bitwarden.network.model.UserDecryptionOptionsJson +import com.bitwarden.network.model.createMockPolicy import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.ForcePasswordResetReason diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt index fedde6dbc0f..46ccf01d7fc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt @@ -7,6 +7,7 @@ import android.service.autofill.FillResponse import android.service.autofill.SaveInfo import android.view.View import android.view.autofill.AutofillId +import com.bitwarden.core.data.util.mockBuilder import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo import com.x8bit.bitwarden.data.autofill.model.AutofillCipher import com.x8bit.bitwarden.data.autofill.model.AutofillPartition @@ -16,7 +17,6 @@ import com.x8bit.bitwarden.data.autofill.model.FilledPartition import com.x8bit.bitwarden.data.autofill.util.buildDataset import com.x8bit.bitwarden.data.autofill.util.buildVaultItemDataset import com.x8bit.bitwarden.data.autofill.util.createAutofillCallbackIntentSender -import com.x8bit.bitwarden.data.util.mockBuilder import io.mockk.every import io.mockk.mockk import io.mockk.mockkConstructor diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/SaveInfoBuilderTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/SaveInfoBuilderTest.kt index c95e7891821..769d4a631ee 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/SaveInfoBuilderTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/builder/SaveInfoBuilderTest.kt @@ -4,10 +4,10 @@ import android.service.autofill.FillRequest import android.service.autofill.SaveInfo import android.view.View import android.view.autofill.AutofillId +import com.bitwarden.core.data.util.mockBuilder import com.x8bit.bitwarden.data.autofill.model.AutofillPartition import com.x8bit.bitwarden.data.autofill.model.AutofillView import com.x8bit.bitwarden.data.platform.repository.SettingsRepository -import com.x8bit.bitwarden.data.util.mockBuilder import io.mockk.every import io.mockk.mockk import io.mockk.mockkConstructor diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt index d8fe3f651c3..34efa8de4aa 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt @@ -12,6 +12,7 @@ import android.view.autofill.AutofillId import android.view.autofill.AutofillValue import android.widget.RemoteViews import android.widget.inline.InlinePresentationSpec +import com.bitwarden.core.data.util.mockBuilder import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo import com.x8bit.bitwarden.data.autofill.model.AutofillCipher import com.x8bit.bitwarden.data.autofill.model.AutofillPartition @@ -19,7 +20,6 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillView import com.x8bit.bitwarden.data.autofill.model.FilledData import com.x8bit.bitwarden.data.autofill.model.FilledItem import com.x8bit.bitwarden.data.autofill.model.FilledPartition -import com.x8bit.bitwarden.data.util.mockBuilder import com.x8bit.bitwarden.ui.autofill.buildVaultItemAutofillRemoteViews import com.x8bit.bitwarden.ui.autofill.util.createVaultItemInlinePresentationOrNull import io.mockk.every diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledItemExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledItemExtensionsTest.kt index 66d380a4723..c773fcdfd0a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledItemExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledItemExtensionsTest.kt @@ -6,8 +6,8 @@ import android.service.autofill.Presentations import android.view.autofill.AutofillId import android.view.autofill.AutofillValue import android.widget.RemoteViews +import com.bitwarden.core.data.util.mockBuilder import com.x8bit.bitwarden.data.autofill.model.FilledItem -import com.x8bit.bitwarden.data.util.mockBuilder import io.mockk.every import io.mockk.mockk import io.mockk.mockkConstructor diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledPartitionExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledPartitionExtensionsTest.kt index 5f7b985be2f..7624fe4fe8a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledPartitionExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledPartitionExtensionsTest.kt @@ -8,11 +8,11 @@ import android.service.autofill.InlinePresentation import android.service.autofill.Presentations import android.widget.RemoteViews import android.widget.inline.InlinePresentationSpec +import com.bitwarden.core.data.util.mockBuilder import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo import com.x8bit.bitwarden.data.autofill.model.AutofillCipher import com.x8bit.bitwarden.data.autofill.model.FilledItem import com.x8bit.bitwarden.data.autofill.model.FilledPartition -import com.x8bit.bitwarden.data.util.mockBuilder import com.x8bit.bitwarden.ui.autofill.buildAutofillRemoteViews import com.x8bit.bitwarden.ui.autofill.util.createCipherInlinePresentationOrNull import io.mockk.every diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt index 47eeab2d2b6..f023706b7ea 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt @@ -8,13 +8,13 @@ import androidx.credentials.provider.BeginGetPasswordOption import androidx.credentials.provider.BeginGetPublicKeyCredentialOption import androidx.credentials.provider.PasswordCredentialEntry import androidx.credentials.provider.PublicKeyCredentialEntry +import com.bitwarden.core.data.util.mockBuilder import com.bitwarden.core.util.isBuildVersionAtLeast import com.bitwarden.fido.Fido2CredentialAutofillView import com.bitwarden.vault.CipherListView import com.bitwarden.vault.CipherListViewType import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager -import com.x8bit.bitwarden.data.util.mockBuilder import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialAutofillView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginListView diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt index 415c86fc133..a3966ac5f87 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt @@ -5,14 +5,13 @@ import app.cash.turbine.test import com.bitwarden.core.data.util.decodeFromStringOrNull import com.bitwarden.core.di.CoreModule import com.bitwarden.data.datasource.disk.base.FakeSharedPreferences -import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import com.x8bit.bitwarden.data.platform.manager.model.AppResumeScreenData import com.x8bit.bitwarden.data.platform.repository.model.ClearClipboardFrequency import com.x8bit.bitwarden.data.platform.repository.model.UriMatchType import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction -import com.x8bit.bitwarden.data.util.assertJsonEquals import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage +import io.mockk.mockk import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import org.junit.jupiter.api.Assertions.assertEquals @@ -27,9 +26,10 @@ class SettingsDiskSourceTest { private val fakeSharedPreferences = FakeSharedPreferences() private val json = CoreModule.providesJson() - private val settingsDiskSource = SettingsDiskSourceImpl( + private val settingsDiskSource: SettingsDiskSource = SettingsDiskSourceImpl( sharedPreferences = fakeSharedPreferences, json = json, + flightRecorderDiskSource = mockk(), ) @Test @@ -85,104 +85,6 @@ class SettingsDiskSourceTest { ) } - @Test - fun `flightRecorderData should pull from SharedPreferences`() { - val flightRecorderKey = "bwPreferencesStorage:flightRecorderData" - val encodedData = """ - { - "data": [ - { - "id": "51" - "fileName": "flight_recorder_2025-04-03_14-22-40", - "startTime": 1744059882, - "duration": 3600, - "isActive": false - } - ] - } - """ - .trimIndent() - val expected = FlightRecorderDataSet( - data = setOf( - FlightRecorderDataSet.FlightRecorderData( - id = "51", - fileName = "flight_recorder_2025-04-03_14-22-40", - startTimeMs = 1_744_059_882L, - durationMs = 3_600L, - isActive = false, - ), - ), - ) - - // Verify initial value is null and disk source matches shared preferences. - assertNull(fakeSharedPreferences.getString(flightRecorderKey, null)) - assertNull(settingsDiskSource.flightRecorderData) - - // Updating the shared preferences should update disk source. - fakeSharedPreferences.edit { putString(flightRecorderKey, encodedData) } - val actual = settingsDiskSource.flightRecorderData - assertEquals(expected, actual) - } - - @Test - fun `flightRecorderDataFlow should react to changes in isFLightRecorderEnabled`() = runTest { - val expected = FlightRecorderDataSet( - data = setOf( - FlightRecorderDataSet.FlightRecorderData( - id = "52", - fileName = "flight_recorder_2025-04-03_14-22-40", - startTimeMs = 1_744_059_882L, - durationMs = 3_600L, - isActive = true, - ), - ), - ) - settingsDiskSource.flightRecorderDataFlow.test { - // The initial values of the Flow and the property are in sync - assertNull(settingsDiskSource.flightRecorderData) - assertNull(awaitItem()) - - settingsDiskSource.flightRecorderData = expected - assertEquals(expected, awaitItem()) - - settingsDiskSource.flightRecorderData = null - assertNull(awaitItem()) - } - } - - @Test - fun `setting flightRecorderData should update SharedPreferences`() { - val flightRecorderKey = "bwPreferencesStorage:flightRecorderData" - val data = FlightRecorderDataSet( - data = setOf( - FlightRecorderDataSet.FlightRecorderData( - id = "53", - fileName = "flight_recorder_2025-04-03_14-22-40", - startTimeMs = 1_744_059_882L, - durationMs = 3_600L, - isActive = true, - ), - ), - ) - val expected = """ - { - "data": [ - { - "id": "53", - "fileName": "flight_recorder_2025-04-03_14-22-40", - "startTime": 1744059882, - "duration": 3600, - "isActive": true - } - ] - } - """ - .trimIndent() - settingsDiskSource.flightRecorderData = data - val actual = fakeSharedPreferences.getString(flightRecorderKey, null) - assertJsonEquals(expected, actual!!) - } - @Test fun `systemBiometricIntegritySource should pull from SharedPreferences`() { val biometricIntegritySource = "bwPreferencesStorage:biometricIntegritySource" diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt index e730a53f4cc..80255175f53 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt @@ -2,7 +2,9 @@ package com.x8bit.bitwarden.data.platform.datasource.disk.util import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.core.data.util.decodeFromStringOrNull +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet +import com.bitwarden.data.datasource.disk.util.FakeFlightRecorderDiskSource import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource import com.x8bit.bitwarden.data.platform.manager.model.AppResumeScreenData @@ -19,7 +21,11 @@ import java.time.Instant /** * Fake, memory-based implementation of [SettingsDiskSource]. */ -class FakeSettingsDiskSource : SettingsDiskSource { +class FakeSettingsDiskSource( + private val flightRecorderDiskSource: FakeFlightRecorderDiskSource = + FakeFlightRecorderDiskSource(), +) : SettingsDiskSource, + FlightRecorderDiskSource by flightRecorderDiskSource { private val mutableAppLanguageFlow = bufferedMutableSharedFlow(replay = 1) @@ -52,9 +58,6 @@ class FakeSettingsDiskSource : SettingsDiskSource { private val mutableShouldShowGeneratorCoachMarkFlow = bufferedMutableSharedFlow() - private val mutableFlightRecorderDataFlow = - bufferedMutableSharedFlow(replay = 1) - private var storedAppLanguage: AppLanguage? = null private var storedAppTheme: AppTheme = AppTheme.DEFAULT private val storedLastSyncTime = mutableMapOf() @@ -86,7 +89,6 @@ class FakeSettingsDiskSource : SettingsDiskSource { private var createSendActionCount: Int? = null private var hasSeenAddLoginCoachMark: Boolean? = null private var hasSeenGeneratorCoachMark: Boolean? = null - private var storedFlightRecorderData: FlightRecorderDataSet? = null private var storedIsDynamicColorsEnabled: Boolean? = null private var storedBrowserAutofillDialogReshowTime: Instant? = null @@ -200,17 +202,6 @@ class FakeSettingsDiskSource : SettingsDiskSource { emit(hasUserLoggedInOrCreatedAccount) } - override var flightRecorderData: FlightRecorderDataSet? - get() = storedFlightRecorderData - set(value) { - storedFlightRecorderData = value - mutableFlightRecorderDataFlow.tryEmit(value) - } - - override val flightRecorderDataFlow: Flow - get() = mutableFlightRecorderDataFlow - .onSubscription { emit(storedFlightRecorderData) } - override var browserAutofillDialogReshowTime: Instant? get() = storedBrowserAutofillDialogReshowTime set(value) { @@ -490,7 +481,7 @@ class FakeSettingsDiskSource : SettingsDiskSource { * Asserts that the stored [FlightRecorderDataSet] matches the [expected] one. */ fun assertFlightRecorderData(expected: FlightRecorderDataSet) { - assertEquals(expected, storedFlightRecorderData) + flightRecorderDiskSource.assertFlightRecorderData(expected) } /** diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/event/OrganizationEventManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/event/OrganizationEventManagerTest.kt index fc148607830..df73f394c1b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/event/OrganizationEventManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/event/OrganizationEventManagerTest.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.manager.event import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.core.data.util.asSuccess import com.bitwarden.network.model.OrganizationEventJson import com.bitwarden.network.model.OrganizationEventType @@ -13,7 +14,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthState import com.x8bit.bitwarden.data.platform.datasource.disk.EventDiskSource import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent import com.x8bit.bitwarden.data.util.FakeLifecycleOwner -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.data.vault.repository.VaultRepository import io.mockk.coEvery diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/garbage/GarbageCollectionManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/garbage/GarbageCollectionManagerTest.kt index a48e4118e96..fd381d7fe60 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/garbage/GarbageCollectionManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/garbage/GarbageCollectionManagerTest.kt @@ -1,7 +1,7 @@ package com.x8bit.bitwarden.data.platform.manager.garbage import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import kotlinx.coroutines.test.StandardTestDispatcher import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkConfigManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkConfigManagerTest.kt index a850cf4ca47..bfa93737bdf 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkConfigManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkConfigManagerTest.kt @@ -2,13 +2,13 @@ package com.x8bit.bitwarden.data.platform.manager.network import com.bitwarden.core.data.manager.dispatcher.DispatcherManager import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.data.repository.ServerConfigRepository import com.bitwarden.data.repository.model.Environment import com.bitwarden.network.BitwardenServiceClient import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.AuthState import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt index 4c1bfce45a8..4a791157cbc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.datasource.disk import app.cash.turbine.test import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager +import com.bitwarden.core.data.util.assertJsonEquals import com.bitwarden.core.di.CoreModule import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.createMockCipher @@ -9,7 +10,6 @@ import com.bitwarden.network.model.createMockCollection import com.bitwarden.network.model.createMockDomains import com.bitwarden.network.model.createMockFolder import com.bitwarden.network.model.createMockSend -import com.x8bit.bitwarden.data.util.assertJsonEquals import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FakeCiphersDao import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FakeCollectionsDao import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FakeDomainsDao diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt index 55975e89b06..0546a3be9f2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.requestFocus import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.ui.platform.components.account.model.AccountSummary import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.asText @@ -37,7 +38,6 @@ import com.bitwarden.ui.util.performLogoutAccountClick import com.bitwarden.ui.util.performRemoveAccountClick import com.bitwarden.ui.util.performYesDialogButtonClick import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManager import com.x8bit.bitwarden.ui.credentials.manager.model.AssertFido2CredentialResult import com.x8bit.bitwarden.ui.credentials.manager.model.GetCredentialsResult diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt index 275fb0c1491..b0b258edfa5 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt @@ -2,12 +2,12 @@ package com.x8bit.bitwarden.ui.platform.feature.debugmenu import app.cash.turbine.test import com.bitwarden.core.data.manager.model.FlagKey +import com.bitwarden.core.data.util.assertCoroutineThrows import com.bitwarden.ui.platform.base.BaseViewModelTest import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.LogsManager import com.x8bit.bitwarden.data.platform.repository.DebugMenuRepository -import com.x8bit.bitwarden.data.util.assertCoroutineThrows import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt index c4efea3a14d..54a1360d9d0 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsScreenTest.kt @@ -13,11 +13,11 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performSemanticsAction import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.core.util.isBuildVersionAtLeast import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData import com.bitwarden.ui.util.asText import com.bitwarden.ui.util.assertNoDialogExists -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager import io.mockk.every diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt index eabfb9f8ef7..56c844395c6 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.performTouchInput import androidx.core.net.toUri import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData import com.bitwarden.ui.platform.manager.IntentManager import com.bitwarden.ui.platform.manager.exit.ExitManager @@ -54,7 +55,6 @@ import com.bitwarden.ui.util.onAllNodesWithTextAfterScroll import com.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll import com.bitwarden.ui.util.onNodeWithTextAfterScroll import com.bitwarden.vault.UriMatchType -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManager import com.x8bit.bitwarden.ui.credentials.manager.model.CreateCredentialResult diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importitems/ImportItemsViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importitems/ImportItemsViewModelTest.kt index cd9e4988dc0..79aee50fc53 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importitems/ImportItemsViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importitems/ImportItemsViewModelTest.kt @@ -5,12 +5,12 @@ import androidx.credentials.providerevents.transfer.CredentialTypes import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test import com.bitwarden.cxf.importer.model.ImportCredentialsSelectionResult +import com.bitwarden.network.model.PolicyTypeJson +import com.bitwarden.network.model.createMockPolicy import com.bitwarden.ui.platform.base.BaseViewModelTest import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData import com.bitwarden.ui.platform.resource.BitwardenPlurals import com.bitwarden.ui.platform.resource.BitwardenString -import com.bitwarden.network.model.PolicyTypeJson -import com.bitwarden.network.model.createMockPolicy import com.bitwarden.ui.util.asPluralsText import com.bitwarden.ui.util.asText import com.x8bit.bitwarden.data.platform.manager.PolicyManager @@ -18,9 +18,9 @@ import com.x8bit.bitwarden.data.vault.manager.model.SyncVaultDataResult import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult import io.mockk.awaits -import io.mockk.every import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.just import io.mockk.mockk import kotlinx.coroutines.test.runTest diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt index a7cfc40657e..6dc7d9de62f 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreenTest.kt @@ -15,12 +15,12 @@ import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performSemanticsAction import androidx.core.net.toUri import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.ui.platform.manager.IntentManager import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.util.asText import com.bitwarden.ui.util.assertNoDialogExists import com.bitwarden.ui.util.isBottomSheet -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import io.mockk.every import io.mockk.just diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index cd6fabe3a50..cbc8188b536 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.test.performScrollToNode import androidx.compose.ui.test.performTextInput import androidx.core.net.toUri import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.advanceTimeByAndRunCurrent import com.bitwarden.data.repository.model.Environment import com.bitwarden.data.repository.util.baseIconUrl import com.bitwarden.ui.platform.components.account.model.AccountSummary @@ -46,7 +47,6 @@ import com.bitwarden.ui.util.performLogoutAccountClick import com.bitwarden.ui.util.performRemoveAccountClick import com.bitwarden.ui.util.performYesDialogButtonClick import com.bitwarden.vault.CipherType -import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest import com.x8bit.bitwarden.ui.platform.manager.review.AppReviewManager import com.x8bit.bitwarden.ui.vault.components.model.CreateVaultItemType diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSource.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSource.kt index 5c6c8faaec7..50ce0f95e5d 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSource.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSource.kt @@ -2,13 +2,14 @@ package com.bitwarden.authenticator.data.platform.datasource.disk import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage import com.bitwarden.authenticator.ui.platform.feature.settings.data.model.DefaultSaveOption +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import kotlinx.coroutines.flow.Flow /** * Primary access point for general settings-related disk information. */ -interface SettingsDiskSource { +interface SettingsDiskSource : FlightRecorderDiskSource { /** * The currently persisted app language (or `null` if not set). diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSourceImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSourceImpl.kt index 1c41016b90d..7635c97b09a 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSourceImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingsDiskSourceImpl.kt @@ -5,6 +5,7 @@ import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model import com.bitwarden.authenticator.ui.platform.feature.settings.data.model.DefaultSaveOption import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.data.datasource.disk.BaseDiskSource +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onSubscription @@ -32,8 +33,10 @@ private const val DEFAULT_ALERT_THRESHOLD_SECONDS = 7 */ class SettingsDiskSourceImpl( sharedPreferences: SharedPreferences, + flightRecorderDiskSource: FlightRecorderDiskSource, ) : BaseDiskSource(sharedPreferences = sharedPreferences), - SettingsDiskSource { + SettingsDiskSource, + FlightRecorderDiskSource by flightRecorderDiskSource { private val mutableAppThemeFlow = bufferedMutableSharedFlow(replay = 1) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/di/PlatformDiskModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/di/PlatformDiskModule.kt index fab3efe20bd..ac5453c491a 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/di/PlatformDiskModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/di/PlatformDiskModule.kt @@ -5,6 +5,7 @@ import com.bitwarden.authenticator.data.platform.datasource.disk.FeatureFlagOver import com.bitwarden.authenticator.data.platform.datasource.disk.FeatureFlagOverrideDiskSourceImpl import com.bitwarden.authenticator.data.platform.datasource.disk.SettingsDiskSource import com.bitwarden.authenticator.data.platform.datasource.disk.SettingsDiskSourceImpl +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences import dagger.Module import dagger.Provides @@ -23,8 +24,12 @@ object PlatformDiskModule { @Singleton fun provideSettingsDiskSource( @UnencryptedPreferences sharedPreferences: SharedPreferences, + flightRecorderDiskSource: FlightRecorderDiskSource, ): SettingsDiskSource = - SettingsDiskSourceImpl(sharedPreferences = sharedPreferences) + SettingsDiskSourceImpl( + sharedPreferences = sharedPreferences, + flightRecorderDiskSource = flightRecorderDiskSource, + ) @Provides @Singleton diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt index b1ea2c4d127..8364ed94fbc 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt @@ -12,6 +12,7 @@ import com.bitwarden.network.interceptor.BaseUrlsProvider import com.bitwarden.network.model.AuthTokenData import com.bitwarden.network.model.BitwardenServiceClientConfig import com.bitwarden.network.service.ConfigService +import com.bitwarden.network.service.DownloadService import com.bitwarden.network.ssl.CertificateProvider import dagger.Module import dagger.Provides @@ -71,4 +72,10 @@ object PlatformNetworkModule { }, ), ) + + @Provides + @Singleton + fun provideDownloadService( + bitwardenServiceClient: BitwardenServiceClient, + ): DownloadService = bitwardenServiceClient.downloadService } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepository.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepository.kt index 0b8cb26b61e..0b13f955a48 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepository.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepository.kt @@ -2,6 +2,7 @@ package com.bitwarden.authenticator.data.platform.repository import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage import com.bitwarden.authenticator.ui.platform.feature.settings.data.model.DefaultSaveOption +import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -9,7 +10,7 @@ import kotlinx.coroutines.flow.StateFlow /** * Provides an API for observing and modifying settings state. */ -interface SettingsRepository { +interface SettingsRepository : FlightRecorderManager { /** * The [AppLanguage] for the current user. diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryImpl.kt index 65594f8a0bb..16c2c6539b5 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.bitwarden.authenticator.data.platform.datasource.disk.SettingsDiskSou import com.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage import com.bitwarden.authenticator.ui.platform.feature.settings.data.model.DefaultSaveOption import com.bitwarden.core.data.manager.dispatcher.DispatcherManager +import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -20,8 +21,10 @@ private val DEFAULT_IS_SCREEN_CAPTURE_ALLOWED = BuildConfig.DEBUG */ class SettingsRepositoryImpl( private val settingsDiskSource: SettingsDiskSource, + flightRecorderManager: FlightRecorderManager, dispatcherManager: DispatcherManager, -) : SettingsRepository { +) : SettingsRepository, + FlightRecorderManager by flightRecorderManager { private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/di/PlatformRepositoryModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/di/PlatformRepositoryModule.kt index 31004a605bf..79800f62ee7 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/di/PlatformRepositoryModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/repository/di/PlatformRepositoryModule.kt @@ -7,6 +7,7 @@ import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepositoryI import com.bitwarden.authenticator.data.platform.repository.SettingsRepository import com.bitwarden.authenticator.data.platform.repository.SettingsRepositoryImpl import com.bitwarden.core.data.manager.dispatcher.DispatcherManager +import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager import com.bitwarden.data.repository.ServerConfigRepository import dagger.Module import dagger.Provides @@ -25,10 +26,12 @@ object PlatformRepositoryModule { @Singleton fun provideSettingsRepository( settingsDiskSource: SettingsDiskSource, + flightRecorderManager: FlightRecorderManager, dispatcherManager: DispatcherManager, ): SettingsRepository = SettingsRepositoryImpl( settingsDiskSource = settingsDiskSource, + flightRecorderManager = flightRecorderManager, dispatcherManager = dispatcherManager, ) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/manager/di/PlatformUiManagerModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/manager/di/PlatformUiManagerModule.kt index 508b8a6b24b..3d5887cc2b3 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/manager/di/PlatformUiManagerModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/manager/di/PlatformUiManagerModule.kt @@ -1,6 +1,8 @@ package com.bitwarden.authenticator.ui.platform.manager.di +import com.bitwarden.authenticator.ui.platform.manager.AuthenticatorBuildInfoManagerImpl import com.bitwarden.authenticator.ui.platform.model.SnackbarRelay +import com.bitwarden.core.data.manager.BuildInfoManager import com.bitwarden.core.data.manager.dispatcher.DispatcherManager import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManagerImpl @@ -16,6 +18,10 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) class PlatformUiManagerModule { + @Provides + @Singleton + fun provideBuildInfoManager(): BuildInfoManager = AuthenticatorBuildInfoManagerImpl() + @Provides @Singleton fun provideSnackbarRelayManager( diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingDiskSourceTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingDiskSourceTest.kt index d0816078ccb..dc5c5d78f0d 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingDiskSourceTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/datasource/disk/SettingDiskSourceTest.kt @@ -4,6 +4,7 @@ import androidx.core.content.edit import app.cash.turbine.test import com.bitwarden.authenticator.ui.platform.feature.settings.data.model.DefaultSaveOption import com.bitwarden.data.datasource.disk.base.FakeSharedPreferences +import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse @@ -15,8 +16,9 @@ class SettingDiskSourceTest { private val sharedPreferences: FakeSharedPreferences = FakeSharedPreferences() - private val settingDiskSource = SettingsDiskSourceImpl( - sharedPreferences, + private val settingDiskSource: SettingsDiskSource = SettingsDiskSourceImpl( + sharedPreferences = sharedPreferences, + flightRecorderDiskSource = mockk(), ) @Test diff --git a/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryTest.kt b/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryTest.kt index 59dd3c75a17..cae6dcd624f 100644 --- a/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryTest.kt +++ b/authenticator/src/test/kotlin/com/bitwarden/authenticator/data/platform/repository/SettingsRepositoryTest.kt @@ -23,8 +23,9 @@ class SettingsRepositoryTest { every { getAlertThresholdSeconds() } returns 7 } - private val settingsRepository = SettingsRepositoryImpl( + private val settingsRepository: SettingsRepository = SettingsRepositoryImpl( settingsDiskSource = settingsDiskSource, + flightRecorderManager = mockk(), dispatcherManager = FakeDispatcherManager(), ) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index db396abde10..5d5fdd32ff4 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -56,7 +56,11 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockk.mockk) + testFixturesImplementation(libs.kotlinx.serialization) + testFixturesImplementation(platform(libs.junit.bom)) + testFixturesImplementation(libs.junit.jupiter) testFixturesImplementation(libs.kotlinx.coroutines.test) + testFixturesImplementation(libs.mockk.mockk) } kotlin { diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/util/TestHelpers.kt b/core/src/testFixtures/kotlin/com/bitwarden/core/data/util/TestHelpers.kt similarity index 98% rename from app/src/test/kotlin/com/x8bit/bitwarden/data/util/TestHelpers.kt rename to core/src/testFixtures/kotlin/com/bitwarden/core/data/util/TestHelpers.kt index a69773edb9c..95b67a61130 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/util/TestHelpers.kt +++ b/core/src/testFixtures/kotlin/com/bitwarden/core/data/util/TestHelpers.kt @@ -1,4 +1,4 @@ -package com.x8bit.bitwarden.data.util +package com.bitwarden.core.data.util import com.bitwarden.core.di.CoreModule import io.mockk.MockKMatcherScope diff --git a/data/src/main/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceImpl.kt b/data/src/main/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceImpl.kt new file mode 100644 index 00000000000..03d49e3cedf --- /dev/null +++ b/data/src/main/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceImpl.kt @@ -0,0 +1,33 @@ +package com.bitwarden.data.datasource.disk + +import android.content.SharedPreferences +import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow +import com.bitwarden.core.data.util.decodeFromStringOrNull +import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onSubscription +import kotlinx.serialization.json.Json + +private const val FLIGHT_RECORDER_KEY = "flightRecorderData" + +/** + * Primary implementation of [FlightRecorderDiskSource]. + */ +internal class FlightRecorderDiskSourceImpl( + private val json: Json, + sharedPreferences: SharedPreferences, +) : BaseDiskSource(sharedPreferences = sharedPreferences), + FlightRecorderDiskSource { + private val mutableFlightRecorderDataFlow = bufferedMutableSharedFlow() + + override var flightRecorderData: FlightRecorderDataSet? + get() = getString(key = FLIGHT_RECORDER_KEY) + ?.let { json.decodeFromStringOrNull(it) } + set(value) { + putString(key = FLIGHT_RECORDER_KEY, value = value?.let { json.encodeToString(it) }) + mutableFlightRecorderDataFlow.tryEmit(value) + } + + override val flightRecorderDataFlow: Flow + get() = mutableFlightRecorderDataFlow.onSubscription { emit(flightRecorderData) } +} diff --git a/data/src/main/kotlin/com/bitwarden/data/datasource/disk/di/DiskModule.kt b/data/src/main/kotlin/com/bitwarden/data/datasource/disk/di/DiskModule.kt index 9af18332b71..e562abec84a 100644 --- a/data/src/main/kotlin/com/bitwarden/data/datasource/disk/di/DiskModule.kt +++ b/data/src/main/kotlin/com/bitwarden/data/datasource/disk/di/DiskModule.kt @@ -1,8 +1,10 @@ package com.bitwarden.data.datasource.disk.di import android.content.SharedPreferences +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.datasource.disk.ConfigDiskSource import com.bitwarden.data.datasource.disk.ConfigDiskSourceImpl +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSourceImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -26,4 +28,15 @@ object DiskModule { sharedPreferences = sharedPreferences, json = json, ) + + @Provides + @Singleton + fun provideFlightRecorderDiskSource( + @UnencryptedPreferences sharedPreferences: SharedPreferences, + json: Json, + ): FlightRecorderDiskSource = + FlightRecorderDiskSourceImpl( + sharedPreferences = sharedPreferences, + json = json, + ) } diff --git a/data/src/main/kotlin/com/bitwarden/data/manager/di/DataManagerModule.kt b/data/src/main/kotlin/com/bitwarden/data/manager/di/DataManagerModule.kt index 02ab624573c..b70228247d7 100644 --- a/data/src/main/kotlin/com/bitwarden/data/manager/di/DataManagerModule.kt +++ b/data/src/main/kotlin/com/bitwarden/data/manager/di/DataManagerModule.kt @@ -3,12 +3,15 @@ package com.bitwarden.data.manager.di import android.content.Context import com.bitwarden.core.data.manager.BuildInfoManager import com.bitwarden.core.data.manager.dispatcher.DispatcherManager +import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.manager.BitwardenPackageManager import com.bitwarden.data.manager.BitwardenPackageManagerImpl import com.bitwarden.data.manager.NativeLibraryManager import com.bitwarden.data.manager.NativeLibraryManagerImpl import com.bitwarden.data.manager.file.FileManager import com.bitwarden.data.manager.file.FileManagerImpl +import com.bitwarden.data.manager.flightrecorder.FlightRecorderManager +import com.bitwarden.data.manager.flightrecorder.FlightRecorderManagerImpl import com.bitwarden.data.manager.flightrecorder.FlightRecorderWriter import com.bitwarden.data.manager.flightrecorder.FlightRecorderWriterImpl import com.bitwarden.network.service.DownloadService @@ -45,6 +48,22 @@ object DataManagerModule { dispatcherManager = dispatcherManager, ) + @Provides + @Singleton + fun provideFlightRecorderManager( + @ApplicationContext context: Context, + clock: Clock, + dispatcherManager: DispatcherManager, + flightRecorderDiskSource: FlightRecorderDiskSource, + flightRecorderWriter: FlightRecorderWriter, + ): FlightRecorderManager = FlightRecorderManagerImpl( + context = context, + clock = clock, + dispatcherManager = dispatcherManager, + flightRecorderDiskSource = flightRecorderDiskSource, + flightRecorderWriter = flightRecorderWriter, + ) + @Provides @Singleton fun provideFlightRecorderWriter( diff --git a/data/src/main/kotlin/com/bitwarden/data/manager/flightrecorder/FlightRecorderManager.kt b/data/src/main/kotlin/com/bitwarden/data/manager/flightrecorder/FlightRecorderManager.kt index 40131f468c2..dfc588131f6 100644 --- a/data/src/main/kotlin/com/bitwarden/data/manager/flightrecorder/FlightRecorderManager.kt +++ b/data/src/main/kotlin/com/bitwarden/data/manager/flightrecorder/FlightRecorderManager.kt @@ -1,12 +1,8 @@ package com.bitwarden.data.manager.flightrecorder -import android.content.Context -import com.bitwarden.core.data.manager.dispatcher.DispatcherManager -import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet import com.bitwarden.data.manager.model.FlightRecorderDuration import kotlinx.coroutines.flow.StateFlow -import java.time.Clock /** * Manager class that handles recording logs for the flight recorder. @@ -46,24 +42,4 @@ interface FlightRecorderManager { * Deletes the raw log files and metadata. */ fun deleteAllLogs() - - @Suppress("UndocumentedPublicClass") - companion object { - /** - * Creates a new instance of the [FlightRecorderManager]. - */ - fun create( - context: Context, - clock: Clock, - flightRecorderDiskSource: FlightRecorderDiskSource, - flightRecorderWriter: FlightRecorderWriter, - dispatcherManager: DispatcherManager, - ): FlightRecorderManager = FlightRecorderManagerImpl( - context = context, - clock = clock, - flightRecorderDiskSource = flightRecorderDiskSource, - flightRecorderWriter = flightRecorderWriter, - dispatcherManager = dispatcherManager, - ) - } } diff --git a/data/src/test/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceTest.kt b/data/src/test/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceTest.kt new file mode 100644 index 00000000000..6c76ec1ff58 --- /dev/null +++ b/data/src/test/kotlin/com/bitwarden/data/datasource/disk/FlightRecorderDiskSourceTest.kt @@ -0,0 +1,120 @@ +package com.bitwarden.data.datasource.disk + +import androidx.core.content.edit +import app.cash.turbine.test +import com.bitwarden.core.data.util.assertJsonEquals +import com.bitwarden.core.di.CoreModule +import com.bitwarden.data.datasource.disk.base.FakeSharedPreferences +import com.bitwarden.data.datasource.disk.model.FlightRecorderDataSet +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class FlightRecorderDiskSourceTest { + private val fakeSharedPreferences = FakeSharedPreferences() + private val json = CoreModule.providesJson() + + private val flightRecorderDiskSource = FlightRecorderDiskSourceImpl( + sharedPreferences = fakeSharedPreferences, + json = json, + ) + + @Test + fun `flightRecorderData should pull from SharedPreferences`() { + val flightRecorderKey = "bwPreferencesStorage:flightRecorderData" + val encodedData = """ + { + "data": [ + { + "id": "51", + "fileName": "flight_recorder_2025-04-03_14-22-40", + "startTime": 1744059882, + "duration": 3600, + "isActive": false + } + ] + } + """ + .trimIndent() + val expected = FlightRecorderDataSet( + data = setOf( + FlightRecorderDataSet.FlightRecorderData( + id = "51", + fileName = "flight_recorder_2025-04-03_14-22-40", + startTimeMs = 1_744_059_882L, + durationMs = 3_600L, + isActive = false, + ), + ), + ) + + // Verify initial value is null and disk source matches shared preferences. + assertNull(fakeSharedPreferences.getString(flightRecorderKey, null)) + assertNull(flightRecorderDiskSource.flightRecorderData) + + // Updating the shared preferences should update disk source. + fakeSharedPreferences.edit { putString(flightRecorderKey, encodedData) } + val actual = flightRecorderDiskSource.flightRecorderData + assertEquals(expected, actual) + } + + @Test + fun `flightRecorderDataFlow should react to changes in isFLightRecorderEnabled`() = runTest { + val expected = FlightRecorderDataSet( + data = setOf( + FlightRecorderDataSet.FlightRecorderData( + id = "52", + fileName = "flight_recorder_2025-04-03_14-22-40", + startTimeMs = 1_744_059_882L, + durationMs = 3_600L, + isActive = true, + ), + ), + ) + flightRecorderDiskSource.flightRecorderDataFlow.test { + // The initial values of the Flow and the property are in sync + assertNull(flightRecorderDiskSource.flightRecorderData) + assertNull(awaitItem()) + + flightRecorderDiskSource.flightRecorderData = expected + assertEquals(expected, awaitItem()) + + flightRecorderDiskSource.flightRecorderData = null + assertNull(awaitItem()) + } + } + + @Test + fun `setting flightRecorderData should update SharedPreferences`() { + val flightRecorderKey = "bwPreferencesStorage:flightRecorderData" + val data = FlightRecorderDataSet( + data = setOf( + FlightRecorderDataSet.FlightRecorderData( + id = "53", + fileName = "flight_recorder_2025-04-03_14-22-40", + startTimeMs = 1_744_059_882L, + durationMs = 3_600L, + isActive = true, + ), + ), + ) + val expected = """ + { + "data": [ + { + "id": "53", + "fileName": "flight_recorder_2025-04-03_14-22-40", + "startTime": 1744059882, + "duration": 3600, + "isActive": true + } + ] + } + """ + .trimIndent() + flightRecorderDiskSource.flightRecorderData = data + val actual = fakeSharedPreferences.getString(flightRecorderKey, null) + assertJsonEquals(expected, actual!!) + } +}