Merge branch 'develop' into feature/bma/fixFdroidNotification

This commit is contained in:
Benoit Marty 2024-06-18 10:28:04 +02:00 committed by GitHub
commit 69dbb08034
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
172 changed files with 1110 additions and 68 deletions

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
}
android {
namespace = "io.element.android.libraries.fullscreenintent.api"
}
dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.preferences.api)
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.fullscreenintent.api
import io.element.android.libraries.architecture.Presenter
interface FullScreenIntentPermissionsPresenter : Presenter<FullScreenIntentPermissionsState>

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.fullscreenintent.api
data class FullScreenIntentPermissionsState(
val permissionGranted: Boolean,
val shouldDisplayBanner: Boolean,
val dismissFullScreenIntentBanner: () -> Unit,
val openFullScreenIntentSettings: () -> Unit,
)

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-compose-library")
alias(libs.plugins.anvil)
}
android {
namespace = "io.element.android.libraries.fullscreenintent.impl"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
api(projects.libraries.fullscreenintent.api)
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.permissions.noop)
implementation(projects.libraries.preferences.api)
implementation(projects.services.toolbox.api)
implementation(libs.androidx.datastore.preferences)
testImplementation(projects.libraries.fullscreenintent.test)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.tests.testutils)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.testtags)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.mockk)
testImplementation(libs.androidx.compose.ui.test.junit)
testImplementation(projects.services.toolbox.test)
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.fullscreenintent.impl
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.core.app.NotificationManagerCompat
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsPresenter
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
import io.element.android.services.toolbox.api.intent.ExternalIntentLauncher
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class DefaultFullScreenIntentPermissionsPresenter @Inject constructor(
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
private val externalIntentLauncher: ExternalIntentLauncher,
private val buildMeta: BuildMeta,
private val notificationManagerCompat: NotificationManagerCompat,
preferencesDataStoreFactory: PreferenceDataStoreFactory,
) : FullScreenIntentPermissionsPresenter {
companion object {
private const val PREF_KEY_FULL_SCREEN_INTENT_BANNER_DISMISSED = "PREF_KEY_FULL_SCREEN_INTENT_BANNER_DISMISSED"
}
private val dataStore = preferencesDataStoreFactory.create("full_screen_intent_permissions")
private val isFullScreenIntentBannerDismissed = dataStore.data.map { prefs ->
prefs[booleanPreferencesKey(PREF_KEY_FULL_SCREEN_INTENT_BANNER_DISMISSED)] ?: false
}
private suspend fun dismissFullScreenIntentBanner() {
dataStore.edit { prefs ->
prefs[booleanPreferencesKey(PREF_KEY_FULL_SCREEN_INTENT_BANNER_DISMISSED)] = true
}
}
@Composable
override fun present(): FullScreenIntentPermissionsState {
val coroutineScope = rememberCoroutineScope()
val isGranted = notificationManagerCompat.canUseFullScreenIntent()
val isBannerDismissed by isFullScreenIntentBannerDismissed.collectAsState(initial = true)
return FullScreenIntentPermissionsState(
permissionGranted = isGranted,
shouldDisplayBanner = !isBannerDismissed && !isGranted,
dismissFullScreenIntentBanner = {
coroutineScope.launch {
dismissFullScreenIntentBanner()
}
},
openFullScreenIntentSettings = ::openFullScreenIntentSettings,
)
}
private fun openFullScreenIntentSettings() {
if (buildVersionSdkIntProvider.isAtLeast(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) {
try {
val intent = Intent(
Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT,
Uri.parse("package:${buildMeta.applicationId}")
)
externalIntentLauncher.launch(intent)
} catch (e: ActivityNotFoundException) {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, buildMeta.applicationId)
externalIntentLauncher.launch(intent)
}
}
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.fullscreenintent.test
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationManagerCompat
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.fullscreenintent.impl.DefaultFullScreenIntentPermissionsPresenter
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory
import io.element.android.services.toolbox.api.intent.ExternalIntentLauncher
import io.element.android.services.toolbox.test.intent.FakeExternalIntentLauncher
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
class DefaultFullScreenIntentPermissionsPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@Test
fun `shouldDisplay - is true when permission is not granted and banner is not dismissed`() = runTest {
val presenter = createPresenter(
notificationManagerCompat = mockk {
every { canUseFullScreenIntent() } returns false
}
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialItem = awaitItem()
assertThat(initialItem.shouldDisplayBanner).isTrue()
}
}
@Test
fun `shouldDisplay - is false if permission is granted`() = runTest {
val presenter = createPresenter(
notificationManagerCompat = mockk {
every { canUseFullScreenIntent() } returns true
}
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialItem = awaitItem()
assertThat(initialItem.shouldDisplayBanner).isFalse()
}
}
@Test
fun `dismissFullScreenIntentBanner - makes shouldDisplay false`() = runTest {
val presenter = createPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val loadedItem = awaitItem()
loadedItem.dismissFullScreenIntentBanner()
runCurrent()
assertThat(awaitItem().shouldDisplayBanner).isFalse()
}
}
@Test
fun `openFullScreenIntentSettings - opens external screen using intent`() = runTest {
val launchLambda = lambdaRecorder<Intent, Unit> { _ -> }
val externalIntentLauncher = FakeExternalIntentLauncher(launchLambda)
val presenter = createPresenter(externalIntentLauncher = externalIntentLauncher)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val loadedItem = awaitItem()
loadedItem.openFullScreenIntentSettings()
launchLambda.assertions().isCalledOnce()
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `openFullScreenIntentSettings - does nothing in old APIs`() = runTest {
val launchLambda = lambdaRecorder<Intent, Unit> { _ -> }
val externalIntentLauncher = FakeExternalIntentLauncher(launchLambda)
val presenter = createPresenter(
buildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(Build.VERSION_CODES.Q),
externalIntentLauncher = externalIntentLauncher,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val loadedItem = awaitItem()
loadedItem.openFullScreenIntentSettings()
launchLambda.assertions().isNeverCalled()
cancelAndIgnoreRemainingEvents()
}
}
private fun createPresenter(
buildVersionSdkIntProvider: FakeBuildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(Build.VERSION_CODES.UPSIDE_DOWN_CAKE),
dataStoreFactory: FakePreferenceDataStoreFactory = FakePreferenceDataStoreFactory(),
externalIntentLauncher: ExternalIntentLauncher = FakeExternalIntentLauncher(),
buildMeta: BuildMeta = aBuildMeta(),
notificationManagerCompat: NotificationManagerCompat = mockk(relaxed = true)
) = DefaultFullScreenIntentPermissionsPresenter(
buildVersionSdkIntProvider = buildVersionSdkIntProvider,
externalIntentLauncher = externalIntentLauncher,
buildMeta = buildMeta,
preferencesDataStoreFactory = dataStoreFactory,
notificationManagerCompat = notificationManagerCompat,
)
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.libraries.fullscreenintent.test"
}
dependencies {
api(projects.libraries.fullscreenintent.api)
implementation(projects.libraries.architecture)
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.fullscreenintent.test
import androidx.compose.runtime.Composable
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsPresenter
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
class FakeFullScreenIntentPermissionsPresenter : FullScreenIntentPermissionsPresenter {
var state = FullScreenIntentPermissionsState(
permissionGranted = true,
shouldDisplayBanner = false,
dismissFullScreenIntentBanner = {},
openFullScreenIntentSettings = {},
)
@Composable
override fun present(): FullScreenIntentPermissionsState {
return state
}
}

View file

@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.test.roomdirectory.FakeRoomDirectoryS
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.collections.immutable.ImmutableList
@ -80,6 +81,7 @@ class FakeMatrixClient(
private val accountManagementUrlString: Result<String?> = Result.success(null),
private val resolveRoomAliasResult: (RoomAlias) -> Result<ResolvedRoomAlias> = { Result.success(ResolvedRoomAlias(A_ROOM_ID, emptyList())) },
private val getRoomPreviewFromRoomIdResult: (RoomId, List<String>) -> Result<RoomPreview> = { _, _ -> Result.failure(AN_EXCEPTION) },
private val clearCacheLambda: () -> Unit = { lambdaError() },
) : MatrixClient {
var setDisplayNameCalled: Boolean = false
private set
@ -161,6 +163,7 @@ class FakeMatrixClient(
}
override suspend fun clearCache() {
clearCacheLambda()
}
override suspend fun logout(ignoreSdkError: Boolean): String? = simulateLongTask {

View file

@ -24,6 +24,7 @@ class FakePermissionStateProvider(
private var permissionGranted: Boolean = true,
permissionDenied: Boolean = false,
permissionAsked: Boolean = false,
private val resetPermissionLambda: (String) -> Unit = {},
) : PermissionStateProvider {
private val permissionDeniedFlow = MutableStateFlow(permissionDenied)
private val permissionAskedFlow = MutableStateFlow(permissionAsked)
@ -49,5 +50,6 @@ class FakePermissionStateProvider(
override suspend fun resetPermission(permission: String) {
setPermissionAsked(permission, false)
setPermissionDenied(permission, false)
resetPermissionLambda(permission)
}
}

View file

@ -20,12 +20,14 @@ import androidx.compose.runtime.Composable
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.api.PermissionsState
class NoopPermissionsPresenter : PermissionsPresenter {
class NoopPermissionsPresenter(
private val isGranted: Boolean = false,
) : PermissionsPresenter {
@Composable
override fun present(): PermissionsState {
return PermissionsState(
permission = "",
permissionGranted = false,
permissionGranted = isGranted,
shouldShowRationale = false,
showDialog = false,
permissionAlreadyAsked = false,

View file

@ -41,7 +41,7 @@ class FakePermissionsPresenter(
}
fun setPermissionDenied() {
state.value = state.value.copy(permissionAlreadyDenied = true)
state.value = state.value.copy(permissionGranted = false, permissionAlreadyDenied = true)
}
@Composable

View file

@ -25,4 +25,5 @@ android {
dependencies {
implementation(libs.coroutines.core)
implementation(projects.libraries.matrix.api)
implementation(libs.androidx.datastore.preferences)
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.preferences.api.store
package io.element.android.libraries.preferences.api.store
import kotlinx.coroutines.flow.Flow

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.preferences.api.store
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
/**
* Factory used to create a [DataStore] for preferences.
*
* It's a wrapper around AndroidX's `PreferenceDataStoreFactory` to make testing easier.
*/
interface PreferenceDataStoreFactory {
fun create(name: String): DataStore<Preferences>
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.preferences.api.store
package io.element.android.libraries.preferences.api.store
import kotlinx.coroutines.flow.Flow

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.features.preferences.api.store
package io.element.android.libraries.preferences.api.store
import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.CoroutineScope

View file

@ -24,11 +24,11 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.preferences.impl.store
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultPreferencesDataStoreFactory @Inject constructor(
@ApplicationContext private val context: Context,
) : PreferenceDataStoreFactory {
private class DataStoreHolder(name: String) {
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = name)
}
override fun create(name: String): DataStore<Preferences> {
return with(DataStoreHolder(name)) {
context.dataStore
}
}
}

View file

@ -22,11 +22,11 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.preferencesDataStoreFile
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.androidutils.hash.hash
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

View file

@ -18,12 +18,12 @@ package io.element.android.libraries.preferences.impl.store
import android.content.Context
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.features.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import kotlinx.coroutines.CoroutineScope

View file

@ -19,10 +19,10 @@ package io.element.android.libraries.preferences.impl.store
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import kotlinx.coroutines.CoroutineScope
@Module

View file

@ -23,8 +23,9 @@ android {
dependencies {
api(projects.libraries.preferences.api)
implementation(libs.coroutines.core)
implementation(projects.libraries.matrix.api)
implementation(projects.tests.testutils)
implementation(libs.coroutines.core)
implementation(libs.androidx.datastore.preferences)
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.preferences.test
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
import java.io.File
import androidx.datastore.preferences.core.PreferenceDataStoreFactory as AndroidPreferenceDataStoreFactory
class FakePreferenceDataStoreFactory : PreferenceDataStoreFactory {
override fun create(name: String): DataStore<Preferences> {
return AndroidPreferenceDataStoreFactory.create { File.createTempFile("test", ".preferences_pb") }
}
}

View file

@ -16,9 +16,9 @@
package io.element.android.libraries.preferences.test
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.features.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder
import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder
import io.element.android.tests.testutils.lambda.lambdaError

View file

@ -16,7 +16,7 @@
package io.element.android.libraries.preferences.test
import io.element.android.features.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -16,7 +16,7 @@
package io.element.android.libraries.preferences.test
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -17,7 +17,6 @@
package io.element.android.libraries.push.impl.notifications
import android.content.Intent
import io.element.android.features.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId
@ -27,6 +26,7 @@ import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.asEventId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
import io.element.android.libraries.push.impl.R
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent

View file

@ -18,8 +18,6 @@ package io.element.android.libraries.push.impl.notifications
import android.content.Intent
import com.google.common.truth.Truth.assertThat
import io.element.android.features.preferences.api.store.SessionPreferencesStore
import io.element.android.features.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
@ -38,6 +36,8 @@ import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.preferences.test.FakeSessionPreferencesStoreFactory
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager