diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt new file mode 100644 index 0000000000..3840e028c0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionChecker.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package im.vector.app.core.utils + +import android.content.Context +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import javax.inject.Inject + +class PermissionChecker @Inject constructor( + private val applicationContext: Context, +) { + fun checkPermission(vararg permissions: String): Boolean { + return permissions.any { permission -> + ActivityCompat.checkSelfPermission(applicationContext, permission) != PackageManager.PERMISSION_GRANTED + } + } +} 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 d9256e7685..82820e90cb 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 @@ -7,6 +7,7 @@ package im.vector.app.features.location +import android.Manifest import android.graphics.drawable.Drawable import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted @@ -15,6 +16,7 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.location.domain.usecase.CompareLocationsUseCase import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -48,6 +50,7 @@ class LocationSharingViewModel @AssistedInject constructor( private val session: Session, private val compareLocationsUseCase: CompareLocationsUseCase, private val vectorPreferences: VectorPreferences, + private val permissionChecker: PermissionChecker, ) : VectorViewModel(initialState), LocationTracker.Callback { private val room = session.getRoom(initialState.roomId)!! @@ -88,7 +91,15 @@ class LocationSharingViewModel @AssistedInject constructor( locationTracker.locations .onEach(::onLocationUpdate) .launchIn(viewModelScope) - locationTracker.start() + if (permissionChecker.checkPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + ) { + locationTracker.start() + } else { + Timber.w("Not allowed to use location api.") + } } private fun setUserItem() { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt index 70e909de27..1baebec617 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt @@ -17,6 +17,7 @@ import androidx.core.content.getSystemService import androidx.core.location.LocationListenerCompat import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.resources.BuildMeta +import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -37,6 +38,7 @@ class LocationTracker @Inject constructor( context: Context, private val activeSessionHolder: ActiveSessionHolder, private val buildMeta: BuildMeta, + private val permissionChecker: PermissionChecker, ) : LocationListenerCompat { private val locationManager = context.getSystemService() @@ -173,7 +175,15 @@ class LocationTracker @Inject constructor( fun removeCallback(callback: Callback) { callbacks.remove(callback) if (callbacks.size == 0) { - stop() + if (permissionChecker.checkPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + ) { + stop() + } else { + Timber.w("Not allowed to use location api.") + } } } diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewModel.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewModel.kt index 2fbd0b3d95..27ac31b785 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewModel.kt @@ -7,6 +7,7 @@ package im.vector.app.features.location.live.map +import android.Manifest import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationTracker import im.vector.app.features.location.live.StopLiveLocationShareUseCase @@ -23,6 +25,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult +import timber.log.Timber class LiveLocationMapViewModel @AssistedInject constructor( @Assisted private val initialState: LiveLocationMapViewState, @@ -31,6 +34,7 @@ class LiveLocationMapViewModel @AssistedInject constructor( private val locationSharingServiceConnection: LocationSharingServiceConnection, private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase, private val locationTracker: LocationTracker, + private val permissionChecker: PermissionChecker, ) : VectorViewModel(initialState), LocationSharingServiceConnection.Callback, @@ -123,7 +127,15 @@ class LiveLocationMapViewModel @AssistedInject constructor( copy(isLoadingUserLocation = true) } viewModelScope.launch(session.coroutineDispatchers.main) { - locationTracker.start() + if (permissionChecker.checkPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + ) { + locationTracker.start() + } else { + Timber.w("Not allowed to use location api.") + } locationTracker.requestLastKnownLocation() } } diff --git a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt index 4b4aa0c19d..76205f98cf 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt @@ -18,6 +18,7 @@ import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.core.services.VectorAndroidService +import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationTracker import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase @@ -55,6 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca @Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase @Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase + @Inject lateinit var permissionChecker: PermissionChecker private var binder: LocationSharingAndroidServiceBinder? = null @@ -77,7 +79,15 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca private fun initLocationTracking() { // Start tracking location locationTracker.addCallback(this) - locationTracker.start() + if (permissionChecker.checkPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + ) { + locationTracker.start() + } else { + Timber.w("Not allowed to use location api.") + } launchWithActiveSession { session -> val job = locationTracker.locations diff --git a/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewViewModel.kt index aacff1f745..ae091adaae 100644 --- a/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/preview/LocationPreviewViewModel.kt @@ -7,6 +7,7 @@ package im.vector.app.features.location.preview +import android.Manifest import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -14,6 +15,7 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.PermissionChecker import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationTracker @@ -23,12 +25,14 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem +import timber.log.Timber class LocationPreviewViewModel @AssistedInject constructor( @Assisted private val initialState: LocationPreviewViewState, private val session: Session, private val locationPinProvider: LocationPinProvider, private val locationTracker: LocationTracker, + private val permissionChecker: PermissionChecker, ) : VectorViewModel(initialState), LocationTracker.Callback { @AssistedFactory @@ -89,7 +93,15 @@ class LocationPreviewViewModel @AssistedInject constructor( copy(isLoadingUserLocation = true) } viewModelScope.launch(session.coroutineDispatchers.main) { - locationTracker.start() + if (permissionChecker.checkPermission( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + ) { + locationTracker.start() + } else { + Timber.w("Not allowed to use location api.") + } locationTracker.requestLastKnownLocation() } }