1. use XXPermissions to manage REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.

2. pre-request permission on Start on Boot enabled.
This commit is contained in:
csf 2023-02-28 21:02:42 +09:00
parent 73bc963311
commit 60ab29ad6e
5 changed files with 43 additions and 47 deletions

View File

@ -18,6 +18,7 @@ import android.provider.Settings
import android.util.Log import android.util.Log
import android.view.WindowManager import android.view.WindowManager
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.hjq.permissions.XXPermissions
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
@ -76,7 +77,7 @@ class MainActivity : FlutterActivity() {
} }
"check_permission" -> { "check_permission" -> {
if (call.arguments is String) { if (call.arguments is String) {
result.success(checkPermission(context, call.arguments as String)) result.success(XXPermissions.isGranted(context, call.arguments as String))
} else { } else {
result.success(false) result.success(false)
} }
@ -115,10 +116,6 @@ class MainActivity : FlutterActivity() {
) )
result.success(true) result.success(true)
} }
"init_input" -> {
initInput()
result.success(true)
}
"stop_input" -> { "stop_input" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
InputService.ctx?.disableSelf() InputService.ctx?.disableSelf()
@ -177,13 +174,6 @@ class MainActivity : FlutterActivity() {
} }
} }
private fun initInput() {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val inputPer = InputService.isOpen val inputPer = InputService.isOpen

View File

@ -13,6 +13,7 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings
import android.provider.Settings.* import android.provider.Settings.*
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.ContextCompat.getSystemService
@ -41,8 +42,6 @@ const val START_ACTION = "start_action"
const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt" const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt"
const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt" const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt"
const val IGNORE_BATTERY_OPTIMIZATIONS = "ignore_battery_optimizations"
const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES" const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES"
const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT" const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT"
@ -70,21 +69,15 @@ fun requestPermission(context: Context, type: String) {
} }
} }
@RequiresApi(Build.VERSION_CODES.M)
fun checkPermission(context: Context, type: String): Boolean {
if (IGNORE_BATTERY_OPTIMIZATIONS == type) {
val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return pw.isIgnoringBatteryOptimizations(context.packageName)
}
return XXPermissions.isGranted(context, type)
}
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
fun startAction(context: Context, action: String) { fun startAction(context: Context, action: String) {
try { try {
context.startActivity(Intent(action).apply { context.startActivity(Intent(action).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.parse("package:" + context.packageName) // don't pass package name when launch ACTION_ACCESSIBILITY_SETTINGS
if (ACTION_ACCESSIBILITY_SETTINGS != action) {
data = Uri.parse("package:" + context.packageName)
}
}) })
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()

View File

@ -931,6 +931,8 @@ class AndroidPermissionManager {
gFFI.invokeMethod(AndroidChannel.kStartAction, action); gFFI.invokeMethod(AndroidChannel.kStartAction, action);
} }
/// We use XXPermissions to request permissions,
/// for supported types, see https://github.com/getActivity/XXPermissions/blob/e46caea32a64ad7819df62d448fb1c825481cd28/library/src/main/java/com/hjq/permissions/Permission.java
static Future<bool> request(String type) { static Future<bool> request(String type) {
if (isDesktop) { if (isDesktop) {
return Future.value(true); return Future.value(true);
@ -938,17 +940,16 @@ class AndroidPermissionManager {
gFFI.invokeMethod("request_permission", type); gFFI.invokeMethod("request_permission", type);
// kIgnoreBatteryOptimizations permission doesn't depend on callback result, the result will be checked and updated on page resume // clear last task
if (type == kIgnoreBatteryOptimizations) { if (_completer?.isCompleted == false) {
return Future.value(false); _completer?.complete(false);
} }
_timer?.cancel();
_current = type; _current = type;
_completer = Completer<bool>(); _completer = Completer<bool>();
// timeout _timer = Timer(Duration(seconds: 120), () {
_timer?.cancel();
_timer = Timer(Duration(seconds: 60), () {
if (_completer == null) return; if (_completer == null) return;
if (!_completer!.isCompleted) { if (!_completer!.isCompleted) {
_completer!.complete(false); _completer!.complete(false);

View File

@ -140,17 +140,14 @@ const kIgnoreDpi = true;
/// Android constants /// Android constants
const kActionApplicationDetailsSettings = const kActionApplicationDetailsSettings =
"android.settings.APPLICATION_DETAILS_SETTINGS"; "android.settings.APPLICATION_DETAILS_SETTINGS";
const kActionRequestIgnoreBatteryOptimizations =
"android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
const kActionAccessibilitySettings = "android.settings.ACCESSIBILITY_SETTINGS"; const kActionAccessibilitySettings = "android.settings.ACCESSIBILITY_SETTINGS";
const kRecordAudio = "android.permission.RECORD_AUDIO"; const kRecordAudio = "android.permission.RECORD_AUDIO";
const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE"; const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE";
const kRequestIgnoreBatteryOptimizations =
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW"; const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW";
/// [kIgnoreBatteryOptimizations] not a Android Permission, it is a custom key, used in `ignore battery optimizations` check
const kIgnoreBatteryOptimizations = "ignore_battery_optimizations";
/// Android channel invoke type key /// Android channel invoke type key
class AndroidChannel { class AndroidChannel {
static final kStartAction = "start_action"; static final kStartAction = "start_action";

View File

@ -65,7 +65,6 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
update = true; update = true;
} }
// TODO need input
// start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
var enableStartOnBoot = var enableStartOnBoot =
await gFFI.invokeMethod(AndroidChannel.kGetStartOnBootOpt); await gFFI.invokeMethod(AndroidChannel.kGetStartOnBootOpt);
@ -162,8 +161,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
} }
Future<bool> checkAndUpdateIgnoreBatteryStatus() async { Future<bool> checkAndUpdateIgnoreBatteryStatus() async {
final res = final res = await AndroidPermissionManager.check(
await AndroidPermissionManager.check(kIgnoreBatteryOptimizations); kRequestIgnoreBatteryOptimizations);
if (_ignoreBatteryOpt != res) { if (_ignoreBatteryOpt != res) {
_ignoreBatteryOpt = res; _ignoreBatteryOpt = res;
return true; return true;
@ -305,8 +304,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
]), ]),
onToggle: (v) async { onToggle: (v) async {
if (v) { if (v) {
AndroidPermissionManager.startAction( await AndroidPermissionManager.request(
kActionRequestIgnoreBatteryOptimizations); kRequestIgnoreBatteryOptimizations);
} else { } else {
final res = await gFFI.dialogManager final res = await gFFI.dialogManager
.show<bool>((setState, close) => CustomAlertDialog( .show<bool>((setState, close) => CustomAlertDialog(
@ -332,17 +331,34 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
enhancementsTiles.add(SettingsTile.switchTile( enhancementsTiles.add(SettingsTile.switchTile(
initialValue: _enableStartOnBoot, initialValue: _enableStartOnBoot,
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text("$translate('Start on Boot') (beta)"), Text("${translate('Start on Boot')} (beta)"),
Text( Text(
'* ${translate('Start the screen recording service on boot, which requires special permissions')}', '* ${translate('Start the screen recording service on boot, which requires special permissions')}',
style: Theme.of(context).textTheme.bodySmall), style: Theme.of(context).textTheme.bodySmall),
]), ]),
onToggle: (v) async { onToggle: (toValue) async {
if (v) { if (toValue) {
// TODO // 1. request kIgnoreBatteryOptimizations
} else { if (!await AndroidPermissionManager.check(
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false); kRequestIgnoreBatteryOptimizations)) {
if (!await AndroidPermissionManager.request(
kRequestIgnoreBatteryOptimizations)) {
return;
}
}
// 2. request kSystemAlertWindow
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
if (!await AndroidPermissionManager.request(kSystemAlertWindow)) {
return;
}
}
// (Optional) 3. request input permission
} }
setState(() => _enableStartOnBoot = toValue);
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue);
})); }));
return SettingsList( return SettingsList(
@ -446,7 +462,6 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
} }
bool canStartOnBoot() { bool canStartOnBoot() {
// TODO need input
// start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
if (_hasIgnoreBattery && !_ignoreBatteryOpt) { if (_hasIgnoreBattery && !_ignoreBatteryOpt) {
return false; return false;