1818
1919package org.blitzortung.android.app
2020
21- import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
22- import android.Manifest.permission.ACCESS_COARSE_LOCATION
23- import android.Manifest.permission.ACCESS_FINE_LOCATION
24- import android.Manifest.permission.POST_NOTIFICATIONS
25- import android.Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
26- import android.content.Context
27- import android.content.DialogInterface
2821import android.content.Intent
2922import android.content.SharedPreferences
3023import android.content.pm.PackageManager
3124import android.graphics.Color
32- import android.location.LocationManager.GPS_PROVIDER
33- import android.location.LocationManager.NETWORK_PROVIDER
34- import android.location.LocationManager.PASSIVE_PROVIDER
3525import android.os.Build
3626import android.os.Bundle
37- import android.os.PowerManager
38- import android.provider.Settings
3927import android.text.format.DateFormat
40- import android.util.AndroidRuntimeException
4128import android.util.Log
4229import android.view.KeyEvent
4330import android.view.View
@@ -47,10 +34,7 @@ import android.widget.ImageButton
4734import android.widget.SeekBar
4835import android.widget.SeekBar.OnSeekBarChangeListener
4936import android.widget.Toast
50- import androidx.annotation.RequiresApi
51- import androidx.appcompat.app.AlertDialog
5237import androidx.core.content.edit
53- import androidx.core.net.toUri
5438import androidx.core.view.isVisible
5539import androidx.core.view.WindowCompat // Import added
5640import androidx.fragment.app.FragmentActivity
@@ -64,6 +48,12 @@ import org.blitzortung.android.app.components.VersionComponent
6448import org.blitzortung.android.app.controller.ButtonColumnHandler
6549import org.blitzortung.android.app.controller.HistoryController
6650import org.blitzortung.android.app.databinding.MainBinding
51+ import org.blitzortung.android.app.permission.LocationProviderRelation
52+ import org.blitzortung.android.app.permission.PermissionsSupport
53+ import org.blitzortung.android.app.permission.requester.BackgroundLocationPermissionRequester
54+ import org.blitzortung.android.app.permission.requester.LocationPermissionRequester
55+ import org.blitzortung.android.app.permission.requester.NotificationPermissionRequester
56+ import org.blitzortung.android.app.permission.requester.WakeupPermissionRequester
6757import org.blitzortung.android.app.view.OnSharedPreferenceChangeListener
6858import org.blitzortung.android.app.view.PreferenceKey
6959import org.blitzortung.android.app.view.components.StatusComponent
@@ -459,19 +449,12 @@ class Main : FragmentActivity(), OnSharedPreferenceChangeListener {
459449 Log .v(LOG_TAG , " Main.onResume()" )
460450
461451 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
462- val permissionRequests: List <Pair <String , () - > Boolean >> = listOf (
463- Pair (" location" , { requestLocationPermissions(preferences) }),
464- Pair (" notification" , { requestNotificationPermissions() }),
465- Pair (" background_location" , { requestBackgroundLocationPermissions() }),
466- Pair (" wakeup" , { requestWakeupPermissions(baseContext) }),
467- )
468- for (pair in permissionRequests) {
469- val result = pair.second()
470- Log .v(LOG_TAG , " Main.onResume() permission ${pair.first} : result: $result " )
471- if (result) {
472- break
473- }
474- }
452+ PermissionsSupport .ensurePermissions(this ,
453+ LocationPermissionRequester (preferences),
454+ NotificationPermissionRequester (),
455+ BackgroundLocationPermissionRequester (this , preferences),
456+ WakeupPermissionRequester (this , preferences)
457+ )
475458 }
476459
477460 mapFragment.updateForgroundColor(strikeColorHandler.lineColor)
@@ -628,177 +611,6 @@ class Main : FragmentActivity(), OnSharedPreferenceChangeListener {
628611 }
629612 }
630613
631- private fun requestNotificationPermissions (): Boolean {
632- return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
633- requestPermission(POST_NOTIFICATIONS , REQUEST_CODE_POST_NOTIFICATIONS , R .string.post_notifications_request)
634- } else {
635- false
636- }
637- }
638-
639- @RequiresApi(Build .VERSION_CODES .M )
640- private fun requestLocationPermissions (sharedPreferences : SharedPreferences ): Boolean {
641- val (permission, requestCode) = getLocationPermission(sharedPreferences)
642-
643- return if (permission != null ) {
644- requestPermission(
645- permission, requestCode, R .string.location_permission_required
646- )
647- } else {
648- false
649- }
650- }
651-
652- @RequiresApi(Build .VERSION_CODES .M )
653- private fun requestBackgroundLocationPermissions (): Boolean {
654- return if (isAtLeast(Build .VERSION_CODES .Q ) && backgroundAlertEnabled && checkSelfPermission(
655- ACCESS_BACKGROUND_LOCATION
656- ) != PackageManager .PERMISSION_GRANTED
657- ) {
658- Log .v(LOG_TAG , " Main.requestLocationPermissions() open background permission dialog" )
659- val locationText = this .resources.getString(R .string.location_permission_background_disclosure)
660- AlertDialog .Builder (this ).setMessage(locationText).setCancelable(false )
661- .setPositiveButton(android.R .string.ok) { dialog, _ ->
662- dialog.dismiss()
663- requestPermission(
664- ACCESS_BACKGROUND_LOCATION ,
665- REQUEST_CODE_BACKGROUND_LOCATION ,
666- R .string.location_permission_background_required
667- )
668- }.setNegativeButton(android.R .string.cancel) { _, _ ->
669- preferences.edit { put(PreferenceKey .BACKGROUND_QUERY_PERIOD , " 0" ) }
670- }.show()
671- true
672- } else {
673- false
674- }
675- }
676-
677- private fun getLocationPermission (sharedPreferences : SharedPreferences ): Pair <String ?, Int > {
678- val locationProviderName = sharedPreferences.get(PreferenceKey .LOCATION_MODE , PASSIVE_PROVIDER )
679- val permission = when (locationProviderName) {
680- PASSIVE_PROVIDER , GPS_PROVIDER -> ACCESS_FINE_LOCATION
681- NETWORK_PROVIDER -> ACCESS_COARSE_LOCATION
682- else -> null
683- }
684- val requestCode = (LocationProviderRelation .byProviderName[locationProviderName]?.ordinal ? : Int .MAX_VALUE )
685- return Pair (permission, requestCode)
686- }
687-
688-
689- @RequiresApi(Build .VERSION_CODES .M )
690- private fun requestPermission (permission : String , requestCode : Int , permissionRequiredStringId : Int ): Boolean {
691- val shouldShowPermissionRationale = shouldShowRequestPermissionRationale(permission)
692- val permissionIsGranted = checkSelfPermission(permission) == PackageManager .PERMISSION_GRANTED
693- Log .v(
694- LOG_TAG ,
695- " Main.requestPermission() permission: $permission , requestCode: $requestCode , isGranted: $permissionIsGranted , shouldShowRationale: ${! shouldShowPermissionRationale} "
696- )
697-
698- return if (! permissionIsGranted) {
699- if (shouldShowPermissionRationale) {
700- requestPermissionsAfterDialog(permissionRequiredStringId, permission, requestCode)
701- } else {
702- requestPermissions(arrayOf(permission), requestCode)
703- }
704- true
705- } else {
706- false
707- }
708- }
709-
710- @RequiresApi(Build .VERSION_CODES .M )
711- private fun requestPermissionsAfterDialog (
712- dialogTextResource : Int ,
713- permission : String ,
714- requestCode : Int ,
715- ) {
716- Log .v(
717- LOG_TAG ,
718- " Main.requestPermissionsAfterDialog() permission: $permission , dialogResource: $dialogTextResource , requestCode: $requestCode "
719- )
720-
721- val locationText = resources.getString(dialogTextResource)
722- AlertDialog .Builder (this ).setMessage(locationText).setCancelable(false )
723- .setPositiveButton(android.R .string.ok) { dialog, count ->
724- dialog.dismiss()
725- requestPermissions(arrayOf(permission), requestCode)
726- }.show()
727- }
728-
729- @RequiresApi(Build .VERSION_CODES .M )
730- private fun requestWakeupPermissions (context : Context ): Boolean {
731- Log .v(LOG_TAG , " requestWakeupPermissions() background alerts: $backgroundAlertEnabled " )
732-
733- if (backgroundAlertEnabled) {
734- val pm = context.getSystemService(POWER_SERVICE )
735- if (pm is PowerManager ) {
736- val packageName = context.packageName
737- Log .v(LOG_TAG , " requestWakeupPermissions() package name $packageName " )
738- if (! pm.isIgnoringBatteryOptimizations(packageName)) {
739- val locationText = context.resources.getString(R .string.open_battery_optimiziation)
740-
741- val dialogClickListener = DialogInterface .OnClickListener { dialog, which ->
742- dialog.dismiss()
743- when (which) {
744- DialogInterface .BUTTON_POSITIVE -> {
745- Log .v(LOG_TAG , " requestWakeupPermissions() request ignore battery optimizations" )
746- val allowIgnoreBatteryOptimization =
747- context.checkSelfPermission(REQUEST_IGNORE_BATTERY_OPTIMIZATIONS ) == PackageManager .PERMISSION_GRANTED
748- val intent = if (allowIgnoreBatteryOptimization) {
749- Intent (
750- Settings .ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS ,
751- " package:$packageName " .toUri()
752- )
753- } else {
754- Intent (Settings .ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS )
755- }
756-
757- try {
758- startActivity(intent)
759- } catch (e: AndroidRuntimeException ) {
760- Toast .makeText(baseContext, R .string.background_query_toast, Toast .LENGTH_LONG )
761- .show()
762- Log .e(
763- LOG_TAG ,
764- " requestWakeupPermissions() could not open battery optimization settings" ,
765- e
766- )
767- }
768- }
769-
770- DialogInterface .BUTTON_NEGATIVE -> {
771- preferences.edit().apply {
772- putString(PreferenceKey .BACKGROUND_QUERY_PERIOD .toString(), 0 .toString())
773- apply ()
774- }
775- }
776- }
777- }
778-
779- AlertDialog .Builder (this ).setMessage(locationText)
780- .setPositiveButton(android.R .string.ok, dialogClickListener)
781- .setNegativeButton(android.R .string.cancel, dialogClickListener).show()
782- return true
783- }
784- } else {
785- Log .w(LOG_TAG , " requestWakeupPermissions() could not get PowerManager" )
786- }
787- }
788- return false
789- }
790-
791- private enum class LocationProviderRelation (val providerName : String ) {
792- GPS (GPS_PROVIDER ), PASSIVE (PASSIVE_PROVIDER ), NETWORK (NETWORK_PROVIDER );
793-
794- companion object {
795- val byProviderName: Map <String , LocationProviderRelation > =
796- entries.groupBy { it.providerName }.mapValues { it.value.first() }
797- val byOrdinal: Map <Int , LocationProviderRelation > =
798- entries.groupBy { it.ordinal }.mapValues { it.value.first() }
799- }
800- }
801-
802614 override fun onKeyUp (keyCode : Int , event : KeyEvent ? ): Boolean {
803615 if (keyCode == KeyEvent .KEYCODE_MENU ) {
804616 Log .v(LOG_TAG , " Main.onKeyUp(KEYCODE_MENU)" )
0 commit comments