mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Disable location sharing when no MapTiler config is available
LocationService now exposes the MapTiler config to use: the one provided by the homeserver takes precedence over the build time one, and null means that no map provider is configured. It is now session scoped, and isServiceAvailable() returns a StateFlow derived from this config. When no config is available: - the location sharing option is hidden from the composer, and location events cannot be opened in the location viewer; - the "Location sharing" entry is hidden from the settings; - location events in the timeline render a placeholder, without performing any request to MapTiler. The show and share location screens now get the config from LocationService rather than fetching the homeserver config by themselves. Closes element-hq/element-android-enterprise#69 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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.enterprise.api.EnterpriseService
|
||||
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.state.FtueService
|
||||
import io.element.android.features.ftue.api.state.FtueState
|
||||
import io.element.android.features.home.api.HomeEntryPoint
|
||||
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
||||
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.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
@@ -153,7 +152,7 @@ class LoggedInFlowNode(
|
||||
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
|
||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
|
||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
||||
private val locationService: LocationService,
|
||||
private val loggedInEventProcessorFactory: LoggedInEventProcessor.Factory,
|
||||
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
@@ -636,10 +635,7 @@ class LoggedInFlowNode(
|
||||
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
|
||||
}.collectAsState(SemanticColorsLightDark.default)
|
||||
|
||||
val currentMapTilerConfig = LocalMapTilerConfig.current
|
||||
val updatedMapTilerConfig by produceState(currentMapTilerConfig) {
|
||||
value = customMapTilerConfigProvider.get().getOrNull() ?: currentMapTilerConfig
|
||||
}
|
||||
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||
|
||||
ElementThemeApp(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
@@ -648,7 +644,7 @@ class LoggedInFlowNode(
|
||||
compoundDark = colors.dark,
|
||||
buildMeta = buildMeta,
|
||||
) {
|
||||
CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) {
|
||||
CompositionLocalProvider(LocalMapTilerConfig provides mapTilerConfig) {
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
// 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.
|
||||
|
||||
+17
-8
@@ -11,13 +11,22 @@ import androidx.compose.runtime.staticCompositionLocalOf
|
||||
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 {
|
||||
MapTilerConfig(
|
||||
apiKey = BuildConfig.MAPTILER_API_KEY,
|
||||
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
||||
)
|
||||
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(
|
||||
apiKey = apiKey,
|
||||
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -8,10 +8,21 @@
|
||||
|
||||
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 {
|
||||
/**
|
||||
* 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. */
|
||||
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.
|
||||
*
|
||||
* Handles 4 distinct cases:
|
||||
* Handles 5 distinct cases:
|
||||
* 1. Stale location (pinVariant is StaleLocation) - shows stale map with stale pin, no fetching
|
||||
* 2. Null location - shows blurred placeholder, no pin, no loading
|
||||
* 3. Loading (location != null, fetching) - shows blurred placeholder with loading indicator
|
||||
* 4. Success (location != null, loaded) - shows actual map with pin
|
||||
* 3. Null mapTilerConfig (no map provider configured) - shows blurred placeholder, no pin, no fetching
|
||||
* 4. Loading (location != null, fetching) - shows blurred placeholder with loading indicator
|
||||
* 5. Success (location != null, loaded) - shows actual map with pin
|
||||
*/
|
||||
@Composable
|
||||
fun StaticMapView(
|
||||
@@ -84,7 +85,8 @@ fun StaticMapView(
|
||||
)
|
||||
}
|
||||
// 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(
|
||||
painter = painterResource(R.drawable.blurred_map),
|
||||
canReload = false,
|
||||
@@ -94,7 +96,7 @@ fun StaticMapView(
|
||||
onLoadMapClick = {}
|
||||
)
|
||||
}
|
||||
// Cases 3 & 4: Non-null location - fetch map
|
||||
// Cases 4 & 5: Non-null location - fetch map
|
||||
else -> LoadableMapContent(
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
location = location,
|
||||
|
||||
+24
-7
@@ -8,14 +8,31 @@
|
||||
|
||||
package io.element.android.features.location.impl
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
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.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)
|
||||
class DefaultLocationService : LocationService {
|
||||
override fun isServiceAvailable(): Boolean {
|
||||
return BuildConfig.MAPTILER_API_KEY.isNotEmpty()
|
||||
}
|
||||
@SingleIn(SessionScope::class)
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultLocationService(
|
||||
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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -22,7 +21,7 @@ import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
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.impl.common.LocationConstraintsCheck
|
||||
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.messages.api.MessageComposerContext
|
||||
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.runUpdatingState
|
||||
import io.element.android.libraries.core.extensions.flatMap
|
||||
@@ -73,7 +71,7 @@ class ShareLocationPresenter(
|
||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||
private val liveLocationStore: LiveLocationStore,
|
||||
private val userLocationStateFactory: UserLocationState.Factory,
|
||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
||||
private val locationService: LocationService,
|
||||
) : Presenter<ShareLocationState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
@@ -94,10 +92,7 @@ class ShareLocationPresenter(
|
||||
var pendingLiveLocationShare by remember { mutableStateOf(false) }
|
||||
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||
val currentUser by client.userProfile.collectAsState()
|
||||
val customMapConfig by produceState(AsyncData.Loading()) {
|
||||
// Ignore errors
|
||||
value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull())
|
||||
}
|
||||
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
||||
perms.sendLiveLocationPermissions()
|
||||
}
|
||||
@@ -201,7 +196,7 @@ class ShareLocationPresenter(
|
||||
}
|
||||
|
||||
return ShareLocationState(
|
||||
customMapTilerConfig = customMapConfig,
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
currentUser = currentUser,
|
||||
dialogState = dialogState,
|
||||
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.userlocation.UserLocationState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ShareLocationState(
|
||||
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
||||
val mapTilerConfig: MapTilerConfig?,
|
||||
val currentUser: MatrixUser,
|
||||
val dialogState: Dialog,
|
||||
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.location.impl.common.ui.LocationConstraintsDialogState
|
||||
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.AsyncData
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -71,13 +71,13 @@ class ShareLocationStatePreviewParam : PreviewParameterProvider<ShareLocationSta
|
||||
canShareLiveLocation = true,
|
||||
),
|
||||
aShareLocationState(
|
||||
customMapTilerConfig = AsyncData.Loading(),
|
||||
mapTilerConfig = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aShareLocationState(
|
||||
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
|
||||
mapTilerConfig: MapTilerConfig? = aPreviewMapTilerConfig(),
|
||||
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
||||
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
||||
trackUserPosition: Boolean = false,
|
||||
@@ -88,7 +88,7 @@ fun aShareLocationState(
|
||||
eventSink: (ShareLocationEvent) -> Unit = {},
|
||||
): ShareLocationState {
|
||||
return ShareLocationState(
|
||||
customMapTilerConfig = customMapTilerConfig,
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
currentUser = currentUser,
|
||||
dialogState = dialogState,
|
||||
trackUserLocation = trackUserPosition,
|
||||
|
||||
+2
-2
@@ -130,7 +130,7 @@ private fun ShareLocationViewContent(
|
||||
navigateUp: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val tileStyleUrl = rememberTileStyleUrl(state.customMapTilerConfig.dataOrNull())
|
||||
val tileStyleUrl = rememberTileStyleUrl(state.mapTilerConfig)
|
||||
val mapState = rememberMapState(
|
||||
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
||||
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
||||
@@ -181,7 +181,7 @@ private fun ShareLocationScaffold(
|
||||
)
|
||||
)
|
||||
MapBottomSheetScaffold(
|
||||
isReady = state.customMapTilerConfig.isReady(),
|
||||
isReady = state.mapTilerConfig != null,
|
||||
mapState = mapState,
|
||||
modifier = modifier,
|
||||
scaffoldState = scaffoldState,
|
||||
|
||||
+4
-8
@@ -22,8 +22,8 @@ import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
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.LocationService
|
||||
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.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.userlocation.UserLocationState
|
||||
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.core.coroutine.mapState
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
@@ -68,7 +67,7 @@ class ShowLocationPresenter(
|
||||
private val joinedRoom: JoinedRoom,
|
||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||
private val userLocationStateFactory: UserLocationState.Factory,
|
||||
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
|
||||
private val locationService: LocationService,
|
||||
) : Presenter<ShowLocationState> {
|
||||
@AssistedFactory fun interface Factory {
|
||||
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
||||
@@ -86,10 +85,7 @@ class ShowLocationPresenter(
|
||||
mutableStateOf(LocationConstraintsDialogState.None)
|
||||
}
|
||||
|
||||
val customMapTilerConfig by produceState(AsyncData.Loading()) {
|
||||
// Ignore errors
|
||||
value = AsyncData.Success(customMapTilerConfigProvider.get().getOrNull())
|
||||
}
|
||||
val mapTilerConfig by locationService.mapTilerConfig.collectAsState()
|
||||
|
||||
fun checkLocationConstraints() {
|
||||
val locationConstraints = checkLocationConstraints(
|
||||
@@ -221,7 +217,7 @@ class ShowLocationPresenter(
|
||||
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
|
||||
}
|
||||
return ShowLocationState(
|
||||
customMapTilerConfig = customMapTilerConfig,
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
dialogState = dialogState,
|
||||
locationShares = locationShares,
|
||||
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.LocationMarkerData
|
||||
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.avatar.AvatarData
|
||||
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
|
||||
|
||||
data class ShowLocationState(
|
||||
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
||||
val mapTilerConfig: MapTilerConfig?,
|
||||
val isLive: Boolean,
|
||||
val dialogState: LocationConstraintsDialogState,
|
||||
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.impl.common.ui.LocationConstraintsDialogState
|
||||
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.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
||||
@@ -37,14 +36,21 @@ class ShowLocationStatePreviewParam : PreviewParameterProvider<ShowLocationState
|
||||
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
|
||||
),
|
||||
aShowLocationState(isTrackMyLocation = true),
|
||||
aShowLocationState(customMapTilerConfig = AsyncData.Loading()),
|
||||
aShowLocationState(mapTilerConfig = null),
|
||||
)
|
||||
}
|
||||
|
||||
private const val APP_NAME = "ApplicationName"
|
||||
|
||||
internal fun aPreviewMapTilerConfig() = MapTilerConfig(
|
||||
apiKey = "preview_api_key",
|
||||
lightStyleId = null,
|
||||
darkStyleId = null,
|
||||
baseUrl = null,
|
||||
)
|
||||
|
||||
fun aShowLocationState(
|
||||
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
|
||||
mapTilerConfig: MapTilerConfig? = aPreviewMapTilerConfig(),
|
||||
isLive: Boolean = false,
|
||||
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
|
||||
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
|
||||
@@ -56,7 +62,7 @@ fun aShowLocationState(
|
||||
eventSink: (ShowLocationEvent) -> Unit = {},
|
||||
): ShowLocationState {
|
||||
return ShowLocationState(
|
||||
customMapTilerConfig = customMapTilerConfig,
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
dialogState = constraintsDialogState,
|
||||
locationShares = locationShares.toImmutableList(),
|
||||
focusedLocation = focusedLocation,
|
||||
|
||||
+2
-2
@@ -102,7 +102,7 @@ private fun ShowLocationViewContent(
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val tileStyleUrl = rememberTileStyleUrl(state.customMapTilerConfig.dataOrNull())
|
||||
val tileStyleUrl = rememberTileStyleUrl(state.mapTilerConfig)
|
||||
val mapState = rememberMapState(
|
||||
baseStyle = BaseStyle.Uri(tileStyleUrl),
|
||||
initialCameraPosition = MapDefaults.defaultCameraPosition,
|
||||
@@ -178,7 +178,7 @@ private fun ShowLocationScaffold(
|
||||
}
|
||||
}
|
||||
MapBottomSheetScaffold(
|
||||
isReady = state.customMapTilerConfig.isReady(),
|
||||
isReady = state.mapTilerConfig != null,
|
||||
sheetDragHandle = if (state.isSheetDraggable) {
|
||||
{ BottomSheetDefaults.DragHandle() }
|
||||
} else {
|
||||
|
||||
+45
-4
@@ -9,15 +9,56 @@
|
||||
package io.element.android.features.location.impl
|
||||
|
||||
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.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
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultLocationServiceTest {
|
||||
@Test
|
||||
fun `isServiceAvailable should return value depending on BuildConfig MAPTILER_API_KEY`() {
|
||||
val locationService = DefaultLocationService()
|
||||
assertThat(locationService.isServiceAvailable()).isEqualTo(
|
||||
BuildConfig.MAPTILER_API_KEY.isNotEmpty()
|
||||
fun `when the homeserver does not provide a config, the build time config is used`() = runTest {
|
||||
val locationService = createDefaultLocationService(
|
||||
customMapTilerConfigProvider = { Result.success(null) },
|
||||
)
|
||||
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.live.LiveLocationStore
|
||||
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.libraries.dateformatter.test.FakeDurationFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
@@ -58,7 +59,7 @@ class DefaultShareLocationEntryPointTest {
|
||||
sessionId = room.sessionId,
|
||||
),
|
||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||
customMapTilerConfigProvider = { Result.success(null) },
|
||||
locationService = FakeLocationService(),
|
||||
)
|
||||
},
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
|
||||
+9
-10
@@ -19,9 +19,9 @@ import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
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.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.common.FakeUserLocationStateFactory
|
||||
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.live.LiveLocationStore
|
||||
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.libraries.dateformatter.test.FakeDurationFormatter
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
@@ -89,7 +90,7 @@ class ShareLocationPresenterTest {
|
||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||
liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId),
|
||||
client: FakeMatrixClient = fakeMatrixClient,
|
||||
customMapTilerConfigProvider: () -> Result<MapTilerConfig?> = { Result.success(null) },
|
||||
locationService: LocationService = FakeLocationService(mapTilerConfig = aMapTilerConfig()),
|
||||
): ShareLocationPresenter = ShareLocationPresenter(
|
||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||
room = joinedRoom,
|
||||
@@ -103,7 +104,7 @@ class ShareLocationPresenterTest {
|
||||
liveLocationShareManager = liveLocationShareManager,
|
||||
liveLocationStore = liveLocationStore,
|
||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||
customMapTilerConfigProvider = customMapTilerConfigProvider,
|
||||
locationService = locationService,
|
||||
)
|
||||
|
||||
@Test
|
||||
@@ -118,26 +119,24 @@ class ShareLocationPresenterTest {
|
||||
val shareLocationPresenter = createShareLocationPresenter()
|
||||
shareLocationPresenter.test {
|
||||
val state = awaitFirstItem()
|
||||
assertThat(state.customMapTilerConfig.isLoading()).isFalse()
|
||||
assertThat(state.mapTilerConfig).isNotNull()
|
||||
assertThat(state.trackUserLocation).isTrue()
|
||||
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||
fun `present - mapTilerConfig is provided by the location service`() = runTest {
|
||||
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||
val shareLocationPresenter = createShareLocationPresenter(
|
||||
client = FakeMatrixClient(
|
||||
sessionId = A_USER_ID,
|
||||
),
|
||||
customMapTilerConfigProvider = { Result.success(mapTilerConfig) }
|
||||
locationService = FakeLocationService(mapTilerConfig = mapTilerConfig),
|
||||
)
|
||||
shareLocationPresenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
||||
assertThat(awaitItem().mapTilerConfig).isEqualTo(mapTilerConfig)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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.permissions.FakePermissionsPresenter
|
||||
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.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
@@ -51,7 +52,7 @@ class DefaultShowLocationEntryPointTest {
|
||||
joinedRoom = joinedRoom,
|
||||
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||
customMapTilerConfigProvider = { Result.success(null) },
|
||||
locationService = FakeLocationService(),
|
||||
)
|
||||
},
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
|
||||
+10
-14
@@ -12,9 +12,9 @@ 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.features.enterprise.api.remoteconfig.CustomMapTilerConfigProvider
|
||||
import io.element.android.features.enterprise.test.remoteconfig.aMapTilerConfig
|
||||
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.impl.aPermissionsState
|
||||
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.ui.LocationConstraintsDialogState
|
||||
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.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
@@ -66,7 +67,7 @@ class ShowLocationPresenterTest {
|
||||
locationActions: FakeLocationActions = fakeLocationActions,
|
||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||
customMapTilerConfigProvider: CustomMapTilerConfigProvider = { Result.success(null) },
|
||||
locationService: LocationService = FakeLocationService(mapTilerConfig = aMapTilerConfig()),
|
||||
) = ShowLocationPresenter(
|
||||
mode = mode,
|
||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||
@@ -77,18 +78,16 @@ class ShowLocationPresenterTest {
|
||||
joinedRoom = joinedRoom,
|
||||
liveLocationShareManager = liveLocationShareManager,
|
||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||
customMapTilerConfigProvider = customMapTilerConfigProvider,
|
||||
locationService = locationService,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||
fun `present - mapTilerConfig is provided by the location service`() = runTest {
|
||||
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||
val shareLocationPresenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(mapTilerConfig) })
|
||||
shareLocationPresenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
||||
val showLocationPresenter = createShowLocationPresenter(locationService = FakeLocationService(mapTilerConfig = mapTilerConfig))
|
||||
showLocationPresenter.test {
|
||||
assertThat(awaitItem().mapTilerConfig).isEqualTo(mapTilerConfig)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +119,7 @@ class ShowLocationPresenterTest {
|
||||
fun `centers on user location`() = runTest {
|
||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||
|
||||
val presenter = createShowLocationPresenter(customMapTilerConfigProvider = { Result.success(aMapTilerConfig()) })
|
||||
val presenter = createShowLocationPresenter()
|
||||
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
@@ -407,9 +406,6 @@ class ShowLocationPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.locationShares).isEmpty()
|
||||
|
||||
// Wait until we receive the custom map style URL result
|
||||
skipItems(1)
|
||||
|
||||
// Emit a new live share
|
||||
liveSharesFlow.value = listOf(
|
||||
aLiveLocationShare(userId = userId)
|
||||
|
||||
@@ -16,6 +16,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
api(projects.features.location.api)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.tests.testutils)
|
||||
|
||||
+13
-2
@@ -8,10 +8,21 @@
|
||||
|
||||
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 kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class FakeLocationService(
|
||||
private val isServiceAvailable: Boolean = false,
|
||||
mapTilerConfig: MapTilerConfig? = null,
|
||||
) : 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,
|
||||
)
|
||||
}
|
||||
NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable() }
|
||||
NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable().value }
|
||||
} else {
|
||||
displayVulkanNotSupportedError = true
|
||||
null
|
||||
|
||||
+2
-5
@@ -184,10 +184,7 @@ class MessageComposerPresenter(
|
||||
|
||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||
|
||||
val canShareLocation = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
canShareLocation.value = locationService.isServiceAvailable()
|
||||
}
|
||||
val canShareLocation by remember { locationService.isServiceAvailable() }.collectAsState()
|
||||
|
||||
val isSendGalleryMessagesEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SendGalleryMessages)
|
||||
.collectAsState(initial = false)
|
||||
@@ -431,7 +428,7 @@ class MessageComposerPresenter(
|
||||
isInThreadTimeline = isInThread,
|
||||
showAttachmentSourcePicker = showAttachmentSourcePicker,
|
||||
showTextFormatting = showTextFormatting,
|
||||
canShareLocation = canShareLocation.value,
|
||||
canShareLocation = canShareLocation,
|
||||
suggestions = suggestions.toImmutableList(),
|
||||
resolveMentionDisplay = resolveMentionDisplay,
|
||||
resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay,
|
||||
|
||||
@@ -75,6 +75,7 @@ dependencies {
|
||||
implementation(projects.features.analytics.api)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.features.licenses.api)
|
||||
implementation(projects.features.location.api)
|
||||
implementation(projects.features.logout.api)
|
||||
implementation(projects.features.deactivation.api)
|
||||
implementation(projects.features.invite.api)
|
||||
@@ -109,6 +110,7 @@ dependencies {
|
||||
testImplementation(projects.features.enterprise.test)
|
||||
testImplementation(projects.features.invite.test)
|
||||
testImplementation(projects.features.licenses.test)
|
||||
testImplementation(projects.features.location.test)
|
||||
testImplementation(projects.features.lockscreen.test)
|
||||
testImplementation(projects.features.logout.test)
|
||||
testImplementation(projects.libraries.indicator.test)
|
||||
|
||||
+5
@@ -19,6 +19,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.compound.theme.Theme
|
||||
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.userstatus.UserStatusState
|
||||
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
||||
@@ -47,6 +48,7 @@ class PreferencesRootPresenter(
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val locationService: LocationService,
|
||||
private val sessionStore: SessionStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val userStatusPresenter: Presenter<UserStatusState>,
|
||||
@@ -115,6 +117,8 @@ class PreferencesRootPresenter(
|
||||
|
||||
val showDeveloperSettings by showDeveloperSettingsProvider.showDeveloperSettings.collectAsState()
|
||||
|
||||
val isLocationServiceAvailable by remember { locationService.isServiceAvailable() }.collectAsState()
|
||||
|
||||
fun handleEvent(event: PreferencesRootEvent) {
|
||||
when (event) {
|
||||
is PreferencesRootEvent.OnVersionInfoClick -> {
|
||||
@@ -150,6 +154,7 @@ class PreferencesRootPresenter(
|
||||
showAnalyticsSettings = hasAnalyticsProviders,
|
||||
showDeveloperSettings = showDeveloperSettings,
|
||||
showLabsItem = showLabsItem,
|
||||
showLocationSettings = isLocationServiceAvailable,
|
||||
snackbarMessage = snackbarMessage,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ data class PreferencesRootState(
|
||||
val showAnalyticsSettings: Boolean,
|
||||
val showDeveloperSettings: Boolean,
|
||||
val showLabsItem: Boolean,
|
||||
val showLocationSettings: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val eventSink: (PreferencesRootEvent) -> Unit,
|
||||
)
|
||||
|
||||
+4
@@ -53,6 +53,8 @@ open class PreferencesRootStatePreviewParam : PreviewParameterProvider<Preferenc
|
||||
aPreferencesRootState(userStatusState = aUserStatusState(pickerState = UserStatusPickerState.ShowingPicker)),
|
||||
// Minimal state
|
||||
aPreferencesRootState(userStatusState = null),
|
||||
// Location sharing not available
|
||||
aPreferencesRootState(showLocationSettings = false),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,6 +70,7 @@ fun aPreferencesRootState(
|
||||
showAnalyticsSettings: Boolean = false,
|
||||
showDeveloperSettings: Boolean = false,
|
||||
showLabsItem: Boolean = false,
|
||||
showLocationSettings: Boolean = true,
|
||||
userStatusState: UserStatusState? = aUserStatusState(),
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
eventSink: (PreferencesRootEvent) -> Unit = {},
|
||||
@@ -83,6 +86,7 @@ fun aPreferencesRootState(
|
||||
showAnalyticsSettings = showAnalyticsSettings,
|
||||
showDeveloperSettings = showDeveloperSettings,
|
||||
showLabsItem = showLabsItem,
|
||||
showLocationSettings = showLocationSettings,
|
||||
userStatusState = userStatusState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
eventSink = eventSink,
|
||||
|
||||
+7
-5
@@ -330,11 +330,13 @@ private fun AppSettingsSection(
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
|
||||
onClick = onOpenLockScreenSettings,
|
||||
)
|
||||
ListItem(
|
||||
content = { Text(stringResource(id = CommonStrings.common_location_sharing)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.LocationPin())),
|
||||
onClick = onOpenLocationSettings,
|
||||
)
|
||||
if (state.showLocationSettings) {
|
||||
ListItem(
|
||||
content = { Text(stringResource(id = CommonStrings.common_location_sharing)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.LocationPin())),
|
||||
onClick = onOpenLocationSettings,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -13,6 +13,8 @@ package io.element.android.features.preferences.impl.root
|
||||
import app.cash.turbine.ReceiveTurbine
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
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.userstatus.aUserStatusState
|
||||
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
|
||||
@@ -78,10 +80,21 @@ class PreferencesRootPresenterTest {
|
||||
assertThat(loadedState.showAnalyticsSettings).isFalse()
|
||||
assertThat(loadedState.showDeveloperSettings).isTrue()
|
||||
assertThat(loadedState.showLabsItem).isFalse()
|
||||
assertThat(loadedState.showLocationSettings).isTrue()
|
||||
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
|
||||
fun `present - user status state is null when the server does not support it`() = runTest {
|
||||
createPreferencesRootPresenter(
|
||||
@@ -291,6 +304,7 @@ class PreferencesRootPresenterTest {
|
||||
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
locationService: LocationService = FakeLocationService(isServiceAvailable = true),
|
||||
) = PreferencesRootPresenter(
|
||||
matrixClient = matrixClient,
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
@@ -298,6 +312,7 @@ class PreferencesRootPresenterTest {
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
showDeveloperSettingsProvider = showDeveloperSettingsProvider,
|
||||
featureFlagService = featureFlagService,
|
||||
locationService = locationService,
|
||||
sessionStore = sessionStore,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
userStatusPresenter = { aUserStatusState() },
|
||||
|
||||
Reference in New Issue
Block a user