diff --git a/changelog.d/6012.wip b/changelog.d/6012.wip new file mode 100644 index 0000000000..9c67d562fe --- /dev/null +++ b/changelog.d/6012.wip @@ -0,0 +1 @@ +Live location sharing: navigation from timeline to map screen diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 20b7c4908a..fc78ce90a3 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -343,6 +343,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt index dfbd2eba97..61c4fe2174 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt @@ -36,9 +36,10 @@ fun Fragment.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): fun Fragment.addFragment( frameId: Int, fragment: Fragment, + tag: String? = null, allowStateLoss: Boolean = false ) { - parentFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) } + parentFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment, tag) } } fun Fragment.addFragment( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 635b00c05d..736722946f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -221,6 +221,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageFormat import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent @@ -647,6 +648,13 @@ class TimelineFragment @Inject constructor( ) } + private fun navigateToLocationLiveMap() { + navigator.openLocationLiveMap( + context = requireContext(), + roomId = timelineArgs.roomId + ) + } + private fun handleChangeLocationIndicator(event: RoomDetailViewEvents.ChangeLocationIndicator) { views.locationLiveStatusIndicator.isVisible = event.isVisible } @@ -2015,6 +2023,9 @@ class TimelineFragment @Inject constructor( is MessageLocationContent -> { handleShowLocationPreview(messageContent, informationData.senderId) } + is MessageBeaconInfoContent -> { + navigateToLocationLiveMap() + } else -> { val handled = onThreadSummaryClicked(informationData.eventId, isRootThreadEvent) if (!handled) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt index 479a742369..912702aaed 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt @@ -75,7 +75,8 @@ class LiveLocationShareMessageItemFactory @Inject constructor( val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP) return MessageLiveLocationInactiveItem_() - .attributes(attributes) + // disable the click on this state item + .attributes(attributes.copy(itemClickListener = null)) .mapWidth(width) .mapHeight(height) .highlighted(highlight) @@ -90,7 +91,8 @@ class LiveLocationShareMessageItemFactory @Inject constructor( val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP) return MessageLiveLocationStartItem_() - .attributes(attributes) + // disable the click on this state item + .attributes(attributes.copy(itemClickListener = null)) .mapWidth(width) .mapHeight(height) .highlighted(highlight) diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewActivity.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewActivity.kt new file mode 100644 index 0000000000..c0f07dba57 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewActivity.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 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 im.vector.app.features.location.live.map + +import android.content.Context +import android.content.Intent +import android.os.Parcelable +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.R +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivityLocationSharingBinding +import kotlinx.parcelize.Parcelize + +@Parcelize +data class LocationLiveMapViewArgs( + val roomId: String +) : Parcelable + +@AndroidEntryPoint +class LocationLiveMapViewActivity : VectorBaseActivity() { + + override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater) + + override fun initUiAndData() { + val mapViewArgs: LocationLiveMapViewArgs? = intent?.extras?.getParcelable(EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS) + if (mapViewArgs == null) { + finish() + return + } + setupToolbar(views.toolbar) + .setTitle(getString(R.string.location_activity_title_preview)) + .allowBack() + + if (isFirstCreation()) { + addFragment( + views.fragmentContainer, + LocationLiveMapViewFragment::class.java, + mapViewArgs + ) + } + } + + companion object { + + private const val EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS = "EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS" + + fun getIntent(context: Context, locationLiveMapViewArgs: LocationLiveMapViewArgs): Intent { + return Intent(context, LocationLiveMapViewActivity::class.java).apply { + putExtra(EXTRA_LOCATION_LIVE_MAP_VIEW_ARGS, locationLiveMapViewArgs) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt new file mode 100644 index 0000000000..32b87727d8 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 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 im.vector.app.features.location.live.map + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope +import com.airbnb.mvrx.args +import com.mapbox.mapboxsdk.maps.MapboxMapOptions +import com.mapbox.mapboxsdk.maps.SupportMapFragment +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.R +import im.vector.app.core.extensions.addChildFragment +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentSimpleContainerBinding +import im.vector.app.features.location.UrlMapProvider +import javax.inject.Inject + +/** + * Screen showing a map with all the current users sharing their live location in room. + */ +@AndroidEntryPoint +class LocationLiveMapViewFragment : VectorBaseFragment() { + + @Inject + lateinit var urlMapProvider: UrlMapProvider + + private val args: LocationLiveMapViewArgs by args() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSimpleContainerBinding { + return FragmentSimpleContainerBinding.inflate(layoutInflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupMap() + } + + private fun setupMap() { + val mapFragment = getOrCreateSupportMapFragment() + + mapFragment.getMapAsync { mapBoxMap -> + lifecycleScope.launchWhenCreated { + mapBoxMap.setStyle(urlMapProvider.getMapUrl()) + } + } + } + + private fun getOrCreateSupportMapFragment() = + childFragmentManager.findFragmentByTag(MAP_FRAGMENT_TAG) as? SupportMapFragment + ?: run { + val options = MapboxMapOptions.createFromAttributes(requireContext(), null) + SupportMapFragment.newInstance(options) + .also { addChildFragment(R.id.fragmentContainer, it, tag = MAP_FRAGMENT_TAG) } + } + + companion object { + private const val MAP_FRAGMENT_TAG = "im.vector.app.features.location.live.map" + } +} diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 0f921ab80a..a051266688 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -68,6 +68,8 @@ import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationSharingActivity import im.vector.app.features.location.LocationSharingArgs import im.vector.app.features.location.LocationSharingMode +import im.vector.app.features.location.live.map.LocationLiveMapViewActivity +import im.vector.app.features.location.live.map.LocationLiveMapViewArgs import im.vector.app.features.login.LoginActivity import im.vector.app.features.login.LoginConfig import im.vector.app.features.matrixto.MatrixToBottomSheet @@ -592,6 +594,14 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openLocationLiveMap(context: Context, roomId: String) { + val intent = LocationLiveMapViewActivity.getIntent( + context = context, + locationLiveMapViewArgs = LocationLiveMapViewArgs(roomId = roomId) + ) + context.startActivity(intent) + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 075b41daf3..d4ef2b8099 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -172,6 +172,8 @@ interface Navigator { initialLocationData: LocationData?, locationOwnerId: String?) + fun openLocationLiveMap(context: Context, roomId: String) + fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs, eventIdToNavigate: String? = null) fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs) diff --git a/vector/src/main/res/layout/fragment_simple_container.xml b/vector/src/main/res/layout/fragment_simple_container.xml new file mode 100644 index 0000000000..fb9e6d38c5 --- /dev/null +++ b/vector/src/main/res/layout/fragment_simple_container.xml @@ -0,0 +1,5 @@ + +