From 7a2cc5df0ab28357e65c08b552f1e6c47d97b622 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 4 Jul 2023 12:04:41 +0100 Subject: [PATCH 1/4] Tap on locations in timeline to see a larger map Show a fully-featured MapView, centered on the dropped pin, which allows panning/zooming. Share button allows opening in a map application. Supports showing a description at the top of the screen, if one is supplied with the event. Out of scope: showing the local user's location (being done as a separate story). Includes some minor tidying: remove duplicate Location, and make GeoURI parsing a method on that class; fix the pointer location in MapView (I broke it earlier, whoops!) --- features/location/api/build.gradle.kts | 1 + .../location/api/{GeoUris.kt => Location.kt} | 24 ++-- .../location/api/ViewLocationEntryPoint.kt | 29 ++++ .../{GeoUrisKtTest.kt => LocationKtTest.kt} | 38 +++--- features/location/impl/build.gradle.kts | 1 + .../impl/location/LocationUpdatesFlowImpl.kt | 1 + .../features/location/impl/map/MapView.kt | 15 ++- .../impl/view/AndroidLocationActions.kt | 67 ++++++++++ .../location/impl/view/LocationActions.kt | 29 ++++ .../impl/view/ViewLocationEntryPointImpl.kt | 32 +++++ .../ViewLocationEvents.kt} | 13 +- .../location/impl/view/ViewLocationNode.kt | 61 +++++++++ .../impl/view/ViewLocationPresenter.kt | 58 ++++++++ .../location/impl/view/ViewLocationState.kt | 25 ++++ .../impl/view/ViewLocationStateProvider.kt | 42 ++++++ .../location/impl/view/ViewLocationView.kt | 125 ++++++++++++++++++ .../impl/location/LocationUpdatesFlowFake.kt | 5 +- .../location/impl/view/FakeLocationActions.kt | 42 ++++++ .../impl/view/ViewLocationPresenterTest.kt | 73 ++++++++++ .../messages/impl/MessagesFlowNode.kt | 18 +++ .../TimelineItemContentMessageFactory.kt | 4 +- 21 files changed, 659 insertions(+), 44 deletions(-) rename features/location/api/src/main/kotlin/io/element/android/features/location/api/{GeoUris.kt => Location.kt} (60%) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt rename features/location/api/src/test/kotlin/io/element/android/features/location/api/{GeoUrisKtTest.kt => LocationKtTest.kt} (53%) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{location/Location.kt => view/ViewLocationEvents.kt} (72%) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index 4d593e1c53..0e517fd3e6 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -17,6 +17,7 @@ plugins { id("io.element.android-compose-library") alias(libs.plugins.ksp) + id("kotlin-parcelize") } android { diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/Location.kt similarity index 60% rename from features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt rename to features/location/api/src/main/kotlin/io/element/android/features/location/api/Location.kt index 2986d50591..d09e163c30 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/Location.kt @@ -16,19 +16,25 @@ package io.element.android.features.location.api +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + private const val GEO_URI_REGEX = """geo:(?-?\d+(?:\.\d+)?),(?-?\d+(?:\.\d+)?)(?:;u=(?\d+(?:\.\d+)?))?""" +@Parcelize data class Location( val lat: Double, val lon: Double, val accuracy: Float, -) - -fun parseGeoUri(geoUri: String): Location? { - val result = Regex(GEO_URI_REGEX).matchEntire(geoUri) ?: return null - return Location ( - lat = result.groups["latitude"]?.value?.toDoubleOrNull() ?: return null, - lon = result.groups["longitude"]?.value?.toDoubleOrNull() ?: return null, - accuracy = result.groups["uncertainty"]?.value?.toFloatOrNull() ?: 0f, - ) +) : Parcelable { + companion object { + fun fromGeoUri(geoUri: String): Location? { + val result = Regex(GEO_URI_REGEX).matchEntire(geoUri) ?: return null + return Location( + lat = result.groups["latitude"]?.value?.toDoubleOrNull() ?: return null, + lon = result.groups["longitude"]?.value?.toDoubleOrNull() ?: return null, + accuracy = result.groups["uncertainty"]?.value?.toFloatOrNull() ?: 0f, + ) + } + } } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt new file mode 100644 index 0000000000..a5fb396775 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs + +interface ViewLocationEntryPoint : FeatureEntryPoint { + + data class Inputs(val location: Location, val description: String?) : NodeInputs + + fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/LocationKtTest.kt similarity index 53% rename from features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt rename to features/location/api/src/test/kotlin/io/element/android/features/location/api/LocationKtTest.kt index 1c591bb2bb..61d9bd0351 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/LocationKtTest.kt @@ -19,57 +19,57 @@ package io.element.android.features.location.api import com.google.common.truth.Truth.assertThat import org.junit.Test -internal class GeoUrisKtTest { +internal class LocationKtTest { @Test fun `parseGeoUri - returns null for invalid urls`() { - assertThat(parseGeoUri("")).isNull() - assertThat(parseGeoUri("http://example.com/")).isNull() - assertThat(parseGeoUri("geo:")).isNull() - assertThat(parseGeoUri("geo:1.234")).isNull() - assertThat(parseGeoUri("geo:1.234,")).isNull() - assertThat(parseGeoUri("geo:,1.234")).isNull() - assertThat(parseGeoUri("notgeo:1.234,5.678")).isNull() - assertThat(parseGeoUri("geo:+1.234,5.678")).isNull() - assertThat(parseGeoUri("geo:+1.234,*5.678")).isNull() - assertThat(parseGeoUri("geo:not,good")).isNull() - assertThat(parseGeoUri("geo:1.234,5.678;u=wrong")).isNull() - assertThat(parseGeoUri("geo:1.234,5.678trailing")).isNull() + assertThat(Location.fromGeoUri("")).isNull() + assertThat(Location.fromGeoUri("http://example.com/")).isNull() + assertThat(Location.fromGeoUri("geo:")).isNull() + assertThat(Location.fromGeoUri("geo:1.234")).isNull() + assertThat(Location.fromGeoUri("geo:1.234,")).isNull() + assertThat(Location.fromGeoUri("geo:,1.234")).isNull() + assertThat(Location.fromGeoUri("notgeo:1.234,5.678")).isNull() + assertThat(Location.fromGeoUri("geo:+1.234,5.678")).isNull() + assertThat(Location.fromGeoUri("geo:+1.234,*5.678")).isNull() + assertThat(Location.fromGeoUri("geo:not,good")).isNull() + assertThat(Location.fromGeoUri("geo:1.234,5.678;u=wrong")).isNull() + assertThat(Location.fromGeoUri("geo:1.234,5.678trailing")).isNull() } @Test fun `parseGeoUri - returns location for valid urls`() { - assertThat(parseGeoUri("geo:1.234,5.678")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:1.234,5.678")).isEqualTo(Location( lat = 1.234, lon = 5.678, accuracy = 0f, )) - assertThat(parseGeoUri("geo:1,5")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:1,5")).isEqualTo(Location( lat = 1.0, lon = 5.0, accuracy = 0f, )) - assertThat(parseGeoUri("geo:1.234,5.678;u=3000")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:1.234,5.678;u=3000")).isEqualTo(Location( lat = 1.234, lon = 5.678, accuracy = 3000f, )) - assertThat(parseGeoUri("geo:1,5;u=3000")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:1,5;u=3000")).isEqualTo(Location( lat = 1.0, lon = 5.0, accuracy = 3000f, )) - assertThat(parseGeoUri("geo:-1.234,-5.678;u=9.10")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:-1.234,-5.678;u=9.10")).isEqualTo(Location( lat = -1.234, lon = -5.678, accuracy = 9.10f, )) - assertThat(parseGeoUri("geo:-1,-5;u=9.10")).isEqualTo(Location( + assertThat(Location.fromGeoUri("geo:-1,-5;u=9.10")).isEqualTo(Location( lat = -1.0, lon = -5.0, accuracy = 9.10f, diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts index eed060cbf8..dfb5192bea 100644 --- a/features/location/impl/build.gradle.kts +++ b/features/location/impl/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.core) implementation(projects.libraries.matrixui) + implementation(projects.services.analytics.api) implementation(libs.maplibre) implementation(libs.maplibre.annotation) implementation(projects.libraries.uiStrings) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowImpl.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowImpl.kt index b650fd51de..f4fcd25de1 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowImpl.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowImpl.kt @@ -24,6 +24,7 @@ import androidx.core.content.getSystemService import androidx.core.location.LocationListenerCompat import androidx.core.location.LocationManagerCompat import androidx.core.location.LocationRequestCompat +import io.element.android.features.location.api.Location import io.element.android.libraries.core.coroutine.CoroutineDispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/map/MapView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/map/MapView.kt index b3104d1ed9..0eac47bf6c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/map/MapView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/map/MapView.kt @@ -19,6 +19,7 @@ package io.element.android.features.location.impl.map import android.annotation.SuppressLint import android.view.Gravity import androidx.annotation.DrawableRes +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable @@ -29,7 +30,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.tooling.preview.Preview @@ -44,10 +47,12 @@ import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions +import com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM +import io.element.android.features.location.api.Location import io.element.android.features.location.api.internal.buildTileServerUrl -import io.element.android.features.location.impl.location.Location import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -69,7 +74,11 @@ fun MapView( // When in preview, early return a Box with the received modifier preserving layout if (LocalInspectionMode.current) { @Suppress("ModifierReused") // False positive, the modifier is not reused due to the early return. - Box(modifier = modifier) + Box( + modifier = modifier.background(Color.DarkGray) + ) { + Text("[MapView]", modifier = Modifier.align(Alignment.Center)) + } return } @@ -155,7 +164,7 @@ fun MapView( .withLatLng(LatLng(location.lat, location.lon)) .withIconImage("pin") .withIconSize(1.3f) - .withIconOffset(arrayOf(0f, 0.5f)) + .withIconAnchor(ICON_ANCHOR_BOTTOM) ) Timber.d("Shown pin at location: $location") } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt new file mode 100644 index 0000000000..55de9a305e --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.platform.LocalContext +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.location.api.Location +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.AppScope +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class AndroidLocationActions @Inject constructor( + private val coroutineDispatchers: CoroutineDispatchers +) : LocationActions { + + private var activityContext: Context? = null + + @Composable + override fun Configure() { + val context = LocalContext.current + return DisposableEffect(Unit) { + activityContext = context + onDispose { + activityContext = null + } + } + } + + override suspend fun share(location: Location, label: String?) { + runCatching { + // Ref: https://developer.android.com/guide/components/intents-common#ViewMap + val suffix = if (label != null) "(${Uri.encode(label)})" else "" + val uri = Uri.parse("geo:0,0?q=${location.lat},${location.lon}$suffix") + val showMapsIntent = Intent(Intent.ACTION_VIEW).setData(uri) + val chooserIntent = Intent.createChooser(showMapsIntent, null) + withContext(coroutineDispatchers.main) { + activityContext!!.startActivity(chooserIntent) + } + }.onSuccess { + Timber.v("Open location succeed") + }.onFailure { + Timber.e(it, "Open location failed") + } + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt new file mode 100644 index 0000000000..6ab9058024 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.runtime.Composable +import io.element.android.features.location.api.Location + +interface LocationActions { + + @Composable + fun Configure() + + suspend fun share(location: Location, label: String?) + +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt new file mode 100644 index 0000000000..5d565f575b --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.location.api.ViewLocationEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class ViewLocationEntryPointImpl @Inject constructor() : ViewLocationEntryPoint { + override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ViewLocationEntryPoint.Inputs): Node { + return parentNode.createNode(buildContext, listOf(inputs)) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/Location.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt similarity index 72% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/Location.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt index 67acf1cb9c..21676a0e71 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/location/Location.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt @@ -14,13 +14,8 @@ * limitations under the License. */ -package io.element.android.features.location.impl.location +package io.element.android.features.location.impl.view -/** - * Represents a location sample emitted by the device's location subsystem. - */ -data class Location( - val lat: Double, - val lon: Double, - val accuracy: Float, -) +sealed interface ViewLocationEvents { + object Share : ViewLocationEvents +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt new file mode 100644 index 0000000000..ae6e42a3f6 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.lifecycle.subscribe +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import im.vector.app.features.analytics.plan.MobileScreen +import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.location.api.ViewLocationEntryPoint +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.RoomScope +import io.element.android.services.analytics.api.AnalyticsService + +@ContributesNode(RoomScope::class) +class ViewLocationNode @AssistedInject constructor( + presenterFactory: ViewLocationPresenter.Factory, + analyticsService: AnalyticsService, + @Assisted buildContext: BuildContext, + @Assisted plugins: List, +) : Node(buildContext, plugins = plugins) { + + init { + lifecycle.subscribe( + onResume = { + analyticsService.screen(MobileScreen(screenName = MobileScreen.ScreenName.LocationView)) + } + ) + } + + private val inputs: ViewLocationEntryPoint.Inputs = inputs() + private val presenter = presenterFactory.create(inputs.location, inputs.description) + + @Composable + override fun View(modifier: Modifier) { + ViewLocationView( + state = presenter.present(), + modifier = modifier, + onBackPressed = ::navigateUp + ) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt new file mode 100644 index 0000000000..64739f1d33 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.features.location.api.Location +import io.element.android.libraries.architecture.Presenter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class ViewLocationPresenter @AssistedInject constructor( + private val actions: LocationActions, + @Assisted private val location: Location, + @Assisted private val description: String? +) : Presenter { + + @AssistedFactory + interface Factory { + fun create(location: Location, description: String?): ViewLocationPresenter + } + + @Composable + override fun present(): ViewLocationState { + val coroutineScope = rememberCoroutineScope() + actions.Configure() + + return ViewLocationState( + location = location, + description = description + ) { + when (it) { + ViewLocationEvents.Share -> coroutineScope.share(location, description) + } + } + } + + private fun CoroutineScope.share(location: Location, label: String?) = launch { + actions.share(location, label) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt new file mode 100644 index 0000000000..db8387e71e --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import io.element.android.features.location.api.Location + +data class ViewLocationState( + val location: Location, + val description: String?, + val eventSink: (ViewLocationEvents) -> Unit, +) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt new file mode 100644 index 0000000000..a4de1b039c --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.location.api.Location + +class ViewLocationStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + ViewLocationState( + Location(1.23, 2.34, 4f), + description = null, + eventSink = {}, + ), + ViewLocationState( + Location(1.23, 2.34, 4f), + description = "My favourite place!", + eventSink = {}, + ), + ViewLocationState( + Location(1.23, 2.34, 4f), + description = "For some reason I decided to write a small essay in the location description. " + + "It is so long that it will wrap onto more than two lines!", + eventSink = {}, + ), + ) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt new file mode 100644 index 0000000000..897d38eaf6 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Share +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.features.location.impl.map.MapState +import io.element.android.features.location.impl.map.MapView +import io.element.android.features.location.impl.map.rememberMapState +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.compound.generated.TypographyTokens +import io.element.android.libraries.ui.strings.CommonStrings + +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) +@Composable +fun ViewLocationView( + state: ViewLocationState, + modifier: Modifier = Modifier, + onBackPressed: () -> Unit = {}, +) { + val mapState = rememberMapState( + location = state.location, + position = MapState.CameraPosition(state.location.lat, state.location.lon, 15.0), + ) + + Scaffold(modifier, + topBar = { + CenterAlignedTopAppBar( + title = { + Text( + text = stringResource(CommonStrings.screen_view_location_title), + style = TypographyTokens.fontBodyLgMedium, + ) + }, + navigationIcon = { + BackButton(onClick = onBackPressed) + }, + actions = { + IconButton(onClick = { state.eventSink(ViewLocationEvents.Share) }) { + Icon(imageVector = Icons.Outlined.Share, contentDescription = stringResource(CommonStrings.action_share)) + } + } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .consumeWindowInsets(paddingValues) + .fillMaxSize(), + ) { + state.description?.let { + Text( + text = it, + textAlign = TextAlign.Center, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + style = TypographyTokens.fontBodyMdRegular, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + ) + } + + MapView( + mapState = mapState, + modifier = Modifier.fillMaxSize(), + ) + } + } +} + +@Preview +@Composable +internal fun ViewLocationViewLightPreview(@PreviewParameter(ViewLocationStateProvider::class) state: ViewLocationState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun ViewLocationViewDarkPreview(@PreviewParameter(ViewLocationStateProvider::class) state: ViewLocationState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: ViewLocationState) { + ViewLocationView( + state = state, + onBackPressed = {}, + ) +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowFake.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowFake.kt index ca88468c0d..861657a7e7 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowFake.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/location/LocationUpdatesFlowFake.kt @@ -16,18 +16,19 @@ package io.element.android.features.location.impl.location +import io.element.android.features.location.api.Location import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -fun fakeLocationUpdatesFlow(): Flow = flow { +fun fakeLocationUpdatesFlow(): Flow = flow { while (true) { delay(1_000) emit(aLocation()) } } -private fun aLocation() = io.element.android.features.location.impl.location.Location( +private fun aLocation() = Location( lat = 51.49404, lon = -0.25484, accuracy = 5f diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt new file mode 100644 index 0000000000..03fdf5222a --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import androidx.compose.runtime.Composable +import io.element.android.features.location.api.Location + +class FakeLocationActions : LocationActions { + + var configured = false + private set + + var sharedLocation: Location? = null + private set + + var sharedLabel: String? = null + private set + + @Composable + override fun Configure() { + configured = true + } + + override suspend fun share(location: Location, label: String?) { + sharedLocation = location + sharedLabel = label + } +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt new file mode 100644 index 0000000000..44dac70be8 --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.view + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth +import io.element.android.features.location.api.Location +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class ViewLocationPresenterTest { + + private val actions = FakeLocationActions() + private val location = Location(1.23, 4.56, 7.8f) + + @Test + fun `emits initial state`() = runTest { + val presenter = ViewLocationPresenter( + actions, + location, + A_DESCRIPTION, + ) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + Truth.assertThat(initialState.location).isEqualTo(location) + Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION) + } + } + + @Test + fun `uses action to share location`() = runTest { + val presenter = ViewLocationPresenter( + actions, + location, + A_DESCRIPTION, + ) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(ViewLocationEvents.Share) + + Truth.assertThat(actions.configured).isTrue() + Truth.assertThat(actions.sharedLocation).isEqualTo(location) + Truth.assertThat(actions.sharedLabel).isEqualTo(A_DESCRIPTION) + } + } + + companion object { + private const val A_DESCRIPTION = "My happy place" + } + +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 35a34638e0..dffffd0329 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -29,7 +29,9 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.location.api.Location import io.element.android.features.location.api.SendLocationEntryPoint +import io.element.android.features.location.api.ViewLocationEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode @@ -41,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.debug.EventDebugInfoNo import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -59,6 +62,7 @@ class MessagesFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val sendLocationEntryPoint: SendLocationEntryPoint, + private val viewLocationEntryPoint: ViewLocationEntryPoint, ) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Messages, @@ -82,6 +86,9 @@ class MessagesFlowNode @AssistedInject constructor( @Parcelize data class AttachmentPreview(val attachment: Attachment) : NavTarget + @Parcelize + data class LocationViewer(val location: Location, val description: String?) : NavTarget + @Parcelize data class EventDebugInfo(val eventId: EventId, val debugInfo: TimelineItemDebugInfo) : NavTarget @@ -147,6 +154,10 @@ class MessagesFlowNode @AssistedInject constructor( val inputs = AttachmentsPreviewNode.Inputs(navTarget.attachment) createNode(buildContext, listOf(inputs)) } + is NavTarget.LocationViewer -> { + val inputs = ViewLocationEntryPoint.Inputs(navTarget.location, navTarget.description) + viewLocationEntryPoint.createNode(this, buildContext, inputs) + } is NavTarget.EventDebugInfo -> { val inputs = EventDebugInfoNode.Inputs(navTarget.eventId, navTarget.debugInfo) createNode(buildContext, listOf(inputs)) @@ -213,6 +224,13 @@ class MessagesFlowNode @AssistedInject constructor( ) backstack.push(navTarget) } + is TimelineItemLocationContent -> { + val navTarget = NavTarget.LocationViewer( + location = event.content.location, + description = event.content.description, + ) + backstack.push(navTarget) + } else -> Unit } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 6d70d8318b..64b5cd697e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.impl.timeline.factories.event -import io.element.android.features.location.api.parseGeoUri +import io.element.android.features.location.api.Location import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent @@ -68,7 +68,7 @@ class TimelineItemContentMessageFactory @Inject constructor( ) } is LocationMessageType -> { - val location = parseGeoUri(messageType.geoUri) + val location = Location.fromGeoUri(messageType.geoUri) if (location == null) { TimelineItemTextContent( body = messageType.body, From 514bab48e8c8e79adff7473b8050d7ab03adaea1 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 4 Jul 2023 15:28:43 +0000 Subject: [PATCH 2/4] Update screenshots --- ...DefaultGroup_MapViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...efaultGroup_MapViewLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...p_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...p_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...p_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ..._ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ..._ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ..._ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...p_SendLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._SendLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_SheetContentDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 2 +- ...roup_SheetContentLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...DefaultGroup_PinIconDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...efaultGroup_PinIconLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...up_TextComposerReplyDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...p_TextComposerReplyLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- 16 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewDarkPreview_0_null,NEXUS_5,1.0,en].png index 56501f54c9..fe31f40122 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74409b405f143793ac5641bb16f66731d7fa96d513a85e5e38c368b295b297c7 -size 4965 +oid sha256:dcd8ccab99efd822f085614edb7296e5d73f3c1ae8d84ec0ccf930ead56bfa13 +size 7803 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewLightPreview_0_null,NEXUS_5,1.0,en].png index 665c8811ac..7200bc82d1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.map_null_DefaultGroup_MapViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b -size 4457 +oid sha256:e6040743cf442f6e3069eb77f562d6803eb9243edc959c0db7b5631ad00c583b +size 7103 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d639c16ee7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1f40593f1075d245f98547fea35ab6861858cb02568efd1e9c6d1f1e07d85a9 +size 10193 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0b0fe1c24d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f571102bed06886257392347b27a935d3d34187de1004311395ee6f849e44a2 +size 13504 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e3e6a8641e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e13f80abff301cf0d345434a2010c7aa9e84f65514842712b1c8be64fde81160 +size 23003 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..76abe2b489 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:779f80e6cc4cb9ca9c3dd113cfaaeb528244b48d3b915f262ae5c797ad0b6524 +size 10101 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..893997718f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57852301ef65b259a7b3a28943918fc37261c3d183d5b543e246ef9fd07b542f +size 13937 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d713d3675a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f1d57e7cbabd106d236a9afb49cfbaf5546c1f2d6b490d11a5d313d101632b4 +size 24994 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 3f52a57bc3..19a09eb962 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46073600d49a438279e3fb891573a88eb17dd7c74f853078ce7d33dadc81c298 -size 14260 +oid sha256:fd4a7d61010660b1c9081c48e562b219fd454e6ef23088bf0a6a1529d5fa67f6 +size 17379 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 24dd806db1..fbffd41d56 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl_null_DefaultGroup_SendLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bd98280b4cfbd97bfebe731a772874cef0a50ceea363d0d365318d38d164774 -size 16249 +oid sha256:7204bf50f6f7352160f9f45dbbecebbb2186b0d2e59d51e4d5237b536f345867 +size 18062 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 17dc639971..a237f65c56 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32c0ace0df1b31d9e47aaa4fcfc4fa7eb0d7e7d9cde321bc3e13ffd85e991f22 +oid sha256:a90e03f54c92c91388b3218942e633ced908fd3b77defbeefc492d5c97de97f5 size 39371 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_6,NEXUS_5,1.0,en].png index 096e9df6c6..07cd2cc9d3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c3a0506c870eb5f940821e5f05357f098f18fa177b143828e3e2afad09732dc -size 40704 +oid sha256:884654e20bd77864e1f7b7f137a539f8111ea50b8dd9c17ab689e246c70b2ead +size 40705 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconDarkPreview_0_null,NEXUS_5,1.0,en].png index c1c554dd7c..830a75b9e8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdca76cdbf676d1b5dfd4bdf3871913a3fca0a9efafbf962e461a45278c9df13 -size 5426 +oid sha256:c81885a3cffdb92e8282bfd511ac4efff4b02b1de2d6a37c01f9e41b94841e25 +size 5429 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconLightPreview_0_null,NEXUS_5,1.0,en].png index a522d00657..cf640a5e80 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_PinIconLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73750e1428e85548a8a8add1193d3c32709d79ffdc6c18125e3fbbc13d078c73 -size 5683 +oid sha256:d08207b517c75145a791e0d1872bb65eb24cfd8c5c9364fb620e392ed9025c51 +size 5678 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyDarkPreview_0_null,NEXUS_5,1.0,en].png index 7c695ff8d9..5bf2f75d49 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:870b7a0002ec6fd77bfe071a64b7c5faf98062227800ca55dd1c21f517025a48 -size 77684 +oid sha256:7d83815b1834a70e52fd55ab3558cb3f705abeba74474f4b23f6aafbb028a5ff +size 77687 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyLightPreview_0_null,NEXUS_5,1.0,en].png index 63b43390cd..0c23b6b043 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.textcomposer_null_DefaultGroup_TextComposerReplyLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:582d2f55a8f6707f1e4a55b4eadb7b19f18f3f78a097300151753c66e737e8e2 -size 80326 +oid sha256:116fb7199e2577efa0d1b3de60f70054697671d554bfb2f695fb229511c03c2f +size 80331 From 87853b467f18b369282caafa9f3fdfef77fc369e Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 5 Jul 2023 10:19:07 +0100 Subject: [PATCH 3/4] s/view/show/ --- ...onEntryPoint.kt => ShowLocationEntryPoint.kt} | 2 +- .../{view => show}/AndroidLocationActions.kt | 2 +- .../impl/{view => show}/LocationActions.kt | 2 +- .../ShowLocationEntryPointImpl.kt} | 10 +++++----- .../ShowLocationEvents.kt} | 6 +++--- .../ShowLocationNode.kt} | 12 ++++++------ .../ShowLocationPresenter.kt} | 14 +++++++------- .../ShowLocationState.kt} | 6 +++--- .../ShowLocationStateProvider.kt} | 12 ++++++------ .../ShowLocationView.kt} | 16 ++++++++-------- .../impl/{view => show}/FakeLocationActions.kt | 2 +- .../ShowLocationPresenterTest.kt} | 10 +++++----- .../features/messages/impl/MessagesFlowNode.kt | 8 ++++---- ...ViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png} | 0 ...ViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png} | 0 ...ViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png} | 0 ...iewLightPreview_0_null_0,NEXUS_5,1.0,en].png} | 0 ...iewLightPreview_0_null_1,NEXUS_5,1.0,en].png} | 0 ...iewLightPreview_0_null_2,NEXUS_5,1.0,en].png} | 0 19 files changed, 51 insertions(+), 51 deletions(-) rename features/location/api/src/main/kotlin/io/element/android/features/location/api/{ViewLocationEntryPoint.kt => ShowLocationEntryPoint.kt} (95%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view => show}/AndroidLocationActions.kt (97%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view => show}/LocationActions.kt (93%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationEntryPointImpl.kt => show/ShowLocationEntryPointImpl.kt} (77%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationEvents.kt => show/ShowLocationEvents.kt} (82%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationNode.kt => show/ShowLocationNode.kt} (85%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationPresenter.kt => show/ShowLocationPresenter.kt} (80%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationState.kt => show/ShowLocationState.kt} (84%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationStateProvider.kt => show/ShowLocationStateProvider.kt} (83%) rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/{view/ViewLocationView.kt => show/ShowLocationView.kt} (90%) rename features/location/impl/src/test/kotlin/io/element/android/features/location/impl/{view => show}/FakeLocationActions.kt (95%) rename features/location/impl/src/test/kotlin/io/element/android/features/location/impl/{view/ViewLocationPresenterTest.kt => show/ShowLocationPresenterTest.kt} (89%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png} (100%) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt similarity index 95% rename from features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt rename to features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt index a5fb396775..3c429dfa63 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/ViewLocationEntryPoint.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt @@ -21,7 +21,7 @@ import com.bumble.appyx.core.node.Node import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs -interface ViewLocationEntryPoint : FeatureEntryPoint { +interface ShowLocationEntryPoint : FeatureEntryPoint { data class Inputs(val location: Location, val description: String?) : NodeInputs diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt similarity index 97% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt index 55de9a305e..b347e2a8a3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/AndroidLocationActions.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import android.content.Context import android.content.Intent diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt similarity index 93% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt index 6ab9058024..c9e982f15d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/LocationActions.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.runtime.Composable import io.element.android.features.location.api.Location diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEntryPointImpl.kt similarity index 77% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEntryPointImpl.kt index 5d565f575b..7dc1fc02f3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEntryPointImpl.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEntryPointImpl.kt @@ -14,19 +14,19 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.location.api.ViewLocationEntryPoint +import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope import javax.inject.Inject @ContributesBinding(AppScope::class) -class ViewLocationEntryPointImpl @Inject constructor() : ViewLocationEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ViewLocationEntryPoint.Inputs): Node { - return parentNode.createNode(buildContext, listOf(inputs)) +class ShowLocationEntryPointImpl @Inject constructor() : ShowLocationEntryPoint { + override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ShowLocationEntryPoint.Inputs): Node { + return parentNode.createNode(buildContext, listOf(inputs)) } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvents.kt similarity index 82% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvents.kt index 21676a0e71..8d5b1143fb 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationEvents.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvents.kt @@ -14,8 +14,8 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show -sealed interface ViewLocationEvents { - object Share : ViewLocationEvents +sealed interface ShowLocationEvents { + object Share : ShowLocationEvents } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt similarity index 85% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt index ae6e42a3f6..24094b03ca 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationNode.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -26,14 +26,14 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode -import io.element.android.features.location.api.ViewLocationEntryPoint +import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) -class ViewLocationNode @AssistedInject constructor( - presenterFactory: ViewLocationPresenter.Factory, +class ShowLocationNode @AssistedInject constructor( + presenterFactory: ShowLocationPresenter.Factory, analyticsService: AnalyticsService, @Assisted buildContext: BuildContext, @Assisted plugins: List, @@ -47,12 +47,12 @@ class ViewLocationNode @AssistedInject constructor( ) } - private val inputs: ViewLocationEntryPoint.Inputs = inputs() + private val inputs: ShowLocationEntryPoint.Inputs = inputs() private val presenter = presenterFactory.create(inputs.location, inputs.description) @Composable override fun View(modifier: Modifier) { - ViewLocationView( + ShowLocationView( state = presenter.present(), modifier = modifier, onBackPressed = ::navigateUp diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt similarity index 80% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 64739f1d33..6bcd9d45db 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope @@ -26,28 +26,28 @@ import io.element.android.libraries.architecture.Presenter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -class ViewLocationPresenter @AssistedInject constructor( +class ShowLocationPresenter @AssistedInject constructor( private val actions: LocationActions, @Assisted private val location: Location, @Assisted private val description: String? -) : Presenter { +) : Presenter { @AssistedFactory interface Factory { - fun create(location: Location, description: String?): ViewLocationPresenter + fun create(location: Location, description: String?): ShowLocationPresenter } @Composable - override fun present(): ViewLocationState { + override fun present(): ShowLocationState { val coroutineScope = rememberCoroutineScope() actions.Configure() - return ViewLocationState( + return ShowLocationState( location = location, description = description ) { when (it) { - ViewLocationEvents.Share -> coroutineScope.share(location, description) + ShowLocationEvents.Share -> coroutineScope.share(location, description) } } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt similarity index 84% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index db8387e71e..c381acb347 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import io.element.android.features.location.api.Location -data class ViewLocationState( +data class ShowLocationState( val location: Location, val description: String?, - val eventSink: (ViewLocationEvents) -> Unit, + val eventSink: (ShowLocationEvents) -> Unit, ) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt similarity index 83% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index a4de1b039c..878cc47882 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -14,25 +14,25 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.api.Location -class ViewLocationStateProvider : PreviewParameterProvider { - override val values: Sequence +class ShowLocationStateProvider : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( - ViewLocationState( + ShowLocationState( Location(1.23, 2.34, 4f), description = null, eventSink = {}, ), - ViewLocationState( + ShowLocationState( Location(1.23, 2.34, 4f), description = "My favourite place!", eventSink = {}, ), - ViewLocationState( + ShowLocationState( Location(1.23, 2.34, 4f), description = "For some reason I decided to write a small essay in the location description. " + "It is so long that it will wrap onto more than two lines!", diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt similarity index 90% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 897d38eaf6..25aa7fbfad 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/view/ViewLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -49,8 +49,8 @@ import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable -fun ViewLocationView( - state: ViewLocationState, +fun ShowLocationView( + state: ShowLocationState, modifier: Modifier = Modifier, onBackPressed: () -> Unit = {}, ) { @@ -72,7 +72,7 @@ fun ViewLocationView( BackButton(onClick = onBackPressed) }, actions = { - IconButton(onClick = { state.eventSink(ViewLocationEvents.Share) }) { + IconButton(onClick = { state.eventSink(ShowLocationEvents.Share) }) { Icon(imageVector = Icons.Outlined.Share, contentDescription = stringResource(CommonStrings.action_share)) } } @@ -108,17 +108,17 @@ fun ViewLocationView( @Preview @Composable -internal fun ViewLocationViewLightPreview(@PreviewParameter(ViewLocationStateProvider::class) state: ViewLocationState) = +internal fun ShowLocationViewLightPreview(@PreviewParameter(ShowLocationStateProvider::class) state: ShowLocationState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -internal fun ViewLocationViewDarkPreview(@PreviewParameter(ViewLocationStateProvider::class) state: ViewLocationState) = +internal fun ShowLocationViewDarkPreview(@PreviewParameter(ShowLocationStateProvider::class) state: ShowLocationState) = ElementPreviewDark { ContentToPreview(state) } @Composable -private fun ContentToPreview(state: ViewLocationState) { - ViewLocationView( +private fun ContentToPreview(state: ShowLocationState) { + ShowLocationView( state = state, onBackPressed = {}, ) diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt similarity index 95% rename from features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt rename to features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt index 03fdf5222a..a1806746e8 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/FakeLocationActions.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import androidx.compose.runtime.Composable import io.element.android.features.location.api.Location diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt similarity index 89% rename from features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt rename to features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index 44dac70be8..b0448afcfe 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/view/ViewLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.location.impl.view +package io.element.android.features.location.impl.show import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow @@ -24,14 +24,14 @@ import io.element.android.features.location.api.Location import kotlinx.coroutines.test.runTest import org.junit.Test -class ViewLocationPresenterTest { +class ShowLocationPresenterTest { private val actions = FakeLocationActions() private val location = Location(1.23, 4.56, 7.8f) @Test fun `emits initial state`() = runTest { - val presenter = ViewLocationPresenter( + val presenter = ShowLocationPresenter( actions, location, A_DESCRIPTION, @@ -48,7 +48,7 @@ class ViewLocationPresenterTest { @Test fun `uses action to share location`() = runTest { - val presenter = ViewLocationPresenter( + val presenter = ShowLocationPresenter( actions, location, A_DESCRIPTION, @@ -58,7 +58,7 @@ class ViewLocationPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(ViewLocationEvents.Share) + initialState.eventSink(ShowLocationEvents.Share) Truth.assertThat(actions.configured).isTrue() Truth.assertThat(actions.sharedLocation).isEqualTo(location) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index dffffd0329..ef9db5fcec 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -31,7 +31,7 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.location.api.Location import io.element.android.features.location.api.SendLocationEntryPoint -import io.element.android.features.location.api.ViewLocationEntryPoint +import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode @@ -62,7 +62,7 @@ class MessagesFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val sendLocationEntryPoint: SendLocationEntryPoint, - private val viewLocationEntryPoint: ViewLocationEntryPoint, + private val showLocationEntryPoint: ShowLocationEntryPoint, ) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Messages, @@ -155,8 +155,8 @@ class MessagesFlowNode @AssistedInject constructor( createNode(buildContext, listOf(inputs)) } is NavTarget.LocationViewer -> { - val inputs = ViewLocationEntryPoint.Inputs(navTarget.location, navTarget.description) - viewLocationEntryPoint.createNode(this, buildContext, inputs) + val inputs = ShowLocationEntryPoint.Inputs(navTarget.location, navTarget.description) + showLocationEntryPoint.createNode(this, buildContext, inputs) } is NavTarget.EventDebugInfo -> { val inputs = EventDebugInfoNode.Inputs(navTarget.eventId, navTarget.debugInfo) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.view_null_DefaultGroup_ViewLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png From 0635fa9ee5821396e8b7a81bf791c1a025a23526 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 5 Jul 2023 10:52:44 +0100 Subject: [PATCH 4/4] Tidy and simplify using PR suggestions --- .../impl/show/AndroidLocationActions.kt | 46 ++++++------ .../location/impl/show/LocationActions.kt | 8 +-- .../impl/show/ShowLocationPresenter.kt | 11 +-- .../impl/show/AndroidLocationActionsTest.kt | 70 +++++++++++++++++++ .../location/impl/show/FakeLocationActions.kt | 11 +-- .../impl/show/ShowLocationPresenterTest.kt | 1 - 6 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/AndroidLocationActionsTest.kt diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt index b347e2a8a3..fb23f72557 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/AndroidLocationActions.kt @@ -19,45 +19,28 @@ package io.element.android.features.location.impl.show import android.content.Context import android.content.Intent import android.net.Uri -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.platform.LocalContext +import androidx.annotation.VisibleForTesting import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.location.api.Location -import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.AppScope -import kotlinx.coroutines.withContext +import io.element.android.libraries.di.ApplicationContext import timber.log.Timber import javax.inject.Inject @ContributesBinding(AppScope::class) class AndroidLocationActions @Inject constructor( - private val coroutineDispatchers: CoroutineDispatchers + @ApplicationContext private val appContext: Context ) : LocationActions { private var activityContext: Context? = null - @Composable - override fun Configure() { - val context = LocalContext.current - return DisposableEffect(Unit) { - activityContext = context - onDispose { - activityContext = null - } - } - } - - override suspend fun share(location: Location, label: String?) { + override fun share(location: Location, label: String?) { runCatching { - // Ref: https://developer.android.com/guide/components/intents-common#ViewMap - val suffix = if (label != null) "(${Uri.encode(label)})" else "" - val uri = Uri.parse("geo:0,0?q=${location.lat},${location.lon}$suffix") + val uri = Uri.parse(buildUrl(location, label)) val showMapsIntent = Intent(Intent.ACTION_VIEW).setData(uri) val chooserIntent = Intent.createChooser(showMapsIntent, null) - withContext(coroutineDispatchers.main) { - activityContext!!.startActivity(chooserIntent) - } + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + appContext.startActivity(chooserIntent) }.onSuccess { Timber.v("Open location succeed") }.onFailure { @@ -65,3 +48,18 @@ class AndroidLocationActions @Inject constructor( } } } + +@VisibleForTesting +internal fun buildUrl( + location: Location, + label: String?, + urlEncoder: (String) -> String = Uri::encode +): String { + // Ref: https://developer.android.com/guide/components/intents-common#ViewMap + val base = "geo:0,0?q=%.6f,%.6f".format(location.lat, location.lon) + return if (label == null) { + base + } else { + "%s (%s)".format(base, urlEncoder(label)) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt index c9e982f15d..7e38bd65fa 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/LocationActions.kt @@ -16,14 +16,8 @@ package io.element.android.features.location.impl.show -import androidx.compose.runtime.Composable import io.element.android.features.location.api.Location interface LocationActions { - - @Composable - fun Configure() - - suspend fun share(location: Location, label: String?) - + fun share(location: Location, label: String?) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 6bcd9d45db..42d57ecf7d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -23,8 +23,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.location.api.Location import io.element.android.libraries.architecture.Presenter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch class ShowLocationPresenter @AssistedInject constructor( private val actions: LocationActions, @@ -39,20 +37,13 @@ class ShowLocationPresenter @AssistedInject constructor( @Composable override fun present(): ShowLocationState { - val coroutineScope = rememberCoroutineScope() - actions.Configure() - return ShowLocationState( location = location, description = description ) { when (it) { - ShowLocationEvents.Share -> coroutineScope.share(location, description) + ShowLocationEvents.Share -> actions.share(location, description) } } } - - private fun CoroutineScope.share(location: Location, label: String?) = launch { - actions.share(location, label) - } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/AndroidLocationActionsTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/AndroidLocationActionsTest.kt new file mode 100644 index 0000000000..14dd983f34 --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/AndroidLocationActionsTest.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.impl.show + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.api.Location +import org.junit.Test +import java.net.URLEncoder + +internal class AndroidLocationActionsTest { + + // We use an Android-native encoder in the actual app, switch to an equivalent JVM one for the tests + private fun urlEncoder(input: String) = URLEncoder.encode(input, "US-ASCII") + + @Test + fun `buildUrl - truncates excessive decimals to 6dp`() { + val location = Location( + lat = 1.234567890123, + lon = 123.456789012345, + accuracy = 0f + ) + + val actual = buildUrl(location, null, ::urlEncoder) + val expected = "geo:0,0?q=1.234568,123.456789" + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `buildUrl - appends label if set`() { + val location = Location( + lat = 1.000001, + lon = 2.000001, + accuracy = 0f + ) + + val actual = buildUrl(location, "point", ::urlEncoder) + val expected = "geo:0,0?q=1.000001,2.000001 (point)" + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `buildUrl - URL encodes label`() { + val location = Location( + lat = 1.000001, + lon = 2.000001, + accuracy = 0f + ) + + val actual = buildUrl(location, "(weird/stuff here)", ::urlEncoder) + val expected = "geo:0,0?q=1.000001,2.000001 (%28weird%2Fstuff+here%29)" + + assertThat(actual).isEqualTo(expected) + } +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt index a1806746e8..411863f725 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/FakeLocationActions.kt @@ -16,26 +16,17 @@ package io.element.android.features.location.impl.show -import androidx.compose.runtime.Composable import io.element.android.features.location.api.Location class FakeLocationActions : LocationActions { - var configured = false - private set - var sharedLocation: Location? = null private set var sharedLabel: String? = null private set - @Composable - override fun Configure() { - configured = true - } - - override suspend fun share(location: Location, label: String?) { + override fun share(location: Location, label: String?) { sharedLocation = location sharedLabel = label } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index b0448afcfe..5ff323f463 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -60,7 +60,6 @@ class ShowLocationPresenterTest { val initialState = awaitItem() initialState.eventSink(ShowLocationEvents.Share) - Truth.assertThat(actions.configured).isTrue() Truth.assertThat(actions.sharedLocation).isEqualTo(location) Truth.assertThat(actions.sharedLabel).isEqualTo(A_DESCRIPTION) }