366 lines
10 KiB
Plaintext
366 lines
10 KiB
Plaintext
/**
|
|
* PermissionManager.uts
|
|
*
|
|
* Utility class for managing Android permissions throughout the app
|
|
* Handles requesting permissions, checking status, and directing users to settings
|
|
*/
|
|
|
|
/**
|
|
* Common permission types that can be requested
|
|
*/
|
|
export enum PermissionType {
|
|
BLUETOOTH = 'bluetooth',
|
|
LOCATION = 'location',
|
|
STORAGE = 'storage',
|
|
CAMERA = 'camera',
|
|
MICROPHONE = 'microphone',
|
|
NOTIFICATIONS = 'notifications',
|
|
CALENDAR = 'calendar',
|
|
CONTACTS = 'contacts',
|
|
SENSORS = 'sensors'
|
|
}
|
|
|
|
/**
|
|
* Result of a permission request
|
|
*/
|
|
type PermissionResult = {
|
|
granted: boolean;
|
|
grantedPermissions: string[];
|
|
deniedPermissions: string[];
|
|
}
|
|
|
|
/**
|
|
* Manages permission requests and checks throughout the app
|
|
*/
|
|
export class PermissionManager {
|
|
/**
|
|
* Maps permission types to the actual Android permission strings
|
|
*/
|
|
private static getPermissionsForType(type: PermissionType): string[] {
|
|
switch (type) {
|
|
case PermissionType.BLUETOOTH:
|
|
return [
|
|
'android.permission.BLUETOOTH_SCAN',
|
|
'android.permission.BLUETOOTH_CONNECT',
|
|
'android.permission.BLUETOOTH_ADVERTISE'
|
|
];
|
|
case PermissionType.LOCATION:
|
|
return [
|
|
'android.permission.ACCESS_FINE_LOCATION',
|
|
'android.permission.ACCESS_COARSE_LOCATION'
|
|
];
|
|
case PermissionType.STORAGE:
|
|
return [
|
|
'android.permission.READ_EXTERNAL_STORAGE',
|
|
'android.permission.WRITE_EXTERNAL_STORAGE'
|
|
];
|
|
case PermissionType.CAMERA:
|
|
return ['android.permission.CAMERA'];
|
|
case PermissionType.MICROPHONE:
|
|
return ['android.permission.RECORD_AUDIO'];
|
|
case PermissionType.NOTIFICATIONS:
|
|
return ['android.permission.POST_NOTIFICATIONS'];
|
|
case PermissionType.CALENDAR:
|
|
return [
|
|
'android.permission.READ_CALENDAR',
|
|
'android.permission.WRITE_CALENDAR'
|
|
];
|
|
case PermissionType.CONTACTS:
|
|
return [
|
|
'android.permission.READ_CONTACTS',
|
|
'android.permission.WRITE_CONTACTS'
|
|
];
|
|
case PermissionType.SENSORS:
|
|
return ['android.permission.BODY_SENSORS'];
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get appropriate display name for a permission type
|
|
*/
|
|
private static getPermissionDisplayName(type: PermissionType): string {
|
|
switch (type) {
|
|
case PermissionType.BLUETOOTH:
|
|
return '蓝牙';
|
|
case PermissionType.LOCATION:
|
|
return '位置';
|
|
case PermissionType.STORAGE:
|
|
return '存储';
|
|
case PermissionType.CAMERA:
|
|
return '相机';
|
|
case PermissionType.MICROPHONE:
|
|
return '麦克风';
|
|
case PermissionType.NOTIFICATIONS:
|
|
return '通知';
|
|
case PermissionType.CALENDAR:
|
|
return '日历';
|
|
case PermissionType.CONTACTS:
|
|
return '联系人';
|
|
case PermissionType.SENSORS:
|
|
return '身体传感器';
|
|
default:
|
|
return '未知权限';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a permission is granted
|
|
* @param type The permission type to check
|
|
* @returns True if the permission is granted, false otherwise
|
|
*/
|
|
static isPermissionGranted(type: PermissionType): boolean {
|
|
// #ifdef APP-ANDROID
|
|
try {
|
|
const permissions = this.getPermissionsForType(type);
|
|
const activity = UTSAndroid.getUniActivity();
|
|
|
|
if (activity == null || permissions.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
// Check each permission in the group
|
|
for (const permission of permissions) {
|
|
if (!UTSAndroid.checkSystemPermissionGranted(activity, [permission])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} catch (e) {
|
|
console.error(`Error checking ${type} permission:`, e);
|
|
return false;
|
|
}
|
|
// #endif
|
|
|
|
// #ifndef APP-ANDROID
|
|
return true; // Non-Android platforms don't need explicit permission checks
|
|
// #endif
|
|
}
|
|
|
|
/**
|
|
* Request a permission from the user
|
|
* @param type The permission type to request
|
|
* @param callback Function to call with the result of the permission request
|
|
* @param showRationale Whether to show a rationale dialog if permission was previously denied
|
|
*/
|
|
static requestPermission(
|
|
type: PermissionType,
|
|
callback: (result: PermissionResult) => void,
|
|
showRationale: boolean = true
|
|
): void {
|
|
// #ifdef APP-ANDROID
|
|
try {
|
|
const permissions = this.getPermissionsForType(type);
|
|
const activity = UTSAndroid.getUniActivity();
|
|
|
|
if (activity == null || permissions.length === 0) {
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: [],
|
|
deniedPermissions: permissions
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if already granted
|
|
let allGranted = true;
|
|
for (const permission of permissions) {
|
|
if (!UTSAndroid.checkSystemPermissionGranted(activity, [permission])) {
|
|
allGranted = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (allGranted) {
|
|
callback({
|
|
granted: true,
|
|
grantedPermissions: permissions,
|
|
deniedPermissions: []
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Request the permissions
|
|
UTSAndroid.requestSystemPermission(
|
|
activity,
|
|
permissions,
|
|
(granted: boolean, grantedPermissions: string[]) => {
|
|
if (granted) {
|
|
callback({
|
|
granted: true,
|
|
grantedPermissions: grantedPermissions,
|
|
deniedPermissions: []
|
|
});
|
|
} else if (showRationale) {
|
|
// Show rationale dialog
|
|
this.showPermissionRationale(type, callback);
|
|
} else {
|
|
// Just report the denial
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: grantedPermissions,
|
|
deniedPermissions: this.getDeniedPermissions(permissions, grantedPermissions)
|
|
});
|
|
}
|
|
},
|
|
(denied: boolean, deniedPermissions: string[]) => {
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: this.getGrantedPermissions(permissions, deniedPermissions),
|
|
deniedPermissions: deniedPermissions
|
|
});
|
|
}
|
|
);
|
|
} catch (e) {
|
|
console.error(`Error requesting ${type} permission:`, e);
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: [],
|
|
deniedPermissions: this.getPermissionsForType(type)
|
|
});
|
|
}
|
|
// #endif
|
|
|
|
// #ifndef APP-ANDROID
|
|
// Non-Android platforms don't need explicit permissions
|
|
callback({
|
|
granted: true,
|
|
grantedPermissions: this.getPermissionsForType(type),
|
|
deniedPermissions: []
|
|
});
|
|
// #endif
|
|
}
|
|
|
|
/**
|
|
* Show a rationale dialog explaining why the permission is needed
|
|
*/
|
|
private static showPermissionRationale(
|
|
type: PermissionType,
|
|
callback: (result: PermissionResult) => void
|
|
): void {
|
|
const permissionName = this.getPermissionDisplayName(type);
|
|
|
|
uni.showModal({
|
|
title: '权限申请',
|
|
content: `需要${permissionName}权限才能使用相关功能`,
|
|
confirmText: '去设置',
|
|
cancelText: '取消',
|
|
success: (result) => {
|
|
if (result.confirm) {
|
|
this.openAppSettings();
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: [],
|
|
deniedPermissions: this.getPermissionsForType(type)
|
|
});
|
|
} else {
|
|
callback({
|
|
granted: false,
|
|
grantedPermissions: [],
|
|
deniedPermissions: this.getPermissionsForType(type)
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Open the app settings page
|
|
*/
|
|
static openAppSettings(): void {
|
|
// #ifdef APP-ANDROID
|
|
try {
|
|
const context = UTSAndroid.getAppContext();
|
|
if (context != null) {
|
|
const intent = new android.content.Intent();
|
|
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
|
|
const uri = android.net.Uri.fromParts("package", context.getPackageName(), null);
|
|
intent.setData(uri);
|
|
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
context.startActivity(intent);
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to open app settings', e);
|
|
uni.showToast({
|
|
title: '请手动前往系统设置修改应用权限',
|
|
icon: 'none',
|
|
duration: 3000
|
|
});
|
|
}
|
|
// #endif
|
|
|
|
// #ifndef APP-ANDROID
|
|
uni.showToast({
|
|
title: '请在系统设置中管理应用权限',
|
|
icon: 'none',
|
|
duration: 2000
|
|
});
|
|
// #endif
|
|
}
|
|
|
|
/**
|
|
* Helper to get the list of granted permissions
|
|
*/
|
|
private static getGrantedPermissions(allPermissions: string[], deniedPermissions: string[]): string[] {
|
|
return allPermissions.filter(p => !deniedPermissions.includes(p));
|
|
}
|
|
|
|
/**
|
|
* Helper to get the list of denied permissions
|
|
*/
|
|
private static getDeniedPermissions(allPermissions: string[], grantedPermissions: string[]): string[] {
|
|
return allPermissions.filter(p => !grantedPermissions.includes(p));
|
|
}
|
|
|
|
/**
|
|
* Request multiple permission types at once
|
|
* @param types Array of permission types to request
|
|
* @param callback Function to call when all permissions have been processed
|
|
*/
|
|
static requestMultiplePermissions(
|
|
types: PermissionType[],
|
|
callback: (results: Map<PermissionType, PermissionResult>) => void
|
|
): void {
|
|
if (types.length === 0) {
|
|
callback(new Map());
|
|
return;
|
|
}
|
|
|
|
const results = new Map<PermissionType, PermissionResult>();
|
|
let remaining = types.length;
|
|
|
|
for (const type of types) {
|
|
this.requestPermission(
|
|
type,
|
|
(result) => {
|
|
results.set(type, result);
|
|
remaining--;
|
|
|
|
if (remaining === 0) {
|
|
callback(results);
|
|
}
|
|
},
|
|
true
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convenience method to request Bluetooth permissions
|
|
* @param callback Function to call after the permission request
|
|
*/
|
|
static requestBluetoothPermissions(callback: (granted: boolean) => void): void {
|
|
this.requestPermission(PermissionType.BLUETOOTH, (result) => {
|
|
// For Bluetooth, we also need location permissions on Android
|
|
if (result.granted) {
|
|
this.requestPermission(PermissionType.LOCATION, (locationResult) => {
|
|
callback(locationResult.granted);
|
|
});
|
|
} else {
|
|
callback(false);
|
|
}
|
|
});
|
|
}
|
|
} |