opt AndroidPermissionManager

This commit is contained in:
csf 2023-02-28 00:41:09 +09:00
parent 63185a5bcb
commit 8cd9f8745d
7 changed files with 85 additions and 101 deletions

View File

@ -88,6 +88,14 @@ class MainActivity : FlutterActivity() {
result.success(false)
}
}
START_ACTION -> {
if (call.arguments is String) {
startAction(context, call.arguments as String)
result.success(true)
} else {
result.success(false)
}
}
"check_video_permission" -> {
mainService?.let {
result.success(it.checkMediaPermission())

View File

@ -1,5 +1,6 @@
package com.carriez.flutter_hbb
import android.Manifest.permission.*
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
@ -35,6 +36,10 @@ const val REQ_REQUEST_MEDIA_PROJECTION = 201
// Activity responseCode
const val RES_FAILED = -100
// Flutter channel
const val START_ACTION = "start_action";
const val IGNORE_BATTERY_OPTIMIZATIONS = "ignore_battery_optimizations";
@SuppressLint("ConstantLocale")
val LOCAL_NAME = Locale.getDefault().toString()
@ -44,56 +49,10 @@ data class Info(
var width: Int, var height: Int, var scale: Int, var dpi: Int
)
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun testVP9Support(): Boolean {
return true
val res = MediaCodecList(MediaCodecList.ALL_CODECS)
.findEncoderForFormat(
MediaFormat.createVideoFormat(
MediaFormat.MIMETYPE_VIDEO_VP9,
SCREEN_INFO.width,
SCREEN_INFO.width
)
)
return res != null
}
@RequiresApi(Build.VERSION_CODES.M)
fun requestPermission(context: Context, type: String) {
val permission = when (type) {
"ignore_battery_optimizations" -> {
try {
context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:" + context.packageName)
})
} catch (e:Exception) {
e.printStackTrace()
}
return
}
"application_details_settings" -> {
try {
context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.parse("package:" + context.packageName)
})
} catch (e:Exception) {
e.printStackTrace()
}
return
}
"audio" -> {
Permission.RECORD_AUDIO
}
"file" -> {
Permission.MANAGE_EXTERNAL_STORAGE
}
else -> {
return
}
}
XXPermissions.with(context)
.permission(permission)
.permission(type)
.request { _, all ->
if (all) {
Handler(Looper.getMainLooper()).post {
@ -108,22 +67,23 @@ fun requestPermission(context: Context, type: String) {
@RequiresApi(Build.VERSION_CODES.M)
fun checkPermission(context: Context, type: String): Boolean {
val permission = when (type) {
"ignore_battery_optimizations" -> {
val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return pw.isIgnoringBatteryOptimizations(context.packageName)
}
"audio" -> {
Permission.RECORD_AUDIO
}
"file" -> {
Permission.MANAGE_EXTERNAL_STORAGE
}
else -> {
return false
}
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)
fun startAction(context: Context, action: String) {
try {
context.startActivity(Intent(action).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.parse("package:" + context.packageName)
})
} catch (e: Exception) {
e.printStackTrace()
}
return XXPermissions.isGranted(context, permission)
}
class AudioReader(val bufSize: Int, private val maxFrames: Int) {

View File

@ -907,21 +907,14 @@ class AccessibilityListener extends StatelessWidget {
}
}
class PermissionManager {
class AndroidPermissionManager {
static Completer<bool>? _completer;
static Timer? _timer;
static var _current = "";
static final permissions = [
"audio",
"file",
"ignore_battery_optimizations",
"application_details_settings"
];
static bool isWaitingFile() {
if (_completer != null) {
return !_completer!.isCompleted && _current == "file";
return !_completer!.isCompleted && _current == kManageExternalStorage;
}
return false;
}
@ -930,9 +923,6 @@ class PermissionManager {
if (isDesktop) {
return Future.value(true);
}
if (!permissions.contains(type)) {
return Future.error("Wrong permission!$type");
}
return gFFI.invokeMethod("check_permission", type);
}
@ -940,17 +930,16 @@ class PermissionManager {
if (isDesktop) {
return Future.value(true);
}
if (!permissions.contains(type)) {
return Future.error("Wrong permission!$type");
}
gFFI.invokeMethod("request_permission", type);
if (type == "ignore_battery_optimizations") {
// kIgnoreBatteryOptimizations permission doesn't depend on callback result, the result will be checked and updated on page resume
if (type == kIgnoreBatteryOptimizations) {
return Future.value(false);
}
_current = type;
_completer = Completer<bool>();
gFFI.invokeMethod("request_permission", type);
// timeout
_timer?.cancel();
@ -1484,8 +1473,8 @@ connect(BuildContext context, String id,
}
} else {
if (isFileTransfer) {
if (!await PermissionManager.check("file")) {
if (!await PermissionManager.request("file")) {
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
return;
}
}

View File

@ -59,11 +59,12 @@ const double kDesktopFileTransferMaximumWidth = 300;
const double kDesktopFileTransferRowHeight = 30.0;
const double kDesktopFileTransferHeaderHeight = 25.0;
EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && Platform.isLinux
? stateGlobal.fullscreen || stateGlobal.maximize
? EdgeInsets.zero
: EdgeInsets.all(5.0)
: EdgeInsets.zero;
EdgeInsets get kDragToResizeAreaPadding =>
!kUseCompatibleUiMode && Platform.isLinux
? stateGlobal.fullscreen || stateGlobal.maximize
? EdgeInsets.zero
: EdgeInsets.all(5.0)
: EdgeInsets.zero;
// https://en.wikipedia.org/wiki/Non-breaking_space
const int $nbsp = 0x00A0;
@ -136,6 +137,21 @@ const kRemoteAudioDualWay = 'dual-way';
const kIgnoreDpi = true;
/// Android constants
const kActionApplicationDetailsSettings =
"android.settings.APPLICATION_DETAILS_SETTINGS";
const kActionRequestIgnoreBatteryOptimizations =
"android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
const kRecordAudio = "android.permission.RECORD_AUDIO";
const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE";
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
const kStartAction = "start_action";
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
/// see [LogicalKeyboardKey.keyLabel]
const Map<int, String> logicalKeyMap = <int, String>{

View File

@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
import '../../consts.dart';
import '../../models/platform_model.dart';
import '../../models/server_model.dart';
import 'home_page.dart';
@ -150,10 +151,11 @@ class _ServerPageState extends State<ServerPage> {
}
void checkService() async {
gFFI.invokeMethod("check_service"); // jvm
// for Android 10/11,MANAGE_EXTERNAL_STORAGE permission from a system setting page
if (PermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
PermissionManager.complete("file", await PermissionManager.check("file"));
gFFI.invokeMethod("check_service");
// for Android 10/11, request MANAGE_EXTERNAL_STORAGE permission from system setting page
if (AndroidPermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
AndroidPermissionManager.complete(kManageExternalStorage,
await AndroidPermissionManager.check(kManageExternalStorage));
debugPrint("file permission finished");
}
}
@ -567,7 +569,7 @@ void androidChannelInit() {
{
var type = arguments["type"] as String;
var result = arguments["result"] as bool;
PermissionManager.complete(type, result);
AndroidPermissionManager.complete(type, result);
break;
}
case "on_media_projection_canceled":

View File

@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
import '../../common/widgets/login.dart';
import '../../consts.dart';
import '../../models/model.dart';
import '../../models/platform_model.dart';
import '../widgets/dialog.dart';
@ -133,7 +134,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
}
Future<bool> updateIgnoreBatteryStatus() async {
final res = await PermissionManager.check("ignore_battery_optimizations");
final res =
await AndroidPermissionManager.check(kIgnoreBatteryOptimizations);
if (_ignoreBatteryOpt != res) {
_ignoreBatteryOpt = res;
return true;
@ -265,7 +267,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
]),
onToggle: (v) async {
if (v) {
PermissionManager.request("ignore_battery_optimizations");
gFFI.invokeMethod(
kStartAction, kActionRequestIgnoreBatteryOptimizations);
} else {
final res = await gFFI.dialogManager
.show<bool>((setState, close) => CustomAlertDialog(
@ -282,7 +285,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
],
));
if (res == true) {
PermissionManager.request("application_details_settings");
gFFI.invokeMethod(
kStartAction, kActionApplicationDetailsSettings);
}
}
}));

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
@ -154,7 +155,8 @@ class ServerModel with ChangeNotifier {
/// file true by default (if permission on)
checkAndroidPermission() async {
// audio
if (androidVersion < 30 || !await PermissionManager.check("audio")) {
if (androidVersion < 30 ||
!await AndroidPermissionManager.check(kRecordAudio)) {
_audioOk = false;
bind.mainSetOption(key: "enable-audio", value: "N");
} else {
@ -163,7 +165,7 @@ class ServerModel with ChangeNotifier {
}
// file
if (!await PermissionManager.check("file")) {
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
_fileOk = false;
bind.mainSetOption(key: "enable-file-transfer", value: "N");
} else {
@ -229,8 +231,8 @@ class ServerModel with ChangeNotifier {
}
toggleAudio() async {
if (!_audioOk && !await PermissionManager.check("audio")) {
final res = await PermissionManager.request("audio");
if (!_audioOk && !await AndroidPermissionManager.check(kRecordAudio)) {
final res = await AndroidPermissionManager.request(kRecordAudio);
if (!res) {
// TODO handle fail
return;
@ -243,8 +245,10 @@ class ServerModel with ChangeNotifier {
}
toggleFile() async {
if (!_fileOk && !await PermissionManager.check("file")) {
final res = await PermissionManager.request("file");
if (!_fileOk &&
!await AndroidPermissionManager.check(kManageExternalStorage)) {
final res =
await AndroidPermissionManager.request(kManageExternalStorage);
if (!res) {
// TODO handle fail
return;
@ -561,7 +565,8 @@ class ServerModel with ChangeNotifier {
}
Future<void> closeAll() async {
await Future.wait(_clients.map((client) => bind.cmCloseConnection(connId: client.id)));
await Future.wait(
_clients.map((client) => bind.cmCloseConnection(connId: client.id)));
_clients.clear();
tabController.state.value.tabs.clear();
}