Check permission for background location (OS version <= Android 10 case)

This commit is contained in:
Maxime Naturel 2022-03-15 13:30:57 +01:00
parent 6f6bb3dbfe
commit d776f0c09c
8 changed files with 71 additions and 9 deletions

View File

@ -45,6 +45,7 @@
<!-- Location Sharing --> <!-- Location Sharing -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- Jitsi SDK is now API23+ --> <!-- Jitsi SDK is now API23+ -->
<uses-sdk tools:overrideLibrary="org.jitsi.meet.sdk,com.oney.WebRTCModule,com.learnium.RNDeviceInfo,com.reactnativecommunity.asyncstorage,com.ocetnik.timer,com.calendarevents,com.reactnativecommunity.netinfo,com.kevinresol.react_native_default_preference,com.rnimmersive,com.corbt.keepawake,com.BV.LinearGradient,com.horcrux.svg,com.oblador.performance,com.reactnativecommunity.slider,com.brentvatne.react" /> <uses-sdk tools:overrideLibrary="org.jitsi.meet.sdk,com.oney.WebRTCModule,com.learnium.RNDeviceInfo,com.reactnativecommunity.asyncstorage,com.ocetnik.timer,com.calendarevents,com.reactnativecommunity.netinfo,com.kevinresol.react_native_default_preference,com.rnimmersive,com.corbt.keepawake,com.BV.LinearGradient,com.horcrux.svg,com.oblador.performance,com.reactnativecommunity.slider,com.brentvatne.react" />

View File

@ -19,6 +19,7 @@ package im.vector.app.core.utils
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -32,6 +33,7 @@ import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
// Permissions sets // Permissions sets
val PERMISSIONS_EMPTY = emptyList<String>()
val PERMISSIONS_FOR_AUDIO_IP_CALL = listOf(Manifest.permission.RECORD_AUDIO) 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_VIDEO_IP_CALL = listOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
val PERMISSIONS_FOR_VOICE_MESSAGE = listOf(Manifest.permission.RECORD_AUDIO) 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_ROOM_AVATAR = listOf(Manifest.permission.CAMERA)
val PERMISSIONS_FOR_WRITING_FILES = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) val PERMISSIONS_FOR_WRITING_FILES = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
val PERMISSIONS_FOR_PICKING_CONTACT = listOf(Manifest.permission.READ_CONTACTS) 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_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) {
val PERMISSIONS_EMPTY = emptyList<String>() listOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
} else {
PERMISSIONS_EMPTY
}
// This is not ideal to store the value like that, but it works // This is not ideal to store the value like that, but it works
private var permissionDialogDisplayed = false private var permissionDialogDisplayed = false

View File

@ -37,7 +37,7 @@ import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.utils.PERMISSIONS_EMPTY 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_PICKING_CONTACT
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.databinding.ViewAttachmentTypeSelectorBinding import im.vector.app.databinding.ViewAttachmentTypeSelectorBinding
@ -215,6 +215,6 @@ class AttachmentTypeSelectorView(context: Context,
STICKER(PERMISSIONS_EMPTY, R.string.tooltip_attachment_sticker), STICKER(PERMISSIONS_EMPTY, R.string.tooltip_attachment_sticker),
CONTACT(PERMISSIONS_FOR_PICKING_CONTACT, R.string.tooltip_attachment_contact), CONTACT(PERMISSIONS_FOR_PICKING_CONTACT, R.string.tooltip_attachment_contact),
POLL(PERMISSIONS_EMPTY, R.string.tooltip_attachment_poll), 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)
} }
} }

View File

@ -17,6 +17,7 @@
package im.vector.app.features.location package im.vector.app.features.location
interface ILocationSharingNavigator { interface ILocationSharingNavigator {
var goingToAppSettings: Boolean
fun quit() fun quit()
fun goToAppSettings() fun goToAppSettings()
} }

View File

@ -23,4 +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()
object StartLiveLocationSharing : LocationSharingAction()
} }

View File

@ -31,6 +31,10 @@ import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
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_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.databinding.FragmentLocationSharingBinding
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
@ -79,8 +83,8 @@ class LocationSharingFragment @Inject constructor(
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
LocationSharingViewEvents.Close -> viewNavigator.quit() LocationSharingViewEvents.Close -> viewNavigator.quit()
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
is LocationSharingViewEvents.ZoomToUserLocation -> handleZoomToUserLocationEvent(it) is LocationSharingViewEvents.ZoomToUserLocation -> handleZoomToUserLocationEvent(it)
}.exhaustive }.exhaustive
} }
@ -89,6 +93,11 @@ class LocationSharingFragment @Inject constructor(
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
views.mapView.onResume() views.mapView.onResume()
if (viewNavigator.goingToAppSettings) {
viewNavigator.goingToAppSettings = false
// retry to start live location
tryStartLiveLocationSharing()
}
} }
override fun onPause() { override fun onPause() {
@ -179,10 +188,43 @@ class LocationSharingFragment @Inject constructor(
viewModel.handle(LocationSharingAction.CurrentUserLocationSharing) viewModel.handle(LocationSharingAction.CurrentUserLocationSharing)
} }
views.shareLocationOptionsPicker.optionUserLive.debouncedClicks { 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) { private fun updateMap(state: LocationSharingViewState) {
// first, update the options view // first, update the options view
val options: Set<LocationSharingOption> = when (state.areTargetAndUserLocationEqual) { val options: Set<LocationSharingOption> = when (state.areTargetAndUserLocationEqual) {

View File

@ -19,13 +19,18 @@ package im.vector.app.features.location
import android.app.Activity import android.app.Activity
import im.vector.app.core.utils.openAppSettingsPage 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() { override fun quit() {
activity?.finish() activity?.finish()
} }
override fun goToAppSettings() { override fun goToAppSettings() {
activity?.let { openAppSettingsPage(it) } activity?.let {
goingToAppSettings = true
openAppSettingsPage(it)
}
} }
} }

View File

@ -38,6 +38,7 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber
/** /**
* Sampling period to compare target location and user location. * Sampling period to compare target location and user location.
@ -120,6 +121,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()
LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction()
}.exhaustive }.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) { override fun onLocationUpdate(locationData: LocationData) {
setState { setState {
copy(lastKnownUserLocation = locationData) copy(lastKnownUserLocation = locationData)