Merge pull request #5671 from vector-im/feature/mna/PSF-673-live-loc-share-duration

#5667: [Location Sharing] - Set duration of live sharing
This commit is contained in:
Maxime NATUREL 2022-04-05 16:13:26 +02:00 committed by GitHub
commit bebe819c54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 178 additions and 10 deletions

1
changelog.d/5667.feature Normal file
View file

@ -0,0 +1 @@
Location sharing: adding possibility to choose duration of live sharing

View file

@ -67,4 +67,6 @@
<dimen name="location_sharing_locate_button_margin_vertical">16dp</dimen> <dimen name="location_sharing_locate_button_margin_vertical">16dp</dimen>
<dimen name="location_sharing_locate_button_margin_horizontal">12dp</dimen> <dimen name="location_sharing_locate_button_margin_horizontal">12dp</dimen>
<dimen name="location_sharing_compass_button_margin_horizontal">8dp</dimen> <dimen name="location_sharing_compass_button_margin_horizontal">8dp</dimen>
<dimen name="location_sharing_live_duration_choice_margin_horizontal">12dp</dimen>
<dimen name="location_sharing_live_duration_choice_margin_vertical">22dp</dimen>
</resources> </resources>

View file

@ -23,5 +23,5 @@ sealed class LocationSharingAction : VectorViewModelAction {
data class PinnedLocationSharing(val locationData: LocationData?) : LocationSharingAction() data class PinnedLocationSharing(val locationData: LocationData?) : LocationSharingAction()
data class LocationTargetChange(val locationData: LocationData) : LocationSharingAction() data class LocationTargetChange(val locationData: LocationData) : LocationSharingAction()
object ZoomToUserLocation : LocationSharingAction() object ZoomToUserLocation : LocationSharingAction()
data class StartLiveLocationSharing(val duration: Long) : LocationSharingAction() data class StartLiveLocationSharing(val durationMillis: Long) : LocationSharingAction()
} }

View file

