mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 16:10:00 +00:00
Merge pull request #7911 from element-hq/feature/bma/disableLocationSharingWithoutMapTiler
Disable location sharing when no MapTiler config is available
This commit is contained in:
@@ -14,7 +14,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -52,13 +51,13 @@ import io.element.android.features.callnative.api.NativeCallHost
|
|||||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||||
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
|
||||||
import io.element.android.features.ftue.api.FtueEntryPoint
|
import io.element.android.features.ftue.api.FtueEntryPoint
|
||||||
import io.element.android.features.ftue.api.state.FtueService
|
import io.element.android.features.ftue.api.state.FtueService
|
||||||
import io.element.android.features.ftue.api.state.FtueState
|
import io.element.android.features.ftue.api.state.FtueState
|
||||||
import io.element.android.features.home.api.HomeEntryPoint
|
import io.element.android.features.home.api.HomeEntryPoint
|
||||||
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
||||||
import io.element.android.features.location.api.LocalMapTilerConfig
|
import io.element.android.features.location.api.LocalMapTilerConfig
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
@@ -153,7 +152,7 @@ class LoggedInFlowNode(
|
|||||||
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
|
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
|
||||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||||
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
|
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
private val locationService: LocationService,
|
||||||
private val loggedInEventProcessorFactory: LoggedInEventProcessor.Factory,
|
private val loggedInEventProcessorFactory: LoggedInEventProcessor.Factory,
|
||||||
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
|
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
@@ -636,10 +635,7 @@ class LoggedInFlowNode(
|
|||||||
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
|
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
|
||||||
}.collectAsState(SemanticColorsLightDark.default)
|
}.collectAsState(SemanticColorsLightDark.default)
|
||||||
|
|
||||||
val currentMapTilerConfig = LocalMapTilerConfig.current
|
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||||
val updatedMapTilerConfig by produceState(currentMapTilerConfig) {
|
|
||||||
value = customMapTilerConfigProvider.get().getOrNull() ?: currentMapTilerConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
ElementThemeApp(
|
ElementThemeApp(
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
@@ -648,7 +644,7 @@ class LoggedInFlowNode(
|
|||||||
compoundDark = colors.dark,
|
compoundDark = colors.dark,
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) {
|
CompositionLocalProvider(LocalMapTilerConfig provides mapTilerConfig) {
|
||||||
val isOnline by syncService.isOnline.collectAsState()
|
val isOnline by syncService.isOnline.collectAsState()
|
||||||
// Outside the connectivity indicator so the two strips stack rather than fight: an
|
// Outside the connectivity indicator so the two strips stack rather than fight: an
|
||||||
// offline banner during a call belongs under the call bar, not over it.
|
// offline banner during a call belongs under the call bar, not over it.
|
||||||
|
|||||||
+12
-3
@@ -11,13 +11,22 @@ import androidx.compose.runtime.staticCompositionLocalOf
|
|||||||
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a [MapTilerConfig] for the MapLibre maps.
|
* Provides the [MapTilerConfig] for the MapLibre maps, or null if no map provider is configured.
|
||||||
*/
|
*/
|
||||||
val LocalMapTilerConfig = staticCompositionLocalOf {
|
val LocalMapTilerConfig = staticCompositionLocalOf { buildTimeMapTilerConfig() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [MapTilerConfig] defined at build time, or null if no MapTiler API key has been provided.
|
||||||
|
*/
|
||||||
|
fun buildTimeMapTilerConfig(): MapTilerConfig? {
|
||||||
|
return BuildConfig.MAPTILER_API_KEY
|
||||||
|
.takeIf { it.isNotEmpty() }
|
||||||
|
?.let { apiKey ->
|
||||||
MapTilerConfig(
|
MapTilerConfig(
|
||||||
apiKey = BuildConfig.MAPTILER_API_KEY,
|
apiKey = apiKey,
|
||||||
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||||
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||||
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -8,10 +8,21 @@
|
|||||||
|
|
||||||
package io.element.android.features.location.api
|
package io.element.android.features.location.api
|
||||||
|
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
||||||
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells whether the location features can be used at all, which depends on the build having a map provider configured.
|
* Tells whether the location features can be used at all, which depends on a map provider being configured,
|
||||||
|
* either by the homeserver or at build time.
|
||||||
*/
|
*/
|
||||||
interface LocationService {
|
interface LocationService {
|
||||||
|
/**
|
||||||
|
* The [MapTilerConfig] to use to render the maps, or null if no map provider is configured.
|
||||||
|
* The configuration provided by the homeserver takes precedence over the one defined at build time.
|
||||||
|
*/
|
||||||
|
val mapTilerConfig: StateFlow<MapTilerConfig?>
|
||||||
|
|
||||||
/** Whether location sharing and map rendering are available; `false` disables the whole feature in the UI. */
|
/** Whether location sharing and map rendering are available; `false` disables the whole feature in the UI. */
|
||||||
fun isServiceAvailable(): Boolean
|
fun isServiceAvailable(): StateFlow<Boolean> = mapTilerConfig.mapState { it != null }
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -50,11 +50,12 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
|
|||||||
/**
|
/**
|
||||||
* Shows a static map image downloaded via a third party service's static maps API.
|
* Shows a static map image downloaded via a third party service's static maps API.
|
||||||
*
|
*
|
||||||
* Handles 4 distinct cases:
|
* Handles 5 distinct cases:
|
||||||
* 1. Stale location (pinVariant is StaleLocation) - shows stale map with stale pin, no fetching
|
* 1. Stale location (pinVariant is StaleLocation) - shows stale map with stale pin, no fetching
|
||||||
* 2. Null location - shows blurred placeholder, no pin, no loading
|
* 2. Null location - shows blurred placeholder, no pin, no loading
|
||||||
* 3. Loading (location != null, fetching) - shows blurred placeholder with loading indicator
|
* 3. Null mapTilerConfig (no map provider configured) - shows blurred placeholder, no pin, no fetching
|
||||||
* 4. Success (location != null, loaded) - shows actual map with pin
|
* 4. Loading (location != null, fetching) - shows blurred placeholder with loading indicator
|
||||||
|
* 5. Success (location != null, loaded) - shows actual map with pin
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun StaticMapView(
|
fun StaticMapView(
|
||||||
@@ -84,7 +85,8 @@ fun StaticMapView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Case 2: Null location - show blurred placeholder, no pin, no loading
|
// Case 2: Null location - show blurred placeholder, no pin, no loading
|
||||||
location == null -> {
|
// Case 3: No map provider configured - show blurred placeholder, no pin, no fetching
|
||||||
|
location == null || mapTilerConfig == null && !LocalInspectionMode.current -> {
|
||||||
StaticMapPlaceholder(
|
StaticMapPlaceholder(
|
||||||
painter = painterResource(R.drawable.blurred_map),
|
painter = painterResource(R.drawable.blurred_map),
|
||||||
canReload = false,
|
canReload = false,
|
||||||
@@ -94,7 +96,7 @@ fun StaticMapView(
|
|||||||
onLoadMapClick = {}
|
onLoadMapClick = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Cases 3 & 4: Non-null location - fetch map
|
// Cases 4 & 5: Non-null location - fetch map
|
||||||
else -> LoadableMapContent(
|
else -> LoadableMapContent(
|
||||||
mapTilerConfig = mapTilerConfig,
|
mapTilerConfig = mapTilerConfig,
|
||||||
location = location,
|
location = location,
|
||||||
|
|||||||
+24
-7
@@ -8,14 +8,31 @@
|
|||||||
|
|
||||||
package io.element.android.features.location.impl
|
package io.element.android.features.location.impl
|
||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.location.api.BuildConfig
|
import dev.zacsweers.metro.SingleIn
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
||||||
import io.element.android.features.location.api.LocationService
|
import io.element.android.features.location.api.LocationService
|
||||||
|
import io.element.android.features.location.api.buildTimeMapTilerConfig
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@SingleIn(SessionScope::class)
|
||||||
class DefaultLocationService : LocationService {
|
@ContributesBinding(SessionScope::class)
|
||||||
override fun isServiceAvailable(): Boolean {
|
class DefaultLocationService(
|
||||||
return BuildConfig.MAPTILER_API_KEY.isNotEmpty()
|
customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
||||||
}
|
@SessionCoroutineScope sessionCoroutineScope: CoroutineScope,
|
||||||
|
) : LocationService {
|
||||||
|
override val mapTilerConfig: StateFlow<MapTilerConfig?> = flow {
|
||||||
|
customMapTilerConfigProvider.get().getOrNull()?.let { emit(it) }
|
||||||
|
}.stateIn(
|
||||||
|
scope = sessionCoroutineScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = buildTimeMapTilerConfig(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-9
@@ -14,7 +14,6 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -22,7 +21,7 @@ import dev.zacsweers.metro.Assisted
|
|||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
import io.element.android.features.location.impl.common.LocationConstraintsCheck
|
import io.element.android.features.location.impl.common.LocationConstraintsCheck
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
@@ -38,7 +37,6 @@ import io.element.android.features.location.impl.common.userlocation.UserLocatio
|
|||||||
import io.element.android.features.location.impl.live.LiveLocationStore
|
import io.element.android.features.location.impl.live.LiveLocationStore
|
||||||
import io.element.android.features.messages.api.MessageComposerContext
|
import io.element.android.features.messages.api.MessageComposerContext
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.core.extensions.flatMap
|
import io.element.android.libraries.core.extensions.flatMap
|
||||||
@@ -73,7 +71,7 @@ class ShareLocationPresenter(
|
|||||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
private val liveLocationStore: LiveLocationStore,
|
private val liveLocationStore: LiveLocationStore,
|
||||||
private val userLocationStateFactory: UserLocationState.Factory,
|
private val userLocationStateFactory: UserLocationState.Factory,
|
||||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
private val locationService: LocationService,
|
||||||
) : Presenter<ShareLocationState> {
|
) : Presenter<ShareLocationState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
@@ -94,10 +92,7 @@ class ShareLocationPresenter(
|
|||||||
var pendingLiveLocationShare by remember { mutableStateOf(false) }
|
var pendingLiveLocationShare by remember { mutableStateOf(false) }
|
||||||
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||||
val currentUser by client.userProfile.collectAsState()
|
val currentUser by client.userProfile.collectAsState()
|
||||||
val customMapConfig by produceState(AsyncData.Loading()) {
|
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||||
// Ignore errors
|
|
||||||
value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull())
|
|
||||||
}
|
|
||||||
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
||||||
perms.sendLiveLocationPermissions()
|
perms.sendLiveLocationPermissions()
|
||||||
}
|
}
|
||||||
@@ -201,7 +196,7 @@ class ShareLocationPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
customMapTilerConfig = customMapConfig,
|
mapTilerConfig = mapTilerConfig,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
|
|||||||
+1
-2
@@ -12,12 +12,11 @@ import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
|||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShareLocationState(
|
data class ShareLocationState(
|
||||||
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
val mapTilerConfig: MapTilerConfig?,
|
||||||
val currentUser: MatrixUser,
|
val currentUser: MatrixUser,
|
||||||
val dialogState: Dialog,
|
val dialogState: Dialog,
|
||||||
val trackUserLocation: Boolean,
|
val trackUserLocation: Boolean,
|
||||||
|
|||||||
+4
-4
@@ -12,8 +12,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||||||
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
||||||
|
import io.element.android.features.location.impl.show.aPreviewMapTilerConfig
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
@@ -71,13 +71,13 @@ class ShareLocationStatePreviewParam : PreviewParameterProvider<ShareLocationSta
|
|||||||
canShareLiveLocation = true,
|
canShareLiveLocation = true,
|
||||||
),
|
),
|
||||||
aShareLocationState(
|
aShareLocationState(
|
||||||
customMapTilerConfig = AsyncData.Loading(),
|
mapTilerConfig = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aShareLocationState(
|
fun aShareLocationState(
|
||||||
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
|
mapTilerConfig: MapTilerConfig? = aPreviewMapTilerConfig(),
|
||||||
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
||||||
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
||||||
trackUserPosition: Boolean = false,
|
trackUserPosition: Boolean = false,
|
||||||
@@ -88,7 +88,7 @@ fun aShareLocationState(
|
|||||||
eventSink: (ShareLocationEvent) -> Unit = {},
|
eventSink: (ShareLocationEvent) -> Unit = {},
|
||||||
): ShareLocationState {
|
): ShareLocationState {
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
customMapTilerConfig = customMapTilerConfig,
|
mapTilerConfig = mapTilerConfig,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
|
|||||||
+2
-2
@@ -130,7 +130,7 @@ private fun ShareLocationViewContent(
|
|||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val tileStyleUrl = rememberTileStyleUrl(state.customMapTilerConfig.dataOrNull())
|
val tileStyleUrl = rememberTileStyleUrl(state.mapTilerConfig)
|
||||||
val mapState = rememberMapState(
|
val mapState = rememberMapState(
|
||||||
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
||||||
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
||||||
@@ -181,7 +181,7 @@ private fun ShareLocationScaffold(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
isReady = state.customMapTilerConfig.isReady(),
|
isReady = state.mapTilerConfig != null,
|
||||||
mapState = mapState,
|
mapState = mapState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
|
|||||||
+4
-8
@@ -22,8 +22,8 @@ import androidx.compose.runtime.setValue
|
|||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.ShowLocationMode
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
import io.element.android.features.location.api.live.isCurrentlySharing
|
import io.element.android.features.location.api.live.isCurrentlySharing
|
||||||
@@ -39,7 +39,6 @@ import io.element.android.features.location.impl.common.toDialogState
|
|||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
||||||
import io.element.android.features.location.impl.common.userlocation.toLocationMeasurement
|
import io.element.android.features.location.impl.common.userlocation.toLocationMeasurement
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
@@ -68,7 +67,7 @@ class ShowLocationPresenter(
|
|||||||
private val joinedRoom: JoinedRoom,
|
private val joinedRoom: JoinedRoom,
|
||||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
private val userLocationStateFactory: UserLocationState.Factory,
|
private val userLocationStateFactory: UserLocationState.Factory,
|
||||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
private val locationService: LocationService,
|
||||||
) : Presenter<ShowLocationState> {
|
) : Presenter<ShowLocationState> {
|
||||||
@AssistedFactory fun interface Factory {
|
@AssistedFactory fun interface Factory {
|
||||||
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
||||||
@@ -86,10 +85,7 @@ class ShowLocationPresenter(
|
|||||||
mutableStateOf(LocationConstraintsDialogState.None)
|
mutableStateOf(LocationConstraintsDialogState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
val customMapTilerConfig by produceState(AsyncData.Loading()) {
|
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||||
// Ignore errors
|
|
||||||
value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkLocationConstraints() {
|
fun checkLocationConstraints() {
|
||||||
val locationConstraints = checkLocationConstraints(
|
val locationConstraints = checkLocationConstraints(
|
||||||
@@ -221,7 +217,7 @@ class ShowLocationPresenter(
|
|||||||
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
|
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
|
||||||
}
|
}
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
customMapTilerConfig = customMapTilerConfig,
|
mapTilerConfig = mapTilerConfig,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
locationShares = locationShares,
|
locationShares = locationShares,
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+1
-2
@@ -13,7 +13,6 @@ import io.element.android.features.location.api.Location
|
|||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||||
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.designsystem.components.PinVariant
|
import io.element.android.libraries.designsystem.components.PinVariant
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
@@ -21,7 +20,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShowLocationState(
|
data class ShowLocationState(
|
||||||
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
val mapTilerConfig: MapTilerConfig?,
|
||||||
val isLive: Boolean,
|
val isLive: Boolean,
|
||||||
val dialogState: LocationConstraintsDialogState,
|
val dialogState: LocationConstraintsDialogState,
|
||||||
val locationShares: ImmutableList<LocationShareItem>,
|
val locationShares: ImmutableList<LocationShareItem>,
|
||||||
|
|||||||
+10
-4
@@ -13,7 +13,6 @@ import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
|||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
import io.element.android.features.location.impl.common.userlocation.UserLocationState
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
||||||
@@ -37,14 +36,21 @@ class ShowLocationStatePreviewParam : PreviewParameterProvider<ShowLocationState
|
|||||||
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
|
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
|
||||||
),
|
),
|
||||||
aShowLocationState(isTrackMyLocation = true),
|
aShowLocationState(isTrackMyLocation = true),
|
||||||
aShowLocationState(customMapTilerConfig = AsyncData.Loading()),
|
aShowLocationState(mapTilerConfig = null),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val APP_NAME = "ApplicationName"
|
private const val APP_NAME = "ApplicationName"
|
||||||
|
|
||||||
|
internal fun aPreviewMapTilerConfig() = MapTilerConfig(
|
||||||
|
apiKey = "preview_api_key",
|
||||||
|
lightStyleId = null,
|
||||||
|
darkStyleId = null,
|
||||||
|
baseUrl = null,
|
||||||
|
)
|
||||||
|
|
||||||
fun aShowLocationState(
|
fun aShowLocationState(
|
||||||
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
|
mapTilerConfig: MapTilerConfig? = aPreviewMapTilerConfig(),
|
||||||
isLive: Boolean = false,
|
isLive: Boolean = false,
|
||||||
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
|
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
|
||||||
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
|
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
|
||||||
@@ -56,7 +62,7 @@ fun aShowLocationState(
|
|||||||
eventSink: (ShowLocationEvent) -> Unit = {},
|
eventSink: (ShowLocationEvent) -> Unit = {},
|
||||||
): ShowLocationState {
|
): ShowLocationState {
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
customMapTilerConfig = customMapTilerConfig,
|
mapTilerConfig = mapTilerConfig,
|
||||||
dialogState = constraintsDialogState,
|
dialogState = constraintsDialogState,
|
||||||
locationShares = locationShares.toImmutableList(),
|
locationShares = locationShares.toImmutableList(),
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+2
-2
@@ -102,7 +102,7 @@ private fun ShowLocationViewContent(
|
|||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val tileStyleUrl = rememberTileStyleUrl(state.customMapTilerConfig.dataOrNull())
|
val tileStyleUrl = rememberTileStyleUrl(state.mapTilerConfig)
|
||||||
val mapState = rememberMapState(
|
val mapState = rememberMapState(
|
||||||
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
||||||
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
||||||
@@ -178,7 +178,7 @@ private fun ShowLocationScaffold(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
isReady = state.customMapTilerConfig.isReady(),
|
isReady = state.mapTilerConfig != null,
|
||||||
sheetDragHandle = if (state.isSheetDraggable) {
|
sheetDragHandle = if (state.isSheetDraggable) {
|
||||||
{ BottomSheetDefaults.DragHandle() }
|
{ BottomSheetDefaults.DragHandle() }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+45
-4
@@ -9,15 +9,56 @@
|
|||||||
package io.element.android.features.location.impl
|
package io.element.android.features.location.impl
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
||||||
|
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
||||||
import io.element.android.features.location.api.BuildConfig
|
import io.element.android.features.location.api.BuildConfig
|
||||||
|
import io.element.android.features.location.api.buildTimeMapTilerConfig
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runCurrent
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class DefaultLocationServiceTest {
|
class DefaultLocationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
fun `isServiceAvailable should return value depending on BuildConfig MAPTILER_API_KEY`() {
|
fun `when the homeserver does not provide a config, the build time config is used`() = runTest {
|
||||||
val locationService = DefaultLocationService()
|
val locationService = createDefaultLocationService(
|
||||||
assertThat(locationService.isServiceAvailable()).isEqualTo(
|
customMapTilerConfigProvider = { Result.success(null) },
|
||||||
BuildConfig.MAPTILER_API_KEY.isNotEmpty()
|
|
||||||
)
|
)
|
||||||
|
runCurrent()
|
||||||
|
assertThat(locationService.mapTilerConfig.value).isEqualTo(buildTimeMapTilerConfig())
|
||||||
|
assertThat(locationService.isServiceAvailable().value).isEqualTo(BuildConfig.MAPTILER_API_KEY.isNotEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when the homeserver config cannot be retrieved, the build time config is used`() = runTest {
|
||||||
|
val locationService = createDefaultLocationService(
|
||||||
|
customMapTilerConfigProvider = { Result.failure(AN_EXCEPTION) },
|
||||||
|
)
|
||||||
|
runCurrent()
|
||||||
|
assertThat(locationService.mapTilerConfig.value).isEqualTo(buildTimeMapTilerConfig())
|
||||||
|
assertThat(locationService.isServiceAvailable().value).isEqualTo(BuildConfig.MAPTILER_API_KEY.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when the homeserver provides a config, it is used and the service is available`() = runTest {
|
||||||
|
val config = aMapTilerConfig(apiKey = "homeserver_api_key")
|
||||||
|
val locationService = createDefaultLocationService(
|
||||||
|
customMapTilerConfigProvider = { Result.success(config) },
|
||||||
|
)
|
||||||
|
runCurrent()
|
||||||
|
assertThat(locationService.mapTilerConfig.value).isEqualTo(config)
|
||||||
|
assertThat(locationService.isServiceAvailable().value).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createDefaultLocationService(
|
||||||
|
customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
||||||
|
sessionCoroutineScope: CoroutineScope = backgroundScope,
|
||||||
|
) = DefaultLocationService(
|
||||||
|
customMapTilerConfigProvider = customMapTilerConfigProvider,
|
||||||
|
sessionCoroutineScope = sessionCoroutineScope,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -16,6 +16,7 @@ import io.element.android.features.location.impl.common.actions.FakeLocationActi
|
|||||||
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
||||||
import io.element.android.features.location.impl.live.LiveLocationStore
|
import io.element.android.features.location.impl.live.LiveLocationStore
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
@@ -58,7 +59,7 @@ class DefaultShareLocationEntryPointTest {
|
|||||||
sessionId = room.sessionId,
|
sessionId = room.sessionId,
|
||||||
),
|
),
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
customMapTilerConfigProvider = { Result.success(null) },
|
locationService = FakeLocationService(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
|||||||
+9
-10
@@ -19,9 +19,9 @@ import app.cash.turbine.ReceiveTurbine
|
|||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
|
||||||
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.impl.aPermissionsState
|
import io.element.android.features.location.impl.aPermissionsState
|
||||||
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
||||||
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
||||||
@@ -31,6 +31,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS
|
|||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.live.LiveLocationStore
|
import io.element.android.features.location.impl.live.LiveLocationStore
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
@@ -89,7 +90,7 @@ class ShareLocationPresenterTest {
|
|||||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId),
|
liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId),
|
||||||
client: FakeMatrixClient = fakeMatrixClient,
|
client: FakeMatrixClient = fakeMatrixClient,
|
||||||
customMapTilerConfigProvider: () -> Result<MapTilerConfig?> = { Result.success(null) },
|
locationService: LocationService = FakeLocationService(mapTilerConfig = aMapTilerConfig()),
|
||||||
): ShareLocationPresenter = ShareLocationPresenter(
|
): ShareLocationPresenter = ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||||
room = joinedRoom,
|
room = joinedRoom,
|
||||||
@@ -103,7 +104,7 @@ class ShareLocationPresenterTest {
|
|||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
liveLocationStore = liveLocationStore,
|
liveLocationStore = liveLocationStore,
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
customMapTilerConfigProvider = customMapTilerConfigProvider,
|
locationService = locationService,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -118,26 +119,24 @@ class ShareLocationPresenterTest {
|
|||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val state = awaitFirstItem()
|
val state = awaitFirstItem()
|
||||||
assertThat(state.customMapTilerConfig.isLoading()).isFalse()
|
assertThat(state.mapTilerConfig).isNotNull()
|
||||||
assertThat(state.trackUserLocation).isTrue()
|
assertThat(state.trackUserLocation).isTrue()
|
||||||
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
fun `present - mapTilerConfig is provided by the location service`() = runTest {
|
||||||
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||||
val shareLocationPresenter = createShareLocationPresenter(
|
val shareLocationPresenter = createShareLocationPresenter(
|
||||||
client = FakeMatrixClient(
|
client = FakeMatrixClient(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
),
|
),
|
||||||
customMapTilerConfigProvider = { Result.success(mapTilerConfig) }
|
locationService = FakeLocationService(mapTilerConfig = mapTilerConfig),
|
||||||
)
|
)
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val state = awaitItem()
|
assertThat(awaitItem().mapTilerConfig).isEqualTo(mapTilerConfig)
|
||||||
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
cancelAndIgnoreRemainingEvents()
|
||||||
val finalState = awaitItem()
|
|
||||||
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -18,6 +18,7 @@ import io.element.android.features.location.impl.common.FakeUserLocationStateFac
|
|||||||
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
||||||
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
@@ -51,7 +52,7 @@ class DefaultShowLocationEntryPointTest {
|
|||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
customMapTilerConfigProvider = { Result.success(null) },
|
locationService = FakeLocationService(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
|||||||
+10
-14
@@ -12,9 +12,9 @@ import app.cash.molecule.RecompositionMode
|
|||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
|
||||||
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.ShowLocationMode
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.impl.aPermissionsState
|
import io.element.android.features.location.impl.aPermissionsState
|
||||||
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
||||||
@@ -24,6 +24,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE
|
|||||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
@@ -66,7 +67,7 @@ class ShowLocationPresenterTest {
|
|||||||
locationActions: FakeLocationActions = fakeLocationActions,
|
locationActions: FakeLocationActions = fakeLocationActions,
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
customMapTilerConfigProvider: CustomMapTilerConfigProvider = { Result.success(null) },
|
locationService: LocationService = FakeLocationService(mapTilerConfig = aMapTilerConfig()),
|
||||||
) = ShowLocationPresenter(
|
) = ShowLocationPresenter(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||||
@@ -77,18 +78,16 @@ class ShowLocationPresenterTest {
|
|||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
customMapTilerConfigProvider = customMapTilerConfigProvider,
|
locationService = locationService,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
fun `present - mapTilerConfig is provided by the location service`() = runTest {
|
||||||
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||||
val shareLocationPresenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(mapTilerConfig) })
|
val showLocationPresenter = createShowLocationPresenter(locationService = FakeLocationService(mapTilerConfig = mapTilerConfig))
|
||||||
shareLocationPresenter.test {
|
showLocationPresenter.test {
|
||||||
val state = awaitItem()
|
assertThat(awaitItem().mapTilerConfig).isEqualTo(mapTilerConfig)
|
||||||
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
cancelAndIgnoreRemainingEvents()
|
||||||
val finalState = awaitItem()
|
|
||||||
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ class ShowLocationPresenterTest {
|
|||||||
fun `centers on user location`() = runTest {
|
fun `centers on user location`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
|
|
||||||
val presenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(aMapTilerConfig()) })
|
val presenter = createShowLocationPresenter()
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
@@ -407,9 +406,6 @@ class ShowLocationPresenterTest {
|
|||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.locationShares).isEmpty()
|
assertThat(initialState.locationShares).isEmpty()
|
||||||
|
|
||||||
// Wait until we receive the custom map style URL result
|
|
||||||
skipItems(1)
|
|
||||||
|
|
||||||
// Emit a new live share
|
// Emit a new live share
|
||||||
liveSharesFlow.value = listOf(
|
liveSharesFlow.value = listOf(
|
||||||
aLiveLocationShare(userId = userId)
|
aLiveLocationShare(userId = userId)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.features.location.api)
|
api(projects.features.location.api)
|
||||||
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.tests.testutils)
|
implementation(projects.tests.testutils)
|
||||||
|
|||||||
+13
-2
@@ -8,10 +8,21 @@
|
|||||||
|
|
||||||
package io.element.android.features.location.test
|
package io.element.android.features.location.test
|
||||||
|
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.MapTilerConfig
|
||||||
import io.element.android.features.location.api.LocationService
|
import io.element.android.features.location.api.LocationService
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
class FakeLocationService(
|
class FakeLocationService(
|
||||||
private val isServiceAvailable: Boolean = false,
|
mapTilerConfig: MapTilerConfig? = null,
|
||||||
) : LocationService {
|
) : LocationService {
|
||||||
override fun isServiceAvailable() = isServiceAvailable
|
constructor(isServiceAvailable: Boolean) : this(
|
||||||
|
MapTilerConfig(
|
||||||
|
apiKey = "test_api_key",
|
||||||
|
lightStyleId = null,
|
||||||
|
darkStyleId = null,
|
||||||
|
baseUrl = null,
|
||||||
|
).takeIf { isServiceAvailable }
|
||||||
|
)
|
||||||
|
|
||||||
|
override val mapTilerConfig = MutableStateFlow(mapTilerConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -776,7 +776,7 @@ class MessagesFlowNode(
|
|||||||
assetType = event.content.assetType,
|
assetType = event.content.assetType,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable() }
|
NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable().value }
|
||||||
} else {
|
} else {
|
||||||
displayVulkanNotSupportedError = true
|
displayVulkanNotSupportedError = true
|
||||||
null
|
null
|
||||||
|
|||||||
+2
-5
@@ -184,10 +184,7 @@ class MessageComposerPresenter(
|
|||||||
|
|
||||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||||
|
|
||||||
val canShareLocation = remember { mutableStateOf(false) }
|
val canShareLocation by remember { locationService.isServiceAvailable() }.collectAsState()
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
canShareLocation.value = locationService.isServiceAvailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
val isSendGalleryMessagesEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SendGalleryMessages)
|
val isSendGalleryMessagesEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SendGalleryMessages)
|
||||||
.collectAsState(initial = false)
|
.collectAsState(initial = false)
|
||||||
@@ -431,7 +428,7 @@ class MessageComposerPresenter(
|
|||||||
isInThreadTimeline = isInThread,
|
isInThreadTimeline = isInThread,
|
||||||
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
||||||
showTextFormatting = showTextFormatting,
|
showTextFormatting = showTextFormatting,
|
||||||
canShareLocation = canShareLocation.value,
|
canShareLocation = canShareLocation,
|
||||||
suggestions = suggestions.toImmutableList(),
|
suggestions = suggestions.toImmutableList(),
|
||||||
resolveMentionDisplay = resolveMentionDisplay,
|
resolveMentionDisplay = resolveMentionDisplay,
|
||||||
resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ dependencies {
|
|||||||
implementation(projects.features.analytics.api)
|
implementation(projects.features.analytics.api)
|
||||||
implementation(projects.features.enterprise.api)
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.features.licenses.api)
|
implementation(projects.features.licenses.api)
|
||||||
|
implementation(projects.features.location.api)
|
||||||
implementation(projects.features.logout.api)
|
implementation(projects.features.logout.api)
|
||||||
implementation(projects.features.deactivation.api)
|
implementation(projects.features.deactivation.api)
|
||||||
implementation(projects.features.invite.api)
|
implementation(projects.features.invite.api)
|
||||||
@@ -109,6 +110,7 @@ dependencies {
|
|||||||
testImplementation(projects.features.enterprise.test)
|
testImplementation(projects.features.enterprise.test)
|
||||||
testImplementation(projects.features.invite.test)
|
testImplementation(projects.features.invite.test)
|
||||||
testImplementation(projects.features.licenses.test)
|
testImplementation(projects.features.licenses.test)
|
||||||
|
testImplementation(projects.features.location.test)
|
||||||
testImplementation(projects.features.lockscreen.test)
|
testImplementation(projects.features.lockscreen.test)
|
||||||
testImplementation(projects.features.logout.test)
|
testImplementation(projects.features.logout.test)
|
||||||
testImplementation(projects.libraries.indicator.test)
|
testImplementation(projects.libraries.indicator.test)
|
||||||
|
|||||||
+5
@@ -19,6 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.compound.theme.Theme
|
import io.element.android.compound.theme.Theme
|
||||||
import io.element.android.compound.theme.mapToTheme
|
import io.element.android.compound.theme.mapToTheme
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.preferences.impl.account.PreferencesAccountState
|
import io.element.android.features.preferences.impl.account.PreferencesAccountState
|
||||||
import io.element.android.features.preferences.impl.userstatus.UserStatusState
|
import io.element.android.features.preferences.impl.userstatus.UserStatusState
|
||||||
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
||||||
@@ -47,6 +48,7 @@ class PreferencesRootPresenter(
|
|||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
|
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
|
private val locationService: LocationService,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
private val userStatusPresenter: Presenter<UserStatusState>,
|
private val userStatusPresenter: Presenter<UserStatusState>,
|
||||||
@@ -115,6 +117,8 @@ class PreferencesRootPresenter(
|
|||||||
|
|
||||||
val showDeveloperSettings by showDeveloperSettingsProvider.showDeveloperSettings.collectAsState()
|
val showDeveloperSettings by showDeveloperSettingsProvider.showDeveloperSettings.collectAsState()
|
||||||
|
|
||||||
|
val isLocationServiceAvailable by remember { locationService.isServiceAvailable() }.collectAsState()
|
||||||
|
|
||||||
fun handleEvent(event: PreferencesRootEvent) {
|
fun handleEvent(event: PreferencesRootEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is PreferencesRootEvent.OnVersionInfoClick -> {
|
is PreferencesRootEvent.OnVersionInfoClick -> {
|
||||||
@@ -150,6 +154,7 @@ class PreferencesRootPresenter(
|
|||||||
showAnalyticsSettings = hasAnalyticsProviders,
|
showAnalyticsSettings = hasAnalyticsProviders,
|
||||||
showDeveloperSettings = showDeveloperSettings,
|
showDeveloperSettings = showDeveloperSettings,
|
||||||
showLabsItem = showLabsItem,
|
showLabsItem = showLabsItem,
|
||||||
|
showLocationSettings = isLocationServiceAvailable,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
|
|||||||
+1
@@ -32,6 +32,7 @@ data class PreferencesRootState(
|
|||||||
val showAnalyticsSettings: Boolean,
|
val showAnalyticsSettings: Boolean,
|
||||||
val showDeveloperSettings: Boolean,
|
val showDeveloperSettings: Boolean,
|
||||||
val showLabsItem: Boolean,
|
val showLabsItem: Boolean,
|
||||||
|
val showLocationSettings: Boolean,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
val eventSink: (PreferencesRootEvent) -> Unit,
|
val eventSink: (PreferencesRootEvent) -> Unit,
|
||||||
)
|
)
|
||||||
|
|||||||
+4
@@ -53,6 +53,8 @@ open class PreferencesRootStatePreviewParam : PreviewParameterProvider<Preferenc
|
|||||||
aPreferencesRootState(userStatusState = aUserStatusState(pickerState = UserStatusPickerState.ShowingPicker)),
|
aPreferencesRootState(userStatusState = aUserStatusState(pickerState = UserStatusPickerState.ShowingPicker)),
|
||||||
// Minimal state
|
// Minimal state
|
||||||
aPreferencesRootState(userStatusState = null),
|
aPreferencesRootState(userStatusState = null),
|
||||||
|
// Location sharing not available
|
||||||
|
aPreferencesRootState(showLocationSettings = false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +70,7 @@ fun aPreferencesRootState(
|
|||||||
showAnalyticsSettings: Boolean = false,
|
showAnalyticsSettings: Boolean = false,
|
||||||
showDeveloperSettings: Boolean = false,
|
showDeveloperSettings: Boolean = false,
|
||||||
showLabsItem: Boolean = false,
|
showLabsItem: Boolean = false,
|
||||||
|
showLocationSettings: Boolean = true,
|
||||||
userStatusState: UserStatusState? = aUserStatusState(),
|
userStatusState: UserStatusState? = aUserStatusState(),
|
||||||
snackbarMessage: SnackbarMessage? = null,
|
snackbarMessage: SnackbarMessage? = null,
|
||||||
eventSink: (PreferencesRootEvent) -> Unit = {},
|
eventSink: (PreferencesRootEvent) -> Unit = {},
|
||||||
@@ -83,6 +86,7 @@ fun aPreferencesRootState(
|
|||||||
showAnalyticsSettings = showAnalyticsSettings,
|
showAnalyticsSettings = showAnalyticsSettings,
|
||||||
showDeveloperSettings = showDeveloperSettings,
|
showDeveloperSettings = showDeveloperSettings,
|
||||||
showLabsItem = showLabsItem,
|
showLabsItem = showLabsItem,
|
||||||
|
showLocationSettings = showLocationSettings,
|
||||||
userStatusState = userStatusState,
|
userStatusState = userStatusState,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
|
|||||||
+2
@@ -330,12 +330,14 @@ private fun AppSettingsSection(
|
|||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
||||||
onClick = onOpenLockScreenSettings,
|
onClick = onOpenLockScreenSettings,
|
||||||
)
|
)
|
||||||
|
if (state.showLocationSettings) {
|
||||||
ListItem(
|
ListItem(
|
||||||
content = { Text(stringResource(id = CommonStrings.common_location_sharing)) },
|
content = { Text(stringResource(id = CommonStrings.common_location_sharing)) },
|
||||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.LocationPin())),
|
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.LocationPin())),
|
||||||
onClick = onOpenLocationSettings,
|
onClick = onOpenLocationSettings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+15
@@ -13,6 +13,8 @@ package io.element.android.features.preferences.impl.root
|
|||||||
import app.cash.turbine.ReceiveTurbine
|
import app.cash.turbine.ReceiveTurbine
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.compound.theme.Theme
|
import io.element.android.compound.theme.Theme
|
||||||
|
import io.element.android.features.location.api.LocationService
|
||||||
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.features.preferences.impl.account.aPreferencesAccountState
|
import io.element.android.features.preferences.impl.account.aPreferencesAccountState
|
||||||
import io.element.android.features.preferences.impl.userstatus.aUserStatusState
|
import io.element.android.features.preferences.impl.userstatus.aUserStatusState
|
||||||
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
||||||
@@ -78,10 +80,21 @@ class PreferencesRootPresenterTest {
|
|||||||
assertThat(loadedState.showAnalyticsSettings).isFalse()
|
assertThat(loadedState.showAnalyticsSettings).isFalse()
|
||||||
assertThat(loadedState.showDeveloperSettings).isTrue()
|
assertThat(loadedState.showDeveloperSettings).isTrue()
|
||||||
assertThat(loadedState.showLabsItem).isFalse()
|
assertThat(loadedState.showLabsItem).isFalse()
|
||||||
|
assertThat(loadedState.showLocationSettings).isTrue()
|
||||||
assertThat(loadedState.snackbarMessage).isNull()
|
assertThat(loadedState.snackbarMessage).isNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - location settings are hidden when the location service is not available`() = runTest {
|
||||||
|
createPreferencesRootPresenter(
|
||||||
|
locationService = FakeLocationService(isServiceAvailable = false),
|
||||||
|
).test {
|
||||||
|
assertThat(awaitItem().showLocationSettings).isFalse()
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - user status state is null when the server does not support it`() = runTest {
|
fun `present - user status state is null when the server does not support it`() = runTest {
|
||||||
createPreferencesRootPresenter(
|
createPreferencesRootPresenter(
|
||||||
@@ -291,6 +304,7 @@ class PreferencesRootPresenterTest {
|
|||||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
||||||
sessionStore: SessionStore = InMemorySessionStore(),
|
sessionStore: SessionStore = InMemorySessionStore(),
|
||||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||||
|
locationService: LocationService = FakeLocationService(isServiceAvailable = true),
|
||||||
) = PreferencesRootPresenter(
|
) = PreferencesRootPresenter(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
@@ -298,6 +312,7 @@ class PreferencesRootPresenterTest {
|
|||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
showDeveloperSettingsProvider = showDeveloperSettingsProvider,
|
showDeveloperSettingsProvider = showDeveloperSettingsProvider,
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
|
locationService = locationService,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
userStatusPresenter = { aUserStatusState() },
|
userStatusPresenter = { aUserStatusState() },
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user