From 969f713914b2703fc796bbcfe896108f8ffa88bc Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 30 Mar 2020 13:14:24 +0200 Subject: [PATCH] AndroidPermission refactor --- .../nightscout/androidaps/MainActivity.java | 11 +- .../androidaps/setupwizard/SWDefinition.kt | 99 +++++------ .../setupwizard/elements/SWFragment.kt | 4 +- .../androidaps/utils/AndroidPermission.java | 155 ----------------- .../androidaps/utils/AndroidPermission.kt | 163 ++++++++++++++++++ 5 files changed, 216 insertions(+), 216 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java create mode 100644 app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index f6a250dc4a..b0c09bc44d 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -78,6 +78,7 @@ public class MainActivity extends NoSplashAppCompatActivity { @Inject AAPSLogger aapsLogger; @Inject RxBusWrapper rxBus; + @Inject AndroidPermission androidPermission; @Inject SP sp; @Inject ResourceHelper resourceHelper; @Inject VersionCheckerUtils versionCheckerUtils; @@ -161,12 +162,12 @@ public class MainActivity extends NoSplashAppCompatActivity { startActivity(intent); } - AndroidPermission.notifyForStoragePermission(this); - AndroidPermission.notifyForBatteryOptimizationPermission(this); + androidPermission.notifyForStoragePermission(this); + androidPermission.notifyForBatteryOptimizationPermission(this); if (Config.PUMPDRIVERS) { - AndroidPermission.notifyForLocationPermissions(this); - AndroidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin); - AndroidPermission.notifyForSystemWindowPermissions(this); + androidPermission.notifyForLocationPermissions(this); + androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin); + androidPermission.notifyForSystemWindowPermissions(this); } } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index 940635d555..5164e8388e 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -1,12 +1,12 @@ package info.nightscout.androidaps.setupwizard import android.Manifest +import android.content.Context import android.content.Intent import androidx.appcompat.app.AppCompatActivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.dialogs.ProfileSwitchDialog @@ -46,7 +46,7 @@ import javax.inject.Singleton class SWDefinition @Inject constructor( injector: HasAndroidInjector, private val rxBus: RxBusWrapper, - private val mainApp: MainApp, + private val context: Context, resourceHelper: ResourceHelper, private val sp: SP, private val profileFunction: ProfileFunction, @@ -58,10 +58,11 @@ class SWDefinition @Inject constructor( private val loopPlugin: LoopPlugin, private val nsClientPlugin: NSClientPlugin, private val nsProfilePlugin: NSProfilePlugin, - private val protectionCheck: ProtectionCheck + private val protectionCheck: ProtectionCheck, + private val androidPermission: AndroidPermission ) { - var activity: AppCompatActivity? = null + lateinit var activity: AppCompatActivity private val screens: MutableList = ArrayList() fun getScreens(): List { @@ -83,7 +84,7 @@ class SWDefinition @Inject constructor( .preferenceId(R.string.key_language).label(R.string.language) .comment(R.string.setupwizard_language_prompt)) .validator(SWValidator { - update(mainApp) + update(context) sp.contains(R.string.key_language) }) private val screenEula = SWScreen(injector, R.string.end_user_license_agreement) @@ -127,10 +128,10 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) private val screenPermissionBt = SWScreen(injector, R.string.permission) .skippable(false) .add(SWInfotext(injector) @@ -138,10 +139,10 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) private val screenPermissionStore = SWScreen(injector, R.string.permission) .skippable(false) .add(SWInfotext(injector) @@ -149,10 +150,10 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .add(SWButton(injector) .text(R.string.askforpermission) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) - .action(Runnable { AndroidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE) })) - .visibility(SWValidator { AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) - .validator(SWValidator { !AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .action(Runnable { androidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE) })) + .visibility(SWValidator { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .validator(SWValidator { !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) private val screenImport = SWScreen(injector, R.string.nav_import) .add(SWInfotext(injector) .label(R.string.storedsettingsfound)) @@ -160,7 +161,7 @@ class SWDefinition @Inject constructor( .add(SWButton(injector) .text(R.string.nav_import) .action(Runnable { ImportExportPrefs.importSharedPreferences(activity) })) - .visibility(SWValidator { ImportExportPrefs.file.exists() && !AndroidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .visibility(SWValidator { ImportExportPrefs.file.exists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title) .skippable(true) .add(SWInfotext(injector) @@ -219,13 +220,11 @@ class SWDefinition @Inject constructor( .text(R.string.insulinsourcesetup) .action(Runnable { val plugin = activePlugin.activeInsulin as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeInsulin as PluginBase).preferencesId > 0 })) private val screenBgSource = SWScreen(injector, R.string.configbuilder_bgsource) @@ -238,13 +237,11 @@ class SWDefinition @Inject constructor( .text(R.string.bgsourcesetup) .action(Runnable { val plugin = activePlugin.activeBgSource as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeBgSource as PluginBase).preferencesId > 0 })) private val screenProfile = SWScreen(injector, R.string.configbuilder_profile) @@ -275,7 +272,7 @@ class SWDefinition @Inject constructor( .label(R.string.profileswitch_ismissing)) .add(SWButton(injector) .text(R.string.doprofileswitch) - .action(Runnable { ProfileSwitchDialog().show(activity!!.supportFragmentManager, "SetupWizard") })) + .action(Runnable { ProfileSwitchDialog().show(activity.supportFragmentManager, "SetupWizard") })) .validator(SWValidator { profileFunction.getProfile() != null }) .visibility(SWValidator { profileFunction.getProfile() == null }) private val screenPump = SWScreen(injector, R.string.configbuilder_pump) @@ -288,13 +285,11 @@ class SWDefinition @Inject constructor( .text(R.string.pumpsetup) .action(Runnable { val plugin = activePlugin.activePump as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activePump as PluginBase).preferencesId > 0 })) .add(SWButton(injector) @@ -317,13 +312,11 @@ class SWDefinition @Inject constructor( .text(R.string.apssetup) .action(Runnable { val plugin = activePlugin.activeAPS as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeAPS as PluginBase).preferencesId > 0 })) .visibility(SWValidator { Config.APS }) @@ -367,13 +360,11 @@ class SWDefinition @Inject constructor( .text(R.string.sensitivitysetup) .action(Runnable { val plugin = activePlugin.activeSensitivity as PluginBase - activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { - val i = Intent(activity, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - activity.startActivity(i) - }, null) - } + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(activity, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + activity.startActivity(i) + }, null) }) .visibility(SWValidator { (activePlugin.activeSensitivity as PluginBase).preferencesId > 0 })) private val getScreenObjectives = SWScreen(injector, R.string.objectives) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt index 72ac956251..d90890f068 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.kt @@ -5,7 +5,7 @@ import androidx.fragment.app.Fragment import dagger.android.HasAndroidInjector import info.nightscout.androidaps.setupwizard.SWDefinition -class SWFragment(injecto:HasAndroidInjector, private var definition: SWDefinition) : SWItem(injecto, Type.FRAGMENT) { +class SWFragment(injector:HasAndroidInjector, private var definition: SWDefinition) : SWItem(injector, Type.FRAGMENT) { lateinit var fragment: Fragment fun add(fragment: Fragment): SWFragment { @@ -14,6 +14,6 @@ class SWFragment(injecto:HasAndroidInjector, private var definition: SWDefinitio } override fun generateDialog(layout: LinearLayout) { - definition.activity?.supportFragmentManager?.beginTransaction()?.add(layout.id, fragment, fragment.tag)?.commit() + definition.activity.supportFragmentManager.beginTransaction().add(layout.id, fragment, fragment.tag).commit() } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java deleted file mode 100644 index 2770e26ed3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java +++ /dev/null @@ -1,155 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; -import android.os.PowerManager; -import android.provider.Settings; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; - -public class AndroidPermission { - - public static final int CASE_STORAGE = 0x1; - public static final int CASE_SMS = 0x2; - public static final int CASE_LOCATION = 0x3; - public static final int CASE_BATTERY = 0x4; - public static final int CASE_PHONE_STATE = 0x5; - public static final int CASE_SYSTEM_WINDOW = 0x6; - - private static boolean permission_battery_optimization_failed = false; - - @SuppressLint("BatteryLife") - private static void askForPermission(Activity activity, String[] permission, Integer requestCode) { - boolean test = false; - boolean testBattery = false; - for (String s : permission) { - test = test || (ContextCompat.checkSelfPermission(activity, s) != PackageManager.PERMISSION_GRANTED); - if (s.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); - String packageName = activity.getPackageName(); - testBattery = testBattery || !powerManager.isIgnoringBatteryOptimizations(packageName); - } - } - if (test) { - ActivityCompat.requestPermissions(activity, permission, requestCode); - } - if (testBattery) { - try { - Intent i = new Intent(); - i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - i.setData(Uri.parse("package:" + activity.getPackageName())); - activity.startActivityForResult(i, CASE_BATTERY); - } catch (ActivityNotFoundException e) { - permission_battery_optimization_failed = true; - OKDialog.show(activity, MainApp.gs(R.string.permission), MainApp.gs(R.string.alert_dialog_permission_battery_optimization_failed), activity::recreate); - } - } - } - - public static void askForPermission(Activity activity, String permission, Integer requestCode) { - String[] permissions = {permission}; - askForPermission(activity, permissions, requestCode); - } - - public static boolean permissionNotGranted(Context context, String permission) { - boolean selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; - if (permission.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - if (!permission_battery_optimization_failed) { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - String packageName = context.getPackageName(); - selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName); - } - } - return !selfCheck; - } - - public static synchronized void notifyForSMSPermissions(Activity activity, SmsCommunicatorPlugin smsCommunicatorPlugin) { - if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { - if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.RECEIVE_MMS}, AndroidPermission.CASE_SMS)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_SMS)); - // Following is a bug in Android 8 - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { - if (permissionNotGranted(activity, Manifest.permission.READ_PHONE_STATE)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_PHONESTATE, MainApp.gs(R.string.smscommunicator_missingphonestatepermission), Notification.URGENT); - notification.action(R.string.request, () -> - AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, AndroidPermission.CASE_PHONE_STATE)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_PHONESTATE)); - } - } - } - - public static synchronized void notifyForBatteryOptimizationPermission(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_BATTERY, String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}, AndroidPermission.CASE_BATTERY)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_BATTERY)); - } - - public static synchronized void notifyForStoragePermission(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_STORAGE, MainApp.gs(R.string.needstoragepermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE}, AndroidPermission.CASE_STORAGE)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_STORAGE)); - } - - public static synchronized void notifyForLocationPermissions(Activity activity) { - if (permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_LOCATION, MainApp.gs(R.string.needlocationpermission), Notification.URGENT); - notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, AndroidPermission.CASE_LOCATION)); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_LOCATION)); - } - - public static synchronized void notifyForSystemWindowPermissions(Activity activity) { - // Check if Android Q or higher - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - if (!Settings.canDrawOverlays(activity)) { - NotificationWithAction notification = new NotificationWithAction(MainApp.instance(), Notification.PERMISSION_SYSTEM_WINDOW, MainApp.gs(R.string.needsystemwindowpermission), Notification.URGENT); - notification.action(R.string.request, () -> { - // Check if Android Q or higher - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - // Show alert dialog to the user saying a separate permission is needed - // Launch the settings activity if the user prefers - Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:" + activity.getPackageName())); - activity.startActivity(intent); - } - }); - RxBus.Companion.getINSTANCE().send(new EventNewNotification(notification)); - } else - RxBus.Companion.getINSTANCE().send(new EventDismissNotification(Notification.PERMISSION_SYSTEM_WINDOW)); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt new file mode 100644 index 0000000000..583aa2bf25 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt @@ -0,0 +1,163 @@ +package info.nightscout.androidaps.utils + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.PowerManager +import android.provider.Settings +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin +import info.nightscout.androidaps.utils.OKDialog.show +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AndroidPermission @Inject constructor( + val resourceHelper: ResourceHelper, + val rxBus: RxBusWrapper, + val injector: HasAndroidInjector +) { + + companion object { + const val CASE_STORAGE = 0x1 + const val CASE_SMS = 0x2 + const val CASE_LOCATION = 0x3 + const val CASE_BATTERY = 0x4 + const val CASE_PHONE_STATE = 0x5 + const val CASE_SYSTEM_WINDOW = 0x6 + } + + private var permission_battery_optimization_failed = false + + @SuppressLint("BatteryLife") + private fun askForPermission(activity: Activity, permission: Array, requestCode: Int) { + var test = false + var testBattery = false + for (s in permission) { + test = test || ContextCompat.checkSelfPermission(activity, s) != PackageManager.PERMISSION_GRANTED + if (s == Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) { + val powerManager = activity.getSystemService(Context.POWER_SERVICE) as PowerManager + val packageName = activity.packageName + testBattery = testBattery || !powerManager.isIgnoringBatteryOptimizations(packageName) + } + } + if (test) { + ActivityCompat.requestPermissions(activity, permission, requestCode) + } + if (testBattery) { + try { + val i = Intent() + i.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + i.data = Uri.parse("package:" + activity.packageName) + activity.startActivityForResult(i, CASE_BATTERY) + } catch (e: ActivityNotFoundException) { + permission_battery_optimization_failed = true + show(activity, resourceHelper.gs(R.string.permission), resourceHelper.gs(R.string.alert_dialog_permission_battery_optimization_failed), Runnable { activity.recreate() }) + } + } + } + + fun askForPermission(activity: Activity, permission: String, requestCode: Int) { + val permissions = arrayOf(permission) + askForPermission(activity, permissions, requestCode) + } + + fun permissionNotGranted(context: Context, permission: String): Boolean { + var selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED + if (permission == Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) { + if (!permission_battery_optimization_failed) { + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + val packageName = context.packageName + selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName) + } + } + return !selfCheck + } + + @Synchronized + fun notifyForSMSPermissions(activity: Activity, smsCommunicatorPlugin: SmsCommunicatorPlugin) { + if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { + if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_SMS, resourceHelper.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + askForPermission(activity, arrayOf(Manifest.permission.RECEIVE_SMS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_MMS), CASE_SMS) + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_SMS)) + // Following is a bug in Android 8 + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { + if (permissionNotGranted(activity, Manifest.permission.READ_PHONE_STATE)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_PHONESTATE, resourceHelper.gs(R.string.smscommunicator_missingphonestatepermission), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.READ_PHONE_STATE), CASE_PHONE_STATE) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_PHONESTATE)) + } + } + } + + @Synchronized + fun notifyForBatteryOptimizationPermission(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_BATTERY, String.format(resourceHelper.gs(R.string.needwhitelisting), resourceHelper.gs(R.string.app_name)), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS), CASE_BATTERY) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_BATTERY)) + } + + @Synchronized fun notifyForStoragePermission(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_STORAGE, resourceHelper.gs(R.string.needstoragepermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + askForPermission(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE), CASE_STORAGE) + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_STORAGE)) + } + + @Synchronized fun notifyForLocationPermissions(activity: Activity) { + if (permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_LOCATION, resourceHelper.gs(R.string.needlocationpermission), Notification.URGENT) + notification.action(R.string.request, Runnable { askForPermission(activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), CASE_LOCATION) }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_LOCATION)) + } + + @Synchronized fun notifyForSystemWindowPermissions(activity: Activity) { + // Check if Android Q or higher + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + if (!Settings.canDrawOverlays(activity)) { + val notification = NotificationWithAction(injector, Notification.PERMISSION_SYSTEM_WINDOW, resourceHelper.gs(R.string.needsystemwindowpermission), Notification.URGENT) + notification.action(R.string.request, Runnable { + // Check if Android Q or higher + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + // Show alert dialog to the user saying a separate permission is needed + // Launch the settings activity if the user prefers + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + activity.packageName)) + activity.startActivity(intent) + } + }) + rxBus.send(EventNewNotification(notification)) + } else rxBus.send(EventDismissNotification(Notification.PERMISSION_SYSTEM_WINDOW)) + } + } +} \ No newline at end of file