From d776f0c09cf7d84a8f59cdf0c0c01f7157f486c8 Mon Sep 17 00:00:00 2001 From: Maxime Naturel Date: Tue, 15 Mar 2022 13:30:57 +0100 Subject: [PATCH] Check permission for background location (OS version <= Android 10 case) --- vector/src/main/AndroidManifest.xml | 1 + .../vector/app/core/utils/PermissionsTools.kt | 11 +++-- .../attachments/AttachmentTypeSelectorView.kt | 4 +- .../location/ILocationSharingNavigator.kt | 1 + .../location/LocationSharingAction.kt | 1 + .../location/LocationSharingFragment.kt | 46 ++++++++++++++++++- .../location/LocationSharingNavigator.kt | 9 +++- .../location/LocationSharingViewModel.kt | 7 +++ 8 files changed, 71 insertions(+), 9 deletions(-) diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 58b1bc177c..1d99fba91a 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -45,6 +45,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt index dabf11b9d3..d2e69a87e9 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt @@ -19,6 +19,7 @@ package im.vector.app.core.utils import android.Manifest import android.app.Activity import android.content.pm.PackageManager +import android.os.Build import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts @@ -32,6 +33,7 @@ import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity // Permissions sets +val PERMISSIONS_EMPTY = emptyList() val PERMISSIONS_FOR_AUDIO_IP_CALL = listOf(Manifest.permission.RECORD_AUDIO) val PERMISSIONS_FOR_VIDEO_IP_CALL = listOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) val PERMISSIONS_FOR_VOICE_MESSAGE = listOf(Manifest.permission.RECORD_AUDIO) @@ -40,9 +42,12 @@ val PERMISSIONS_FOR_MEMBERS_SEARCH = listOf(Manifest.permission.READ_CONTACTS) val PERMISSIONS_FOR_ROOM_AVATAR = listOf(Manifest.permission.CAMERA) val PERMISSIONS_FOR_WRITING_FILES = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) val PERMISSIONS_FOR_PICKING_CONTACT = listOf(Manifest.permission.READ_CONTACTS) -val PERMISSIONS_FOR_LOCATION_SHARING = listOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) - -val PERMISSIONS_EMPTY = emptyList() +val PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING = listOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) +val PERMISSIONS_FOR_BACKGROUND_LOCATION_SHARING = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + listOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION) +} else { + PERMISSIONS_EMPTY +} // This is not ideal to store the value like that, but it works private var permissionDialogDisplayed = false diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt index a15bd52174..7fcbb6bae6 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt @@ -37,7 +37,7 @@ import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.core.epoxy.onClick import im.vector.app.core.utils.PERMISSIONS_EMPTY -import im.vector.app.core.utils.PERMISSIONS_FOR_LOCATION_SHARING +import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.databinding.ViewAttachmentTypeSelectorBinding @@ -215,6 +215,6 @@ class AttachmentTypeSelectorView(context: Context, STICKER(PERMISSIONS_EMPTY, R.string.tooltip_attachment_sticker), CONTACT(PERMISSIONS_FOR_PICKING_CONTACT, R.string.tooltip_attachment_contact), POLL(PERMISSIONS_EMPTY, R.string.tooltip_attachment_poll), - LOCATION(PERMISSIONS_FOR_LOCATION_SHARING, R.string.tooltip_attachment_location) + LOCATION(PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING, R.string.tooltip_attachment_location) } } diff --git a/vector/src/main/java/im/vector/app/features/location/ILocationSharingNavigator.kt b/vector/src/main/java/im/vector/app/features/location/ILocationSharingNavigator.kt index f86f622712..91c62618dd 100644 --- a/vector/src/main/java/im/vector/app/features/location/ILocationSharingNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/location/ILocationSharingNavigator.kt @@ -17,6 +17,7 @@ package im.vector.app.features.location interface ILocationSharingNavigator { + var goingToAppSettings: Boolean fun quit() fun goToAppSettings() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt index ec47c23ea7..d7d686ee60 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -23,4 +23,5 @@ sealed class LocationSharingAction : VectorViewModelAction { data class PinnedLocationSharing(val locationData: LocationData?) : LocationSharingAction() data class LocationTargetChange(val locationData: LocationData) : LocationSharingAction() object ZoomToUserLocation : LocationSharingAction() + object StartLiveLocationSharing : LocationSharingAction() } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 7e7303c1f3..bbef06c11e 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -31,6 +31,10 @@ import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.extensions.exhaustive 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_FOREGROUND_LOCATION_SHARING +import im.vector.app.core.utils.checkPermissions +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.FragmentLocationSharingBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider @@ -79,8 +83,8 @@ class LocationSharingFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() LocationSharingViewEvents.Close -> viewNavigator.quit() + LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() is LocationSharingViewEvents.ZoomToUserLocation -> handleZoomToUserLocationEvent(it) }.exhaustive } @@ -89,6 +93,11 @@ class LocationSharingFragment @Inject constructor( override fun onResume() { super.onResume() views.mapView.onResume() + if (viewNavigator.goingToAppSettings) { + viewNavigator.goingToAppSettings = false + // retry to start live location + tryStartLiveLocationSharing() + } } override fun onPause() { @@ -179,10 +188,43 @@ class LocationSharingFragment @Inject constructor( viewModel.handle(LocationSharingAction.CurrentUserLocationSharing) } views.shareLocationOptionsPicker.optionUserLive.debouncedClicks { - // TODO + tryStartLiveLocationSharing() } } + private val foregroundLocationResultLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> + if (allGranted && checkPermissions(PERMISSIONS_FOR_BACKGROUND_LOCATION_SHARING, requireActivity(), backgroundLocationResultLauncher)) { + startLiveLocationSharing() + } else if (deniedPermanently) { + handleMissingBackgroundLocationPermission() + } + } + + private val backgroundLocationResultLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> + if (allGranted) { + startLiveLocationSharing() + } else if (deniedPermanently) { + handleMissingBackgroundLocationPermission() + } + } + + private fun tryStartLiveLocationSharing() { + // TODO handle Android 11+ case => cannot ask runtime permission for background location + // when ActivityCompat.shouldShowRequestPermissionRationale() returns true + // show dialog to redirect to app settings (handleMissingBackgroundLocationPermission()) + + // TODO test with Android 6, Android 10 and Android 11 + // we need to re-check foreground location to be sure it has not changed after landing on this screen + if (checkPermissions(PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING, requireActivity(), foregroundLocationResultLauncher) && + checkPermissions(PERMISSIONS_FOR_BACKGROUND_LOCATION_SHARING, requireActivity(), backgroundLocationResultLauncher)) { + startLiveLocationSharing() + } + } + + private fun startLiveLocationSharing() { + viewModel.handle(LocationSharingAction.StartLiveLocationSharing) + } + private fun updateMap(state: LocationSharingViewState) { // first, update the options view val options: Set = when (state.areTargetAndUserLocationEqual) { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingNavigator.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingNavigator.kt index c474cd6b58..b4412710e3 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingNavigator.kt @@ -19,13 +19,18 @@ package im.vector.app.features.location import android.app.Activity import im.vector.app.core.utils.openAppSettingsPage -class LocationSharingNavigator constructor(val activity: Activity?): ILocationSharingNavigator { +class LocationSharingNavigator constructor(val activity: Activity?) : ILocationSharingNavigator { + + override var goingToAppSettings: Boolean = false override fun quit() { activity?.finish() } override fun goToAppSettings() { - activity?.let { openAppSettingsPage(it) } + activity?.let { + goingToAppSettings = true + openAppSettingsPage(it) + } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 25bc482412..639666e63f 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem +import timber.log.Timber /** * Sampling period to compare target location and user location. @@ -120,6 +121,7 @@ class LocationSharingViewModel @AssistedInject constructor( is LocationSharingAction.PinnedLocationSharing -> handlePinnedLocationSharingAction(action) is LocationSharingAction.LocationTargetChange -> handleLocationTargetChangeAction(action) LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction() + LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction() }.exhaustive } @@ -157,6 +159,11 @@ class LocationSharingViewModel @AssistedInject constructor( } } + private fun handleStartLiveLocationSharingAction() { + // TODO start sharing live location and update view state + Timber.d("live location sharing started") + } + override fun onLocationUpdate(locationData: LocationData) { setState { copy(lastKnownUserLocation = locationData)