@ -30,6 +30,7 @@ import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_BACKGROUND_LOCATION_SHARING import im.vector.app.core.utils.PERMISSIONS_FOR_BACKGROUND_LOCATION_SHARING
import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING
@ -39,6 +40,7 @@ import im.vector.app.databinding.FragmentLocationSharingBinding
import im.vector.app.features.VectorFeatures import im.vector.app.features.VectorFeatures
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.location.live.duration.ChooseLiveDurationBottomSheet
import im.vector.app.features.location.option.LocationSharingOption import im.vector.app.features.location.option.LocationSharingOption
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@ -52,7 +54,9 @@ class LocationSharingFragment @Inject constructor(
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider, private val matrixItemColorProvider: MatrixItemColorProvider,
private val vectorFeatures: VectorFeatures, private val vectorFeatures: VectorFeatures,
) : VectorBaseFragment<FragmentLocationSharingBinding>(), LocationTargetChangeListener { ) : VectorBaseFragment<FragmentLocationSharingBinding>(),
LocationTargetChangeListener,
VectorBaseBottomSheetDialogFragment.ResultListener {
private val viewModel: LocationSharingViewModel by fragmentViewModel() private val viewModel: LocationSharingViewModel by fragmentViewModel()
@ -181,13 +185,15 @@ class LocationSharingFragment @Inject constructor(
} }
private fun handleStartLiveLocationService(event: LocationSharingViewEvents.StartLiveLocationService) { private fun handleStartLiveLocationService(event: LocationSharingViewEvents.StartLiveLocationService) {
val args = LocationSharingService.RoomArgs(event.sessionId, event.roomId, event.duration) val args = LocationSharingService.RoomArgs(event.sessionId, event.roomId, event.durationMillis)
Intent(requireContext(), LocationSharingService::class.java) Intent(requireContext(), LocationSharingService::class.java)
.putExtra(LocationSharingService.EXTRA_ROOM_ARGS, args) .putExtra(LocationSharingService.EXTRA_ROOM_ARGS, args)
.also { .also {
ContextCompat.startForegroundService(requireContext(), it) ContextCompat.startForegroundService(requireContext(), it)
} }
vectorBaseActivity.finish()
} }
private fun initOptionsPicker() { private fun initOptionsPicker() {
@ -235,9 +241,14 @@ class LocationSharingFragment @Inject constructor(
} }
private fun startLiveLocationSharing() { private fun startLiveLocationSharing() {
// TODO. Get duration from user ChooseLiveDurationBottomSheet.newInstance(this)
val duration = 30 * 1000L .show(requireActivity().supportFragmentManager, "DISPLAY_CHOOSE_DURATION_OPTIONS")
viewModel.handle(LocationSharingAction.StartLiveLocationSharing(duration)) }
override fun onBottomSheetResult(resultCode: Int, data: Any?) {
if (resultCode == VectorBaseBottomSheetDialogFragment.ResultListener.RESULT_OK) {
(data as? Long)?.let { viewModel.handle(LocationSharingAction.StartLiveLocationSharing(it)) }
}
} }
private fun updateMap(state: LocationSharingViewState) { private fun updateMap(state: LocationSharingViewState) {

View file

@ -22,5 +22,5 @@ sealed class LocationSharingViewEvents : VectorViewEvents {
object Close : LocationSharingViewEvents() object Close : LocationSharingViewEvents()
object LocationNotAvailableError : LocationSharingViewEvents() object LocationNotAvailableError : LocationSharingViewEvents()
data class ZoomToUserLocation(val userLocation: LocationData) : LocationSharingViewEvents() data class ZoomToUserLocation(val userLocation: LocationData) : LocationSharingViewEvents()
data class StartLiveLocationService(val sessionId: String, val roomId: String, val duration: Long) : LocationSharingViewEvents() data class StartLiveLocationService(val sessionId: String, val roomId: String, val durationMillis: Long) : LocationSharingViewEvents()
} }

View file

@ -120,7 +120,7 @@ class LocationSharingViewModel @AssistedInject constructor(
is LocationSharingAction.PinnedLocationSharing -> handlePinnedLocationSharingAction(action) is LocationSharingAction.PinnedLocationSharing -> handlePinnedLocationSharingAction(action)
is LocationSharingAction.LocationTargetChange -> handleLocationTargetChangeAction(action) is LocationSharingAction.LocationTargetChange -> handleLocationTargetChangeAction(action)
LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction() LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction()
is LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction(action.duration) is LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction(action.durationMillis)
} }
} }
@ -158,11 +158,11 @@ class LocationSharingViewModel @AssistedInject constructor(
} }
} }
private fun handleStartLiveLocationSharingAction(duration: Long) { private fun handleStartLiveLocationSharingAction(durationMillis: Long) {
_viewEvents.post(LocationSharingViewEvents.StartLiveLocationService( _viewEvents.post(LocationSharingViewEvents.StartLiveLocationService(
sessionId = session.sessionId, sessionId = session.sessionId,
roomId = room.roomId, roomId = room.roomId,
duration = duration durationMillis = durationMillis
)) ))
} }

View file

