From 04df53f4b898f3d2a106c5c5f41a0005945f2212 Mon Sep 17 00:00:00 2001 From: Andrew Rowson Date: Mon, 29 Jul 2024 13:08:49 +0100 Subject: [PATCH] feat(permission): be more explicit about asking for background location permission Hopefully fixes #1759 --- CHANGELOG.md | 4 + .../owntracks/android/testutils/Helpers.kt | 10 +- .../android/ui/WaypointsActivityTests.kt | 5 +- .../android/ui/WelcomeActivityTests.kt | 99 ++++++++++++++++++- .../android/preferences/DefaultsProvider.kt | 1 + .../android/preferences/Preferences.kt | 3 + .../android/services/BackgroundService.kt | 12 +-- .../android/support/OSSRequirementsChecker.kt | 9 ++ .../android/support/RequirementsChecker.kt | 2 + .../owntracks/android/ui/map/MapActivity.kt | 33 ++++++- .../BackgroundLocationPermissionRequester.kt | 53 ++++++++++ .../ui/mixins/LocationPermissionRequester.kt | 2 +- .../ui/preferences/AdvancedFragment.kt | 11 +-- .../fragments/LocationPermissionFragment.kt | 51 ++++++---- .../drawable/baseline_share_location_24.xml | 15 +++ .../layout/ui_welcome_location_permission.xml | 11 ++- .../ui_welcome_notification_permission.xml | 2 +- .../src/main/res/values-en-rGB/strings.xml | 2 +- project/app/src/main/res/values/strings.xml | 10 +- 19 files changed, 288 insertions(+), 47 deletions(-) create mode 100644 project/app/src/main/java/org/owntracks/android/ui/mixins/BackgroundLocationPermissionRequester.kt create mode 100644 project/app/src/main/res/drawable/baseline_share_location_24.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b297d7e4e..b6789670dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Version 2.5.1 +### New features + +- The background location permission is explicitly asked for in the welcome activity. It's also prompted if missing (but foreground location permissions are present) in the map activity, to catch people upgrading from <2.5.0. + ### Bug fixes - Re-added `tst` from Lwt MQTT message type that was accidentally dropped in 2.5.0 (#1766) diff --git a/project/app/src/androidTest/java/org/owntracks/android/testutils/Helpers.kt b/project/app/src/androidTest/java/org/owntracks/android/testutils/Helpers.kt index 6dcb5f7071..a2c06981e6 100644 --- a/project/app/src/androidTest/java/org/owntracks/android/testutils/Helpers.kt +++ b/project/app/src/androidTest/java/org/owntracks/android/testutils/Helpers.kt @@ -31,6 +31,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry import androidx.test.runner.lifecycle.Stage +import com.adevinta.android.barista.interaction.BaristaDialogInteractions.clickDialogNegativeButton import com.adevinta.android.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawer import com.adevinta.android.barista.interaction.BaristaEditTextInteractions @@ -198,14 +199,19 @@ fun enableDeviceLocation() { } } -/** Who knows what order these will appear in. */ -fun grantMapActivityPermissions() { +fun grantNotificationAndForegroundPermissions() { PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.POST_NOTIFICATIONS) PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION) PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.POST_NOTIFICATIONS) PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION) } +/** Who knows what order these will appear in. */ +fun grantMapActivityPermissions() { + grantNotificationAndForegroundPermissions() + clickDialogNegativeButton() +} + /** * Write file to device * diff --git a/project/app/src/androidTest/java/org/owntracks/android/ui/WaypointsActivityTests.kt b/project/app/src/androidTest/java/org/owntracks/android/ui/WaypointsActivityTests.kt index 8c16f94c8d..a8e25190dd 100644 --- a/project/app/src/androidTest/java/org/owntracks/android/ui/WaypointsActivityTests.kt +++ b/project/app/src/androidTest/java/org/owntracks/android/ui/WaypointsActivityTests.kt @@ -14,7 +14,6 @@ import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawer import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo import com.fasterxml.jackson.databind.ObjectMapper -import org.hamcrest.Matchers.not import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before @@ -25,7 +24,7 @@ import org.owntracks.android.testutils.TestWithAnActivity import org.owntracks.android.testutils.TestWithAnMQTTBroker import org.owntracks.android.testutils.TestWithAnMQTTBrokerImpl import org.owntracks.android.testutils.getText -import org.owntracks.android.testutils.grantMapActivityPermissions +import org.owntracks.android.testutils.grantNotificationAndForegroundPermissions import org.owntracks.android.ui.waypoints.WaypointsActivity @OptIn(ExperimentalUnsignedTypes::class) @@ -36,7 +35,7 @@ class WaypointsActivityTests : TestWithAnMQTTBroker by TestWithAnMQTTBrokerImpl() { @Before fun grantPermissions() { - grantMapActivityPermissions() + grantNotificationAndForegroundPermissions() } @Test diff --git a/project/app/src/androidTest/java/org/owntracks/android/ui/WelcomeActivityTests.kt b/project/app/src/androidTest/java/org/owntracks/android/ui/WelcomeActivityTests.kt index 02eff8144e..9f3abfdab5 100644 --- a/project/app/src/androidTest/java/org/owntracks/android/ui/WelcomeActivityTests.kt +++ b/project/app/src/androidTest/java/org/owntracks/android/ui/WelcomeActivityTests.kt @@ -3,20 +3,112 @@ package org.owntracks.android.ui import android.Manifest import androidx.test.espresso.Espresso.pressBack import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest import androidx.test.filters.SdkSuppress import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed +import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertNotDisplayed import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.PermissionGranter.allowPermissionsIfNeeded +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.owntracks.android.R import org.owntracks.android.testutils.TestWithAnActivity import org.owntracks.android.testutils.doIfViewNotVisible +import org.owntracks.android.testutils.getCurrentActivity +import org.owntracks.android.ui.map.MapActivity import org.owntracks.android.ui.welcome.WelcomeActivity @RunWith(AndroidJUnit4::class) +@MediumTest class WelcomeActivityTests : TestWithAnActivity(WelcomeActivity::class.java) { + @Test + fun welcomeActivityStartsWithIntroFragment() { + assertDisplayed(R.string.welcome_heading) + assertDisplayed(R.string.welcome_description) + assertDisplayed(R.id.btn_next) + } + + @Test + fun welcomeActivityShowsConnectionSetupDetails() { + // Intro fragment + clickOn(R.id.btn_next) + // Connection setup fragment + assertDisplayed(R.string.welcome_connection_setup_title) + assertDisplayed(R.string.welcome_connection_setup_description) + assertDisplayed(R.id.btn_next) + } + + @SdkSuppress(minSdkVersion = 34) + @Test + fun welcomeActivityStartsTheMapActivityWhenDone() { + // Intro fragment + clickOn(R.id.btn_next) + // Connection setup fragment + clickOn(R.id.btn_next) + // Location Permission fragment + doIfViewNotVisible(R.id.btn_next) { + R.id.ui_fragment_welcome_location_permissions_request.run { + assertDisplayed(this) + clickOn(this) + } + allowPermissionsIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION) + } + clickOn(R.id.btn_next) + // Notification permission fragment + doIfViewNotVisible(R.id.btn_next) { + R.id.ui_fragment_welcome_notification_permissions_request.run { + assertDisplayed(this) + clickOn(this) + } + allowPermissionsIfNeeded(Manifest.permission.POST_NOTIFICATIONS) + } + clickOn(R.id.btn_next) + clickOn(R.id.btn_done) + assertTrue(getCurrentActivity() is MapActivity) + } + + @SdkSuppress(minSdkVersion = 29) + @Test + fun welcomeActivityPromptsForBackgroundLocationPermission() { + clickOn(R.id.btn_next) + clickOn(R.id.btn_next) + + // Location permissions fragment + assertDisplayed(R.string.welcome_location_permission_description) + doIfViewNotVisible(R.id.btn_next) { + R.id.ui_fragment_welcome_location_permissions_request.run { + assertDisplayed(this) + clickOn(this) + } + allowPermissionsIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION) + } + + assertDisplayed(R.id.btn_next) + assertDisplayed(R.id.ui_fragment_welcome_location_background_permissions_request) + } + + @SdkSuppress(maxSdkVersion = 28) + @Test + fun welcomeActivityDoesntPromptForBackgroundLocationPermission() { + clickOn(R.id.btn_next) + clickOn(R.id.btn_next) + + // Location permissions fragment + assertDisplayed(R.string.welcome_location_permission_description) + doIfViewNotVisible(R.id.btn_next) { + R.id.ui_fragment_welcome_location_permissions_request.run { + assertDisplayed(this) + clickOn(this) + } + allowPermissionsIfNeeded(Manifest.permission.ACCESS_FINE_LOCATION) + } + + assertDisplayed(R.id.btn_next) + assertNotDisplayed(R.id.ui_fragment_welcome_location_background_permissions_request) + } + @SdkSuppress(minSdkVersion = 34) @Test fun welcomeActivityDisplaysCorrectFragmentsWithNotificationPermissions() { @@ -36,7 +128,7 @@ class WelcomeActivityTests : TestWithAnActivity(WelcomeActivity } // Location permissions fragment - assertDisplayed(R.id.ui_fragment_welcome_location_permissions_message) + assertDisplayed(R.string.welcome_location_permission_description) doIfViewNotVisible(R.id.btn_next) { R.id.ui_fragment_welcome_location_permissions_request.run { assertDisplayed(this) @@ -46,11 +138,12 @@ class WelcomeActivityTests : TestWithAnActivity(WelcomeActivity } R.id.btn_next.run { assertDisplayed(this) + assertDisplayed(R.id.ui_fragment_welcome_location_background_permissions_request) clickOn(this) } // Notification permissions fragment - assertDisplayed(R.id.ui_fragment_welcome_notification_permissions_message) + assertDisplayed(R.string.welcome_notification_permission_description) doIfViewNotVisible(R.id.btn_next) { R.id.ui_fragment_welcome_notification_permissions_request.run { assertDisplayed(this) @@ -90,7 +183,7 @@ class WelcomeActivityTests : TestWithAnActivity(WelcomeActivity } // Location permissions fragment - assertDisplayed(R.id.ui_fragment_welcome_location_permissions_message) + assertDisplayed(R.string.welcome_location_permission_description) doIfViewNotVisible(R.id.btn_next) { R.id.ui_fragment_welcome_location_permissions_request.run { assertDisplayed(this) diff --git a/project/app/src/main/java/org/owntracks/android/preferences/DefaultsProvider.kt b/project/app/src/main/java/org/owntracks/android/preferences/DefaultsProvider.kt index b58e146d52..35d0dbfc7d 100644 --- a/project/app/src/main/java/org/owntracks/android/preferences/DefaultsProvider.kt +++ b/project/app/src/main/java/org/owntracks/android/preferences/DefaultsProvider.kt @@ -73,6 +73,7 @@ interface DefaultsProvider { StringMaxTwoAlphaNumericChars(preferences.deviceId.takeLast(2).ifEmpty { "na" }) Preferences::url -> "" Preferences::userDeclinedEnableLocationPermissions -> false + Preferences::userDeclinedEnableBackgroundLocationPermissions -> false Preferences::userDeclinedEnableLocationServices -> false Preferences::userDeclinedEnableNotificationPermissions -> false Preferences::username -> "" diff --git a/project/app/src/main/java/org/owntracks/android/preferences/Preferences.kt b/project/app/src/main/java/org/owntracks/android/preferences/Preferences.kt index b66d214f73..6e287da815 100644 --- a/project/app/src/main/java/org/owntracks/android/preferences/Preferences.kt +++ b/project/app/src/main/java/org/owntracks/android/preferences/Preferences.kt @@ -297,6 +297,9 @@ constructor( @Preference(exportModeMqtt = false, exportModeHttp = false) var userDeclinedEnableLocationPermissions: Boolean by preferencesStore + @Preference(exportModeMqtt = false, exportModeHttp = false) + var userDeclinedEnableBackgroundLocationPermissions: Boolean by preferencesStore + @Preference(exportModeMqtt = false, exportModeHttp = false) var userDeclinedEnableLocationServices: Boolean by preferencesStore diff --git a/project/app/src/main/java/org/owntracks/android/services/BackgroundService.kt b/project/app/src/main/java/org/owntracks/android/services/BackgroundService.kt index ab7e74e41e..cd99dffab8 100644 --- a/project/app/src/main/java/org/owntracks/android/services/BackgroundService.kt +++ b/project/app/src/main/java/org/owntracks/android/services/BackgroundService.kt @@ -79,6 +79,7 @@ import org.owntracks.android.preferences.types.MonitoringMode import org.owntracks.android.preferences.types.MonitoringMode.Companion.getByValue import org.owntracks.android.services.worker.Scheduler import org.owntracks.android.support.DateFormatter.formatDate +import org.owntracks.android.support.RequirementsChecker import org.owntracks.android.support.RunThingsOnOtherThreads import org.owntracks.android.test.SimpleIdlingResource import org.owntracks.android.ui.map.MapActivity @@ -114,6 +115,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList @Inject lateinit var locationProviderClient: LocationProviderClient + @Inject lateinit var requirementsChecker: RequirementsChecker + @Inject @Named("contactsClearedIdlingResource") lateinit var contactsClearedIdlingResource: SimpleIdlingResource @@ -296,13 +299,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList INTENT_ACTION_BOOT_COMPLETED, INTENT_ACTION_PACKAGE_REPLACED -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val backgroundLocationPermissionDenied = - ActivityCompat.checkSelfPermission( - this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == - PackageManager.PERMISSION_DENIED - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && - !hasBeenStartedExplicitly && - backgroundLocationPermissionDenied) { + if (!requirementsChecker.hasBackgroundLocationPermission() && + !hasBeenStartedExplicitly) { notifyUserOfBackgroundLocationRestriction() } } diff --git a/project/app/src/main/java/org/owntracks/android/support/OSSRequirementsChecker.kt b/project/app/src/main/java/org/owntracks/android/support/OSSRequirementsChecker.kt index a386e7be83..0ef18d9f1c 100644 --- a/project/app/src/main/java/org/owntracks/android/support/OSSRequirementsChecker.kt +++ b/project/app/src/main/java/org/owntracks/android/support/OSSRequirementsChecker.kt @@ -20,6 +20,15 @@ open class OSSRequirementsChecker @Inject constructor(open val context: Context) ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED + override fun hasBackgroundLocationPermission(): Boolean = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ContextCompat.checkSelfPermission( + context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == + PackageManager.PERMISSION_GRANTED + } else { + true + } + override fun isLocationServiceEnabled(): Boolean = (context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?)?.run { LocationManagerCompat.isLocationEnabled(this) diff --git a/project/app/src/main/java/org/owntracks/android/support/RequirementsChecker.kt b/project/app/src/main/java/org/owntracks/android/support/RequirementsChecker.kt index c186339481..40cfc08f7b 100644 --- a/project/app/src/main/java/org/owntracks/android/support/RequirementsChecker.kt +++ b/project/app/src/main/java/org/owntracks/android/support/RequirementsChecker.kt @@ -3,6 +3,8 @@ package org.owntracks.android.support interface RequirementsChecker { fun hasLocationPermissions(): Boolean + fun hasBackgroundLocationPermission(): Boolean + fun isLocationServiceEnabled(): Boolean fun isPlayServicesCheckPassed(): Boolean diff --git a/project/app/src/main/java/org/owntracks/android/ui/map/MapActivity.kt b/project/app/src/main/java/org/owntracks/android/ui/map/MapActivity.kt index 3086836923..3050697399 100644 --- a/project/app/src/main/java/org/owntracks/android/ui/map/MapActivity.kt +++ b/project/app/src/main/java/org/owntracks/android/ui/map/MapActivity.kt @@ -11,6 +11,7 @@ import android.hardware.Sensor import android.hardware.SensorManager import android.hardware.SensorManager.SENSOR_DELAY_UI import android.net.Uri +import android.os.Build import android.os.Bundle import android.os.IBinder import android.provider.Settings @@ -61,6 +62,7 @@ import org.owntracks.android.support.RequirementsChecker import org.owntracks.android.test.CountingIdlingResourceShim import org.owntracks.android.test.SimpleIdlingResource import org.owntracks.android.ui.NotificationsStash +import org.owntracks.android.ui.mixins.BackgroundLocationPermissionRequester import org.owntracks.android.ui.mixins.LocationPermissionRequester import org.owntracks.android.ui.mixins.NotificationPermissionRequester import org.owntracks.android.ui.mixins.ServiceStarter @@ -81,6 +83,9 @@ class MapActivity : this, ::notificationPermissionGranted, ::notificationPermissionDenied) private val locationPermissionRequester = LocationPermissionRequester(this, ::locationPermissionGranted, ::locationPermissionDenied) + private val backgroundLocationPermissionRequester = + BackgroundLocationPermissionRequester( + this, ::backgroundLocationPermissionGranted, ::backgroundLocationPermissionDenied) private var service: BackgroundService? = null private var bottomSheetBehavior: BottomSheetBehavior? = null private var menu: Menu? = null @@ -392,7 +397,7 @@ class MapActivity : } /** - * User has declined notification permissions. Log this in the preferences so we don't keep askin + * User has declined notification permissions. Log this in the preferences so we don't keep asking * them */ private fun notificationPermissionDenied() { @@ -437,6 +442,24 @@ class MapActivity : .show() } + /** + * User has declined to enable background location permissions. Log this in the preferences so we + * don't keep asking + */ + private fun backgroundLocationPermissionGranted() { + Timber.d("Background location permission granted") + preferences.userDeclinedEnableBackgroundLocationPermissions = false + } + + /** + * User has declined to enable background location permissions. Log this in the preferences so we + * don't keep asking + */ + private fun backgroundLocationPermissionDenied() { + Timber.d("Background location permission denied") + preferences.userDeclinedEnableBackgroundLocationPermissions = true + } + enum class CheckPermissionsResult { HAS_PERMISSIONS, NO_PERMISSIONS_LAUNCHED_REQUEST, @@ -521,9 +544,11 @@ class MapActivity : updateMonitoringModeMenu() viewModel.updateMyLocationStatus() + // Request Notification permissions when (checkAndRequestNotificationPermissions()) { CheckPermissionsResult.HAS_PERMISSIONS, CheckPermissionsResult.NO_PERMISSIONS_NOT_LAUNCHED_REQUEST -> { + // Request Location permissions when (checkAndRequestLocationPermissions(false)) { CheckPermissionsResult.NO_PERMISSIONS_LAUNCHED_REQUEST -> { Timber.d("Launched location permission request") @@ -535,6 +560,12 @@ class MapActivity : Timber.d("Has location permissions") if (checkAndRequestLocationServicesEnabled(false)) { viewModel.requestLocationUpdatesForBlueDot() + // Request background location permissions if needed + if (!requirementsChecker.hasBackgroundLocationPermission() && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && + !preferences.userDeclinedEnableBackgroundLocationPermissions) { + backgroundLocationPermissionRequester.requestLocationPermissions(this) { true } + } } } } diff --git a/project/app/src/main/java/org/owntracks/android/ui/mixins/BackgroundLocationPermissionRequester.kt b/project/app/src/main/java/org/owntracks/android/ui/mixins/BackgroundLocationPermissionRequester.kt new file mode 100644 index 0000000000..03301b2701 --- /dev/null +++ b/project/app/src/main/java/org/owntracks/android/ui/mixins/BackgroundLocationPermissionRequester.kt @@ -0,0 +1,53 @@ +package org.owntracks.android.ui.mixins + +import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION +import android.content.Context +import android.os.Build +import androidx.activity.result.ActivityResultCaller +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.owntracks.android.R +import timber.log.Timber + +class BackgroundLocationPermissionRequester( + caller: ActivityResultCaller, + private val permissionGrantedCallback: () -> Unit, + private val permissionDeniedCallback: () -> Unit +) { + + private val permissionRequest = + caller.registerForActivityResult(ActivityResultContracts.RequestPermission()) { result -> + Timber.d("Background permission callback, result=$result ") + if (result) { + permissionGrantedCallback() + } else { + permissionDeniedCallback() + } + } + + /** Request background location permissions, optionally showing a request dialog first */ + @RequiresApi(Build.VERSION_CODES.Q) + fun requestLocationPermissions( + context: Context, + showPermissionRationale: (permissions: String) -> Boolean + ) { + Timber.d("Requesting Background Location Permissions") + if (showPermissionRationale(ACCESS_BACKGROUND_LOCATION)) { + // The user may have denied us once already, so show a rationale + Timber.d("Showing Background Location permission rationale") + MaterialAlertDialogBuilder(context) + .setCancelable(true) + .setIcon(R.drawable.baseline_share_location_24) + .setTitle(R.string.backgroundLocationPermissionRequestDialogTitle) + .setMessage(R.string.backgroundLocationPermissionRequestDialogText) + .setPositiveButton(android.R.string.ok) { _, _ -> + permissionRequest.launch(ACCESS_BACKGROUND_LOCATION) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> permissionDeniedCallback() } + .show() + } else { + permissionRequest.launch(ACCESS_BACKGROUND_LOCATION) + } + } +} diff --git a/project/app/src/main/java/org/owntracks/android/ui/mixins/LocationPermissionRequester.kt b/project/app/src/main/java/org/owntracks/android/ui/mixins/LocationPermissionRequester.kt index 544836a80f..631d536116 100644 --- a/project/app/src/main/java/org/owntracks/android/ui/mixins/LocationPermissionRequester.kt +++ b/project/app/src/main/java/org/owntracks/android/ui/mixins/LocationPermissionRequester.kt @@ -19,7 +19,7 @@ class LocationPermissionRequester( private val permissionRequest = caller.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - Timber.d("Notification permission callback, result=$permissions ") + Timber.d("Location permission callback, result=$permissions ") when { permissions[ACCESS_COARSE_LOCATION] ?: false || permissions[ACCESS_FINE_LOCATION] ?: false -> { diff --git a/project/app/src/main/java/org/owntracks/android/ui/preferences/AdvancedFragment.kt b/project/app/src/main/java/org/owntracks/android/ui/preferences/AdvancedFragment.kt index 2e1f656108..3f88362709 100644 --- a/project/app/src/main/java/org/owntracks/android/ui/preferences/AdvancedFragment.kt +++ b/project/app/src/main/java/org/owntracks/android/ui/preferences/AdvancedFragment.kt @@ -1,12 +1,8 @@ package org.owntracks.android.ui.preferences -import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION -import android.app.ProgressDialog.show import android.content.Context -import android.os.Build import android.os.Bundle import android.widget.TextView -import androidx.core.content.PermissionChecker import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat @@ -16,10 +12,13 @@ import javax.inject.Inject import org.owntracks.android.R import org.owntracks.android.preferences.Preferences import org.owntracks.android.preferences.types.ReverseGeocodeProvider +import org.owntracks.android.support.RequirementsChecker @AndroidEntryPoint class AdvancedFragment @Inject constructor() : AbstractPreferenceFragment(), Preferences.OnPreferenceChangeListener { + @Inject lateinit var requirementsChecker: RequirementsChecker + override fun onAttach(context: Context) { super.onAttach(context) preferences.registerOnPreferenceChangedListener(this) @@ -58,9 +57,7 @@ class AdvancedFragment @Inject constructor() : remoteCommandAndConfigurationChangeListener findPreference("autostartWarning")?.isVisible = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && - PermissionChecker.checkSelfPermission(requireActivity(), ACCESS_BACKGROUND_LOCATION) == - PermissionChecker.PERMISSION_DENIED + !requirementsChecker.hasBackgroundLocationPermission() findPreference(Preferences::reverseGeocodeProvider.name) ?.onPreferenceChangeListener = diff --git a/project/app/src/main/java/org/owntracks/android/ui/welcome/fragments/LocationPermissionFragment.kt b/project/app/src/main/java/org/owntracks/android/ui/welcome/fragments/LocationPermissionFragment.kt index 74a878df52..cb4dab4657 100644 --- a/project/app/src/main/java/org/owntracks/android/ui/welcome/fragments/LocationPermissionFragment.kt +++ b/project/app/src/main/java/org/owntracks/android/ui/welcome/fragments/LocationPermissionFragment.kt @@ -12,6 +12,7 @@ import javax.inject.Inject import org.owntracks.android.databinding.UiWelcomeLocationPermissionBinding import org.owntracks.android.preferences.Preferences import org.owntracks.android.support.RequirementsChecker +import org.owntracks.android.ui.mixins.BackgroundLocationPermissionRequester import org.owntracks.android.ui.mixins.LocationPermissionRequester import org.owntracks.android.ui.welcome.WelcomeViewModel @@ -24,7 +25,16 @@ class LocationPermissionFragment @Inject constructor() : WelcomeFragment() { @Inject lateinit var requirementsChecker: RequirementsChecker private val locationPermissionRequester = - LocationPermissionRequester(this, ::permissionGranted, ::permissionDenied) + LocationPermissionRequester( + this, + { preferences.userDeclinedEnableLocationPermissions = false }, + { preferences.userDeclinedEnableLocationPermissions = true }) + + private val backgroundLocationPermissionRequester = + BackgroundLocationPermissionRequester( + this, + { preferences.userDeclinedEnableBackgroundLocationPermissions = false }, + { preferences.userDeclinedEnableBackgroundLocationPermissions = true }) override fun shouldBeDisplayed(context: Context): Boolean = true @@ -37,41 +47,48 @@ class LocationPermissionFragment @Inject constructor() : WelcomeFragment() { binding = UiWelcomeLocationPermissionBinding.inflate(inflater, container, false).apply { uiFragmentWelcomeLocationPermissionsRequest.setOnClickListener { - requestLocationPermissions() + locationPermissionRequester.requestLocationPermissions(0, requireContext()) { + shouldShowRequestPermissionRationale(it) + } + } + uiFragmentWelcomeLocationBackgroundPermissionsRequest.setOnClickListener { + backgroundLocationPermissionRequester.requestLocationPermissions(requireContext()) { + false + } } } return binding.root } - private fun requestLocationPermissions() { - locationPermissionRequester.requestLocationPermissions(0, requireContext()) { - shouldShowRequestPermissionRationale(it) - } - } - override fun onResume() { super.onResume() viewModel.setWelcomeState( if (requirementsChecker.hasLocationPermissions()) { - binding.uiFragmentWelcomeLocationPermissionsRequest.visibility = View.INVISIBLE - binding.uiFragmentWelcomeLocationPermissionsMessage.visibility = View.VISIBLE WelcomeViewModel.ProgressState.PERMITTED } else if (preferences.userDeclinedEnableLocationPermissions) { WelcomeViewModel.ProgressState.PERMITTED } else { WelcomeViewModel.ProgressState.NOT_PERMITTED }) - } - private fun permissionGranted(@Suppress("UNUSED_PARAMETER") code: Int) { - preferences.userDeclinedEnableLocationPermissions = false - binding.uiFragmentWelcomeLocationPermissionsRequest.visibility = View.INVISIBLE - binding.uiFragmentWelcomeLocationPermissionsMessage.visibility = View.VISIBLE - viewModel.setWelcomeState(WelcomeViewModel.ProgressState.PERMITTED) + if (requirementsChecker.hasLocationPermissions() && + !requirementsChecker.hasBackgroundLocationPermission()) { + binding.uiFragmentWelcomeLocationBackgroundPermissionsRequest.visibility = View.VISIBLE + binding.uiFragmentWelcomeLocationPermissionsRequest.visibility = View.INVISIBLE + binding.uiFragmentWelcomeLocationPermissionsMessage.visibility = View.INVISIBLE + } else if (requirementsChecker.hasLocationPermissions() && + requirementsChecker.hasBackgroundLocationPermission()) { + binding.uiFragmentWelcomeLocationBackgroundPermissionsRequest.visibility = View.INVISIBLE + binding.uiFragmentWelcomeLocationPermissionsRequest.visibility = View.INVISIBLE + binding.uiFragmentWelcomeLocationPermissionsMessage.visibility = View.VISIBLE + } else { + binding.uiFragmentWelcomeLocationBackgroundPermissionsRequest.visibility = View.INVISIBLE + binding.uiFragmentWelcomeLocationPermissionsRequest.visibility = View.VISIBLE + binding.uiFragmentWelcomeLocationPermissionsMessage.visibility = View.INVISIBLE + } } private fun permissionDenied(@Suppress("UNUSED_PARAMETER") code: Int) { preferences.userDeclinedEnableLocationPermissions = true - viewModel.setWelcomeState(WelcomeViewModel.ProgressState.PERMITTED) } } diff --git a/project/app/src/main/res/drawable/baseline_share_location_24.xml b/project/app/src/main/res/drawable/baseline_share_location_24.xml new file mode 100644 index 0000000000..e1cee9898c --- /dev/null +++ b/project/app/src/main/res/drawable/baseline_share_location_24.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/project/app/src/main/res/layout/ui_welcome_location_permission.xml b/project/app/src/main/res/layout/ui_welcome_location_permission.xml index 5d11f5431b..1427ebf272 100644 --- a/project/app/src/main/res/layout/ui_welcome_location_permission.xml +++ b/project/app/src/main/res/layout/ui_welcome_location_permission.xml @@ -59,7 +59,16 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" - android:text="@string/welcome_permission_request" /> + android:text="@string/welcome_location_permission_request" /> +