@ -0,0 +1,86 @@
/*
* Copyright 2019 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.duration
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment.ResultListener.Companion.RESULT_OK
import im.vector.app.databinding.BottomSheetChooseLiveLocationShareDurationBinding
/**
* 15 minutes.
*/
private const val DURATION_IN_MS_OPTION_1 = 15 * 60_000L
/**
* 1 hour.
*/
private const val DURATION_IN_MS_OPTION_2 = 60 * 60_000L
/**
* 8 hours.
*/
private const val DURATION_IN_MS_OPTION_3 = 8 * 60 * 60_000L
/**
* Bottom sheet displaying list of options to choose the duration of the location live sharing.
*/
@AndroidEntryPoint
class ChooseLiveDurationBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetChooseLiveLocationShareDurationBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetChooseLiveLocationShareDurationBinding {
return BottomSheetChooseLiveLocationShareDurationBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initConfirmButton()
}
// we are not using state for this one as it's static, so no need to override invalidate()
private fun initConfirmButton() {
views.liveLocShareChooseDurationConfirm.setOnClickListener {
val currentChoice = getCurrentChoice()
resultListener?.onBottomSheetResult(RESULT_OK, currentChoice)
dismiss()
}
}
private fun getCurrentChoice(): Long {
return when (views.liveLocShareChooseDurationOptions.checkedRadioButtonId) {
R.id.liveLocShareChooseDurationOption1 -> DURATION_IN_MS_OPTION_1
R.id.liveLocShareChooseDurationOption2 -> DURATION_IN_MS_OPTION_2
R.id.liveLocShareChooseDurationOption3 -> DURATION_IN_MS_OPTION_3
else -> DURATION_IN_MS_OPTION_1
}
}
companion object {
fun newInstance(resultListener: ResultListener): ChooseLiveDurationBottomSheet {
val bottomSheet = ChooseLiveDurationBottomSheet()
bottomSheet.resultListener = resultListener
return bottomSheet
}
}
}

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorSurface"
android:orientation="vertical">
<TextView
android:id="@+id/liveLocShareChooseDurationTitle"
style="@style/Widget.Vector.TextView.Subtitle.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingHorizontal="15dp"
android:paddingVertical="24dp"
android:text="@string/location_share_live_select_duration_title"
android:textColor="?vctr_content_primary" />
<RadioGroup
android:id="@+id/liveLocShareChooseDurationOptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@drawable/divider_horizontal"
android:showDividers="beginning">
<RadioButton
android:id="@+id/liveLocShareChooseDurationOption1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:checked="true"
android:paddingHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:paddingVertical="@dimen/location_sharing_live_duration_choice_margin_vertical"
android:text="@string/location_share_live_select_duration_option_1" />
<RadioButton
android:id="@+id/liveLocShareChooseDurationOption2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:paddingHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:paddingVertical="@dimen/location_sharing_live_duration_choice_margin_vertical"
android:text="@string/location_share_live_select_duration_option_2" />
<RadioButton
android:id="@+id/liveLocShareChooseDurationOption3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:paddingHorizontal="@dimen/location_sharing_live_duration_choice_margin_horizontal"
android:paddingVertical="@dimen/location_sharing_live_duration_choice_margin_vertical"
android:text="@string/location_share_live_select_duration_option_3" />
</RadioGroup>
<Button
android:id="@+id/liveLocShareChooseDurationConfirm"
style="@style/Widget.Vector.Button.CallToAction"
android:layout_width="match_parent"
android:layout_height="65dp"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:text="@string/action_share" />
</LinearLayout>

View file

@ -2961,6 +2961,10 @@
<string name="a11y_location_share_option_user_live_icon">Share live location</string> <string name="a11y_location_share_option_user_live_icon">Share live location</string>
<string name="location_share_option_pinned">Share this location</string> <string name="location_share_option_pinned">Share this location</string>
<string name="a11y_location_share_option_pinned_icon">Share this location</string> <string name="a11y_location_share_option_pinned_icon">Share this location</string>
<string name="location_share_live_select_duration_title">Share your live location for</string>
<string name="location_share_live_select_duration_option_1">15 minutes</string>
<string name="location_share_live_select_duration_option_2">1 hour</string>
<string name="location_share_live_select_duration_option_3">8 hours</string>
<string name="location_in_background_missing_permission_dialog_title">Allow access</string> <string name="location_in_background_missing_permission_dialog_title">Allow access</string>
<string name="location_in_background_missing_permission_dialog_content">If youd like to share your Live location, ${app_name} needs location access all the time when the app is in the background.\nWe will only access your location for the duration that you choose.</string> <string name="location_in_background_missing_permission_dialog_content">If youd like to share your Live location, ${app_name} needs location access all the time when the app is in the background.\nWe will only access your location for the duration that you choose.</string>
<string name="location_not_available_dialog_title">${app_name} could not access your location</string> <string name="location_not_available_dialog_title">${app_name} could not access your location</string>