diff --git a/AppScope/app.json b/AppScope/app.json index cb1e2bf9dfc46138544831ba1ad9322296210183..1953285597165353e69ae120267e5d182fe30a10 100644 --- a/AppScope/app.json +++ b/AppScope/app.json @@ -4,14 +4,10 @@ "app": { "bundleName": "com.ohos.permissionmanager", "vendor": "example", - "versionCode": 1000076, - "versionName": "1.7.6", + "versionCode": 1000080, + "versionName": "1.8.0", "icon": "$media:app_icon", "label": "$string:app_name", - "minAPIVersion": 14, - "targetAPIVersion": 14, - "distributedNotificationEnabled": true, - "apiReleaseType": "Beta5", "configuration": "$profile:configuration" } } diff --git a/AppScope/app.json5 b/AppScope/app.json5 index fb84c462a43e807ffcf8d90dfc61474bb8de3d38..eedb4583c8817a30c3b51b3f48c53fa51ccd240b 100644 --- a/AppScope/app.json5 +++ b/AppScope/app.json5 @@ -16,14 +16,10 @@ "app": { "bundleName": "com.ohos.permissionmanager", "vendor": "example", - "versionCode": 1000076, - "versionName": "1.7.6", + "versionCode": 1000080, + "versionName": "1.8.0", "icon": "$media:app_icon", "label": "$string:app_name", - "minAPIVersion": 14, - "targetAPIVersion": 14, - "distributedNotificationEnabled": true, - "apiReleaseType": "Beta5", "configuration": "$profile:configuration" } } diff --git a/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogIntent.ets b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogIntent.ets new file mode 100644 index 0000000000000000000000000000000000000000..c715cca1ce0571456c851e72240178e12b8f2ab8 --- /dev/null +++ b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogIntent.ets @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseIntent } from '../common/base/BaseIntent'; +import { ButtonStatus } from '../common/model/definition'; +import { PermissionGroupConfig, CallerAppInfo } from '../common/model/typedef'; +import common from '@ohos.app.ability.common'; + +export namespace GrantDialogIntent { + + export class InitIntent extends BaseIntent { + public context: common.ServiceExtensionContext; + + constructor(context: common.ServiceExtensionContext) { + super(); + this.context = context; + } + + public getIntentTag(): string { + return 'InitIntent'; + } + } + + export class RefreshIntent extends BaseIntent { + public context: common.ServiceExtensionContext; + public callerAppInfo: CallerAppInfo; + + constructor(context: common.ServiceExtensionContext, callerAppInfo: CallerAppInfo) { + super(); + this.context = context; + this.callerAppInfo = callerAppInfo; + } + + public getIntentTag(): string { + return 'RefreshIntent'; + } + } + + export class ClickIntent extends BaseIntent { + public context: common.ServiceExtensionContext; + public groupConfig: PermissionGroupConfig; + public callerAppInfo: CallerAppInfo; + public buttonStatus: ButtonStatus; + + constructor( + context: common.ServiceExtensionContext, + groupConfig: PermissionGroupConfig, + callerAppInfo: CallerAppInfo, + buttonStatus: ButtonStatus + ) { + super(); + this.context = context; + this.groupConfig = groupConfig; + this.callerAppInfo = callerAppInfo; + this.buttonStatus = buttonStatus; + } + + public getIntentTag(): string { + return 'ClickIntent'; + } + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogModel.ets b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..619499a7bb2b5a154561f536851d4aad7fbe631c --- /dev/null +++ b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogModel.ets @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseModel } from '../common/base/BaseModel'; +import { Want } from '@kit.AbilityKit'; +import { GlobalContext } from '../common/utils/globalContext'; +import { Permission, PermissionGroup, ButtonStatus, PermissionOption } from '../common/model/definition'; +import { Log, titleTrim, supportPermission } from '../common/utils/utils'; +import { + Property, CallerAppInfo, PermissionGroupConfig, PermissionWithOption, optionAndState +} from '../common/model/typedef'; +import { PermissionGroupManager } from '../common/permissionGroupManager/PermissionGroupManager'; +import rpc from '@ohos.rpc'; +import window from '@ohos.window'; +import display from '@ohos.display'; +import pasteboard from '@ohos.pasteboard'; +import common from '@ohos.app.ability.common'; +import osAccount from '@ohos.account.osAccount'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import bundleManager from '@ohos.bundle.bundleManager'; +import bundleResourceManager from '@ohos.bundle.bundleResourceManager'; +import Constants from '../common/utils/constant'; + +const BG_COLOR = '#00000000'; +const THIS_TIME_FLAG = [ButtonStatus.THIS_TIME_ONLY, ButtonStatus.ALLOW_THIS_TIME]; +const DEFAULT_CALLER_APP_INFO: CallerAppInfo = { + bundleName: '', + tokenId: -1, + reqPerms: [], + reqPermsState: [], + reqPermsDetails: [], + proxy: {} as rpc.RemoteObject, + grantResult: [], + groupWithPermission: new Map>() +} + +export class GrantDialogModel extends BaseModel { + + /** + * 从want解析调用方信息和权限申请情况 + * @param want + * return 应用包信息 + */ + public async getCallerAppInfo(want: Want): Promise { + if (!want.parameters) { + Log.error(`want.parameters is undefined!`); + return DEFAULT_CALLER_APP_INFO; + } + let bundleName: string = want.parameters['ohos.aafwk.param.callerBundleName'] as string ?? ''; + let tokenId: number = want.parameters['ohos.aafwk.param.callerToken'] as number ?? -1; + let reqPerms: Permission[] = [...(want.parameters['ohos.user.grant.permission'] as Permission[])] ?? []; + let reqPermsState: number[] = [...(want.parameters['ohos.user.grant.permission.state'] as number[])] ?? []; + let reqPermsDetails: bundleManager.ReqPermissionDetail[] = []; + let callback: Property = want.parameters['ohos.ability.params.callback'] as Property; + let proxy: rpc.RemoteObject = callback.value as rpc.RemoteObject; + try { + let userId: number = await this.getUserId(); + let flag: number = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; + let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoSync(bundleName, flag, userId); + reqPermsDetails = bundleInfo.reqPermissionDetails ?? []; + } catch (error) { + Log.error(`getBundleInfo faild, code: ${error.code}, message: ${error.message}.`); + } + let grantResult: number[] = this.initGrantResult(reqPermsState); + await this.preProcessPermission(reqPerms, reqPermsState, tokenId, grantResult); + let callerAppInfo: CallerAppInfo = { + bundleName, + tokenId, + reqPerms, + reqPermsState, + reqPermsDetails, + proxy, + grantResult, + groupWithPermission: this.getGroupWithPermission(reqPerms, reqPermsState) + } + return callerAppInfo; + } + + /** + * 获取当前用户id + * return 用户id + */ + private async getUserId(): Promise { + let userId: number = 0; + try { + const accountManager: osAccount.AccountManager = osAccount.getAccountManager(); + userId = await accountManager.getForegroundOsAccountLocalId(); + } catch (error) { + Log.error(`getForegroundOsAccountLocalId faild, code: ${error.code}, message: ${error.message}.`); + } + return userId; + } + + /** + * 初始化授权状态列表 + * @param reqPermsState 权限状态 + * return 授权状态列表 + */ + private initGrantResult(reqPermsState: number[]): number[] { + let grantResult: number[] = new Array(reqPermsState.length).fill(abilityAccessCtrl.GrantStatus.PERMISSION_DENIED); + reqPermsState.forEach((state, idx) => { + if (state === Constants.PASS_OPER) { + grantResult[idx] = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; + } + }) + return grantResult; + } + + /** + * 预处理待授权权限 + * @param reqPerms 授权权限 + * @param reqPermsState 权限状态 + * @param tokenId 应用token + * @param grantResult 授权状态 + * return + */ + private async preProcessPermission( + reqPerms: Permission[], reqPermsState: number[], tokenId: number, grantResult: number[] + ): Promise { + let allGroupConfigs: PermissionGroupConfig[] = PermissionGroupManager.getInstance().getAllGroupConfigs(); + for (let index = 0; index < reqPerms.length; index++) { + if (!this.needHandlePermission(reqPerms[index], reqPermsState[index])) { + continue; + } + let group = PermissionGroupManager.getInstance().getGroupNameByPermission(reqPerms[index], allGroupConfigs); + let result = await PermissionGroupManager.getInstance().preProcessPermission( + reqPerms[index], group as PermissionGroup, tokenId + ); + this.preProcessState(reqPerms, reqPermsState, grantResult, result); + } + } + + /** + * 初始化授权状态列表 + * @param reqPerms 授权权限 + * @param reqPermsState 权限状态 + * @param grantResult 授权状态 + * @param result 授权结果 + * return + */ + private preProcessState( + reqPerms: Permission[], reqPermsState: number[], grantResult: number[], result: PermissionWithOption[] + ): void { + result.forEach(permissionWithOption => { + let needRefresh = reqPerms.find(perm => perm === permissionWithOption.permission); + if (!needRefresh) { + return; + } + for (let index = 0; index < reqPerms.length; index++) { + if (reqPerms[index] === permissionWithOption.permission) { + reqPermsState[index] = permissionWithOption.permissionOption === PermissionOption.GRANT ? + Constants.PASS_OPER : Constants.SETTING_OPER; + grantResult[index] = reqPermsState[index]; + } + } + }) + } + + /** + * 获取待授权权限及其所属权限组 + * @param reqPerms 权限 + * @param reqPermsState 权限状态 + * return key:权限组,value:待授权权限 + */ + private getGroupWithPermission( + reqPerms: Permission[], reqPermsState: number[] + ): Map> { + if (reqPerms.length !== reqPermsState.length) { + return new Map>(); + } + let groupWithPermission: Map> = new Map(); + let allGroupConfigs: PermissionGroupConfig[] = PermissionGroupManager.getInstance().getAllGroupConfigs(); + for (let index = 0; index < reqPerms.length; index++) { + let perm = reqPerms[index]; + if (this.needHandlePermission(perm, reqPermsState[index])) { + let groupName = PermissionGroupManager.getInstance().getGroupNameByPermission(perm, allGroupConfigs); + if (!groupName) { + continue; + } + if (!groupWithPermission.has(groupName)) { + groupWithPermission.set(groupName, new Set([perm])); + } else { + groupWithPermission.get(groupName)?.add(perm); + } + } + } + return groupWithPermission; + } + + /** + * 判断权限是否为当前设备所支持权限 且状态为待授权 + * @param permission 权限 + * @param state 权限初始状态 + * return + */ + private needHandlePermission(permission: Permission, state: number): boolean { + let supportPermissions = supportPermission(); + if (supportPermissions.includes(permission) && state === Constants.DYNAMIC_OPER) { + return true; + } else { + return false; + } + } + + /** + * 创建模态窗口 + * @param context ServiceExtensionContext + * @param name 窗口名 + * @param rect 窗口参数 + * @param want + * return + */ + public async createWindow( + context: common.ServiceExtensionContext, name: string, rect: display.Rect, want: Want + ): Promise { + try { + let configuration: window.Configuration = { + ctx: context, + name, + windowType: window.WindowType.TYPE_DIALOG + } + const win = await window.createWindow(configuration); + Log.info('createWindow end.'); + let callerAppInfo: CallerAppInfo = await this.getCallerAppInfo(want); + let property: Record = { 'win': win, 'callerAppInfo': callerAppInfo }; + let storage: LocalStorage = new LocalStorage(property); + await this.BindDialogTarget(context, win, want); + Log.info('bindDialogTarget end.'); + await win.moveWindowTo(rect.left, rect.top); + Log.info('moveWindowTo end.'); + await win.resize(rect.width, rect.height); + Log.info('resize end.'); + await win.loadContent('pages/dialogPlus', storage); + win.setWindowBackgroundColor(BG_COLOR); + await win.showWindow(); + Log.info('showWindow end.'); + let windowNum: number = GlobalContext.getContext().increaseAndGetWindowNum(); + Log.info(`global windowNum is: ${windowNum}.`); + } catch (error) { + Log.error(`window create faild, code: ${error.code}, message: ${error.message}.`); + } + } + + /** + * 绑定模态窗口 + * @param context ServiceExtensionContext + * @param win 窗口对象 + * @param want + * return + */ + private async BindDialogTarget( + context: common.ServiceExtensionContext, win: window.Window, want: Want + ): Promise { + let callback = want.parameters?.['ohos.ability.params.callback'] as Property; + let proxy = callback.value as rpc.RemoteObject; + let token = want.parameters?.['ohos.ability.params.token'] as Property; + win.bindDialogTarget(token.value, () => { + let option = new rpc.MessageOption(); + let data = new rpc.MessageSequence(); + let reply = new rpc.MessageSequence(); + try { + data.writeInterfaceToken(Constants.ACCESS_TOKEN); + proxy.sendMessageRequest(Constants.RESULT_CODE_1, data, reply, option); + } catch (err) { + Log.error(`write result failed: ${JSON.stringify(err)}`); + } finally { + data.reclaim(); + reply.reclaim(); + } + let windowNum: number = GlobalContext.getContext().decreaseAndGetWindowNum(); + Log.info(`global windowNum is: ${windowNum}.`); + win.destroyWindow(); + windowNum <= 0 ? context.terminateSelf() : null; + }); + } + + /** + * 获取剪贴板信息 + * return 剪贴板内容来源应用名 + */ + public getPasteBoardInfo(): string { + let systemPasteboardDataSource: string = ''; + try { + let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); + let data: string = systemPasteboard.getDataSource(); + systemPasteboardDataSource = data || ''; + } catch (error) { + Log.error(`getSystemPasteboard faild, code: ${error.code}, message: ${error.message}.`); + } + Log.info(`systemPasteboard dataSource: ${systemPasteboardDataSource}.`); + return systemPasteboardDataSource; + } + + /** + * 获取应用名 + * @param bundleName 包名 + * return 应用名 + */ + public getAppName(bundleName: string): string { + let resourceFlag: bundleResourceManager.ResourceFlag = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_ALL; + let appName: string = ''; + try { + let resourceInfo: bundleResourceManager.BundleResourceInfo = + bundleResourceManager.getBundleResourceInfo(bundleName, resourceFlag); + appName = resourceInfo?.label || ''; + } catch (error) { + Log.error(`getBundleResourceInfo faild, code: ${error.code}, message: ${error.message}.`); + appName = 'Application'; + } + Log.info(`appName: ${appName}.`); + return titleTrim(appName); + } + + /** + * 初始化位置权限组授权信息 + * @param callerAppInfo 调用方信息 + * return flag: 0:不授权,1:只申请模糊权限,2:模糊升级为精确,3:模糊+精确,开启精确,4:模糊+精确,关闭精确 + */ + public initLocationFlag(callerAppInfo: CallerAppInfo): number { + let locationFlag: number = Constants.LOCATION_NONE; + let hasFuzz: boolean = callerAppInfo.reqPerms.includes(Permission.APPROXIMATELY_LOCATION); + let hasPrecise: boolean = callerAppInfo.reqPerms.includes(Permission.LOCATION); + + if (hasFuzz) { + locationFlag = Constants.LOCATION_FUZZY; + if (hasPrecise) { + locationFlag = Constants.LOCATION_BOTH_PRECISE; + let fuzzyIndex = callerAppInfo.reqPerms.indexOf(Permission.APPROXIMATELY_LOCATION); + if (callerAppInfo.reqPermsState[fuzzyIndex] === Constants.PASS_OPER) { + locationFlag = Constants.LOCATION_UPGRADE; + } + } + } else { + if (hasPrecise) { + locationFlag = Constants.LOCATION_UPGRADE; + } + } + return locationFlag; + } + + /** + * 获取所有授权权限组 + * @param callerAppInfo 调用方信息 + * @param context 应用上下文 + * @param appName 应用名 + * @param locationFlag 位置权限组flag + * @param pasteBoardName 剪贴板信息 + * return + */ + public getGrantGroups( + callerAppInfo: CallerAppInfo, + context: common.ServiceExtensionContext, + appName: string, + locationFlag: number, + pasteBoardName: string + ): PermissionGroupConfig[] { + return PermissionGroupManager.getInstance().getGroupConfigs( + callerAppInfo, context, appName, locationFlag, pasteBoardName + ); + } + + /** + * 点击事件处理 + * @param groupConfig + * @param callerAppInfo 调用方信息 + * @param locationFlag 位置权限组flag + * @param buttonStatus 点击状态 + * return + */ + public async clickHandle( + groupConfig: PermissionGroupConfig, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): Promise { + let flag: number = + THIS_TIME_FLAG.includes(buttonStatus) ? Constants.PERMISSION_ALLOW_THIS_TIME : Constants.PERMISSION_FLAG; + Log.info(`clickHandle, group: ${groupConfig.groupName}, buttonStatus: ${buttonStatus}.`); + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + let reportPermissions: Permission[] = []; + let permissionWithOptionList: PermissionWithOption[] = []; + + for (let permission of groupConfig.permissions) { + let result: optionAndState = { + operationResult: Constants.RESULT_SUCCESS, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED + }; + let permissionOption = PermissionGroupManager.getInstance().getPermissionOption( + permission, groupConfig.groupName, callerAppInfo, locationFlag, buttonStatus + ); + let permissionWithOption: PermissionWithOption = { permission, permissionOption }; + if (permissionOption !== PermissionOption.SKIP) { + permissionWithOptionList.push(permissionWithOption); + } + if (permissionOption === PermissionOption.GRANT) { + result = await this.grantPermissionWithResult(permission, flag, callerAppInfo.tokenId, atManager); + } + if (permissionOption === PermissionOption.REVOKE) { + result = await this.revokePermissionWithResult(permission, flag, callerAppInfo.tokenId, atManager); + } + Log.info(`permission: ${permission}, opt: ${permissionOption}, result: ${result.operationResult}.`); + if (result.operationResult === Constants.RESULT_SUCCESS && permissionOption !== PermissionOption.SKIP) { + reportPermissions.push(permission); + this.writeToGrantResult(permission, callerAppInfo, result.permissionState); + } + } + } + + /** + * 授予权限并返回操作结果 + * @param permission 操作权限 + * @param flag 授权flag + * @param tokenId 应用token + * @param atManager atManager对象 + * return + */ + private async grantPermissionWithResult( + permission: Permission, flag: number, tokenId: number, atManager: abilityAccessCtrl.AtManager + ): Promise { + try { + await atManager.grantUserGrantedPermission(tokenId, permission, flag); + Log.info(`grant permission success, permission: ${permission}.`); + return { + operationResult: Constants.RESULT_SUCCESS, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED + } + } catch (error) { + Log.error(`grant permission faild, permission: ${permission}, code: ${error.code}, message: ${error.message}.`); + return { + operationResult: Constants.RESULT_FAILURE, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED + } + } + } + + /** + * 撤销权限并返回操作结果 + * @param permission 操作权限 + * @param flag 授权flag + * @param tokenId 应用token + * @param atManager atManager对象 + * return + */ + private async revokePermissionWithResult( + permission: Permission, flag: number, tokenId: number, atManager: abilityAccessCtrl.AtManager + ): Promise { + try { + await atManager.revokeUserGrantedPermission(tokenId, permission, flag); + Log.info(`revoke permission success, permission: ${permission}.`); + return { + operationResult: Constants.RESULT_SUCCESS, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED + } + } catch (error) { + Log.error(`revoke permission faild, permission: ${permission}, code: ${error.code}, message: ${error.message}.`); + return { + operationResult: Constants.RESULT_FAILURE, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED + } + } + } + + /** + * 授权或撤销成功后,将结果写入grantResult + * @param permission 权限 + * @param callerAppInfo 调用方信息 + * @param state 权限状态 + * return + */ + public writeToGrantResult( + permission: Permission, callerAppInfo: CallerAppInfo, state: abilityAccessCtrl.GrantStatus + ): void { + callerAppInfo.reqPerms.forEach((reqPerm, index) => { + if (reqPerm === permission) { + callerAppInfo.grantResult[index] = state; + } + }) + } + + /** + * 销毁模态窗口 + * @param context ServiceExtensionContext + * @param win 窗口对象 + * @param callerAppInfo 调用方信息 + * return + */ + public async terminateWithResult( + context: common.ServiceExtensionContext, win: window.Window, callerAppInfo: CallerAppInfo + ): Promise { + Log.info(`Perms: ${JSON.stringify(callerAppInfo.reqPerms)}, result: ${JSON.stringify(callerAppInfo.grantResult)}.`); + let option = new rpc.MessageOption(); + let data = new rpc.MessageSequence(); + let reply = new rpc.MessageSequence(); + let setDialogData = new rpc.MessageSequence(); + try { + data.writeInterfaceToken(Constants.ACCESS_TOKEN); + data.writeStringArray(callerAppInfo.reqPerms); + data.writeIntArray(callerAppInfo.grantResult); + setDialogData.writeInterfaceToken(Constants.ACCESS_TOKEN); + callerAppInfo.proxy.sendMessageRequest(Constants.RESULT_CODE, data, reply, option); + callerAppInfo.proxy.sendMessageRequest(Constants.RESULT_CODE_1, setDialogData, reply, option); + } catch (error) { + Log.error(`terminateWithResult faild, code: ${error.code}, message: ${error.message}.`); + } finally { + data.reclaim(); + reply.reclaim(); + setDialogData.reclaim(); + let windowNum: number = GlobalContext.getContext().decreaseAndGetWindowNum(); + Log.info(`global windowNum is: ${windowNum}.`); + win.destroyWindow(); + windowNum <= 0 ? context.terminateSelf() : null; + } + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewModel.ets b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..59286be47a8508446d7b68225b8637e4a279afaf --- /dev/null +++ b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewModel.ets @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseViewModel, ProcessResult } from '../common/base/BaseViewModel'; +import { BaseIntent } from '../common/base/BaseIntent'; +import { GrantDialogModel } from './GrantDialogModel'; +import { GrantDialogIntent } from './GrantDialogIntent'; +import { GrantDialogViewState } from './GrantDialogViewState'; +import { Permission } from '../common/model/definition'; +import { CallerAppInfo } from '../common/model/typedef'; +import { Log } from '../common/utils/utils'; +import window from '@ohos.window'; + +export class GrantDialogViewModel extends BaseViewModel { + private callerAppInfo: CallerAppInfo; + private win: window.Window; + + constructor(callerAppInfo: CallerAppInfo, win: window.Window) { + super(); + this.callerAppInfo = callerAppInfo; + this.win = win; + } + + private async processInitIntent( + viewState: GrantDialogViewState, model: GrantDialogModel, intention: GrantDialogIntent.InitIntent + ): Promise { + if (this.callerAppInfo.reqPerms.includes(Permission.READ_PASTEBOARD)) { + viewState.pasteBoardName = model.getPasteBoardInfo(); + } + viewState.curIndex = 0; + viewState.appName = model.getAppName(this.callerAppInfo.bundleName); + viewState.locationFlag = model.initLocationFlag(this.callerAppInfo); + viewState.grantGroups = model.getGrantGroups( + this.callerAppInfo, intention.context, viewState.appName, viewState.locationFlag, viewState.pasteBoardName + ); + if (viewState.grantGroups.length === 0) { + Log.error(`grantGroups length is 0, terminate.`); + model.terminateWithResult(intention.context, this.win, this.callerAppInfo); + } + viewState.grantGroups.forEach((groupConfig) => { + Log.info(`group: ${groupConfig.groupName}.`); + if (groupConfig.title === '') { + Log.error(`get resource faild, terminate.`); + model.terminateWithResult(intention.context, this.win, this.callerAppInfo); + } + }) + return ProcessResult.SUCCESS; + } + + private async processRefreshIntent( + viewState: GrantDialogViewState, model: GrantDialogModel, intention: GrantDialogIntent.RefreshIntent + ): Promise { + if (this.callerAppInfo.reqPerms.includes(Permission.READ_PASTEBOARD)) { + viewState.pasteBoardName = model.getPasteBoardInfo(); + } + viewState.appName = model.getAppName(intention.callerAppInfo.bundleName); + viewState.grantGroups = model.getGrantGroups( + intention.callerAppInfo, intention.context, viewState.appName, viewState.locationFlag, viewState.pasteBoardName + ); + return ProcessResult.SUCCESS; + } + + private async processClickIntent( + viewState: GrantDialogViewState, model: GrantDialogModel, intention: GrantDialogIntent.ClickIntent + ): Promise { + await model.clickHandle( + intention.groupConfig, intention.callerAppInfo, viewState.locationFlag, intention.buttonStatus + ); + if (viewState.curIndex === viewState.grantGroups.length - 1) { + let timer: number = setTimeout(() => { + model.terminateWithResult(intention.context, this.win, intention.callerAppInfo); + clearTimeout(timer); + }, 200); + return ProcessResult.SUCCESS; + } else { + let nextIndex = viewState.curIndex + 1; + viewState.curIndex = nextIndex >= viewState.grantGroups.length ? viewState.grantGroups.length - 1 : nextIndex; + } + return ProcessResult.SUCCESS; + } + + protected initModel(): GrantDialogModel { + return new GrantDialogModel(); + } + + protected initViewState(): GrantDialogViewState { + return new GrantDialogViewState(); + } + + protected async processIntentWithModel( + intention: BaseIntent, model: GrantDialogModel, viewState: GrantDialogViewState + ): Promise { + if (intention instanceof GrantDialogIntent.InitIntent) { + return await this.processInitIntent(viewState, model, intention); + } + if (intention instanceof GrantDialogIntent.RefreshIntent) { + return await this.processRefreshIntent(viewState, model, intention); + } + if (intention instanceof GrantDialogIntent.ClickIntent) { + return await this.processClickIntent(viewState, model, intention); + } + return ProcessResult.FAIL; + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewState.ets b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewState.ets new file mode 100644 index 0000000000000000000000000000000000000000..578bd98e70605e8b283336cd13f2128a74e6b009 --- /dev/null +++ b/permissionmanager/src/main/ets/ServiceExtAbility/GrantDialogViewState.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseState } from '../common/base/BaseState'; +import { PermissionGroupConfig } from '../common/model/typedef'; +import Constants from '../common/utils/constant'; + +@Observed +export class GrantDialogViewState extends BaseState { + // 授权应用名 + public appName: string | undefined = ''; + + // 授权权限组列表 + public grantGroups: PermissionGroupConfig[] = []; + + // 当前展示的权限组索引 + public curIndex: number = 0; + + // 剪贴板内容来源应用名 + public pasteBoardName: string = ''; + + // 位置权限组状态 + public locationFlag: number = Constants.LOCATION_NONE; + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ts b/permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ets similarity index 30% rename from permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ts rename to permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ets index 7493a617909b10c0d7e4bf1c9248ed24c5884dad..fc29cac3e95a0569723f9d804d59620c3e20e978 100644 --- a/permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ts +++ b/permissionmanager/src/main/ets/ServiceExtAbility/ServiceExtAbility.ets @@ -14,52 +14,47 @@ */ import extension from '@ohos.app.ability.ServiceExtensionAbility'; -import window from '@ohos.window'; import display from '@ohos.display'; -import rpc from '@ohos.rpc'; -import { GlobalContext } from '../common/utils/globalContext'; -import dialogRequest from '@ohos.app.ability.dialogRequest'; import deviceInfo from '@ohos.deviceInfo'; +import { Want } from '@kit.AbilityKit'; +import { Log } from '../common/utils/utils'; +import { GrantDialogModel } from './GrantDialogModel'; +import { GlobalContext } from '../common/utils/globalContext'; -const TAG = 'PermissionManager_Log: '; -const BG_COLOR = '#00000000'; -const DEFAULT_CORNER_RADIUS_L = 16; -const RESULT_CODE_1 = 1; -const ACCESS_TOKEN = 'ohos.security.accesstoken.tokencallback'; +let grantDialogModel: GrantDialogModel = new GrantDialogModel(); export default class ServiceExtensionAbility extends extension { /** * Lifecycle function, called back when a service extension is started for initialization. */ - onCreate(want): void { - console.info(TAG + 'ServiceExtensionAbility onCreate, ability name is ' + want.abilityName); - - globalThis.windowNum = 0; + onCreate(want: Want): void { + Log.info('ServiceExtensionAbility onCreate, ability name is ' + want.abilityName); + GlobalContext.getContext().setAndGetWindowNum(0); } /** * Lifecycle function, called back when a service extension is started or recall. */ - onRequest(want, startId): void { - console.info(TAG + 'ServiceExtensionAbility onRequest. start id is ' + startId); - console.info(TAG + 'want: ' + JSON.stringify(want)); + onRequest(want: Want, startId: number): void { + Log.info('ServiceExtensionAbility onRequest. start id is ' + startId); if (deviceInfo.deviceType === 'wearable') { this.context.terminateSelf(); - console.info(TAG + 'ServiceExtensionAbility terminateSelf'); + Log.info('ServiceExtensionAbility terminateSelf'); return; } try { let dis = display.getDefaultDisplaySync(); - let navigationBarRect = { + let navigationBarRect: display.Rect = { left: 0, top: 0, width: dis.width, height: dis.height }; - this.createWindow('permissionDialog' + startId, window.WindowType.TYPE_DIALOG, navigationBarRect, want); + Log.info('want: ' + JSON.stringify(want)); + grantDialogModel.createWindow(this.context, 'permissionDialog' + startId, navigationBarRect, want); } catch (exception) { - console.error(TAG + 'Failed to obtain the default display object. Code: ' + JSON.stringify(exception)); + Log.error('Failed to obtain the default display object. Code: ' + JSON.stringify(exception)); }; } @@ -67,66 +62,7 @@ export default class ServiceExtensionAbility extends extension { * Lifecycle function, called back before a service extension is destroyed. */ onDestroy(): void { - console.info(TAG + 'ServiceExtensionAbility onDestroy.'); + Log.info('ServiceExtensionAbility onDestroy.'); } - private async createWindow(name: string, windowType, rect, want): Promise { - let requestInfo: dialogRequest.RequestInfo; - try { - requestInfo = dialogRequest.getRequestInfo(want); - } catch (err) { - console.error(`getRequestInfo err= ${JSON.stringify(err)}`); - } - - console.info(TAG + 'create window start, requestInfo: ' + JSON.stringify(requestInfo)); - let rectInfo = requestInfo ? requestInfo.windowRect : rect; - rectInfo = rectInfo.width === 0 ? rect : rectInfo; - try { - const win = await window.createWindow({ ctx: this.context, name, windowType }); - console.info(TAG + 'createWindow end.'); - let storage: LocalStorage = new LocalStorage({ 'want': want, 'win': win }); - await this.BindDialogTarget(win, want); - console.info(TAG + 'bindDialogTarget end.'); - await win.moveWindowTo(rectInfo.left, rectInfo.top); - console.info(TAG + 'moveWindowTo end.'); - await win.resize(rectInfo.width, rectInfo.height); - console.info(TAG + 'resize end.'); - await win.loadContent('pages/dialogPlus', storage); - win.setWindowBackgroundColor(BG_COLOR); - if (rectInfo.width < rect.width) { - win.setCornerRadius(DEFAULT_CORNER_RADIUS_L); - } - await win.showWindow(); - console.info(TAG + 'showWindow end.'); - globalThis.windowNum ++; - GlobalContext.store('windowNum', globalThis.windowNum); - } catch (err) { - console.error(TAG + `window create failed! err: ${JSON.stringify(err)}`); - } - } - - private async BindDialogTarget(win, want): Promise { - let proxy = want.parameters['ohos.ability.params.callback'].value; - win.bindDialogTarget(want.parameters['ohos.ability.params.token'].value, () => { - let option = new rpc.MessageOption(); - let data = new rpc.MessageSequence(); - let reply = new rpc.MessageSequence(); - try { - data.writeInterfaceToken(ACCESS_TOKEN); - proxy.sendMessageRequest(RESULT_CODE_1, data, reply, option); - } catch (err) { - console.error(TAG + `write result failed: ${JSON.stringify(err)}`); - } finally { - data.reclaim(); - reply.reclaim(); - } - let windowNum = GlobalContext.load('windowNum'); - windowNum --; - GlobalContext.store('windowNum', windowNum); - win.destroyWindow(); - if (windowNum === 0) { - this.context.terminateSelf(); - } - }); - } }; \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/base/BaseIntent.ets b/permissionmanager/src/main/ets/common/base/BaseIntent.ets new file mode 100644 index 0000000000000000000000000000000000000000..84a98a92f00ba8a8ae7c5f2433e0a719a32e6512 --- /dev/null +++ b/permissionmanager/src/main/ets/common/base/BaseIntent.ets @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export abstract class BaseIntent { + public abstract getIntentTag(): string; +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/base/BaseModel.ets b/permissionmanager/src/main/ets/common/base/BaseModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..1e397dbd2c1727b97628792238a41da5f697e110 --- /dev/null +++ b/permissionmanager/src/main/ets/common/base/BaseModel.ets @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class BaseModel { +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/base/BasePermissionStrategy.ets b/permissionmanager/src/main/ets/common/base/BasePermissionStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..98dfa78da604557210734b9ea78afc171b73e8e3 --- /dev/null +++ b/permissionmanager/src/main/ets/common/base/BasePermissionStrategy.ets @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Permission, ButtonStatus, PermissionOption } from '../model/definition'; +import { PermissionGroupConfig, CallerAppInfo, PermissionWithOption } from '../model/typedef'; +import { Log } from '../utils/utils'; +import common from '@ohos.app.ability.common'; + +export abstract class BasePermissionStrategy { + /** + * 获取权限组配置 + * return 权限组配置 + */ + public abstract getPermissionGroupConfig(): PermissionGroupConfig; + + /** + * 获取权限组标题 + * @param appName 应用名 + * @param locationFlag 位置权限状态 + * return 标题 + */ + public abstract getGroupTitle(appName: string, locationFlag: number): ResourceStr; + + /** + * 获取读写信息 + * @param permissions 授权权限 + * return 按钮列表 + */ + public getReadAndWrite(permissions: Set): ResourceStr { + return ''; + } + + /** + * 根据待申请的权限获取授权理由 + * @param groupToPermissions 权限组内所有权限 + * @param permissions 授权权限 + * @param context 上下文信息 + * @param callerAppInfo 调用方信息 + * @param pasteBoardName 剪贴板信息 + * return reason + */ + public getReasonByPermission( + groupToPermissions: Permission[], + permissions: Set, + context: common.ServiceExtensionContext, + callerAppInfo: CallerAppInfo, + pasteBoardName: string + ): ResourceStr { + let reason: string = ''; + groupToPermissions.forEach(perm => { + if (!permissions.has(perm)) { + return; + } + let permissionDetail = callerAppInfo.reqPermsDetails.find(item => item.name === perm); + if (permissionDetail === undefined) { + return; + } + try { + let moduleContext: Context = context.createModuleContext(callerAppInfo.bundleName, permissionDetail.moduleName); + let value = moduleContext.resourceManager.getStringSync(permissionDetail.reasonId); + if (value && reason === '') { + reason = value; + return; + } + } catch (error) { + Log.error(`get reason faild, code: ${error.code}, message: ${error.message}.`); + } + }) + return reason || ''; + } + + /** + * 获取权限组按钮列表 + * return 按钮列表 + */ + public getButtonList(): ButtonStatus[] { + return [ButtonStatus.DENY, ButtonStatus.ALLOW]; + } + + /** + * 区分设备是否支持 + * @param permission 权限 + * return + */ + public isSupport(permission: Permission): boolean { + return true; + } + + /** + * 授权操作 + * @param permission 权限 + * @param callerAppInfo 调用方信息 + * @param locationFlag 位置权限组flag + * @param buttonStatus + * return + */ + public grantHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + return PermissionOption.GRANT; + } + + /** + * 禁止操作 + * @param permission 权限 + * @param callerAppInfo 调用方信息 + * @param locationFlag 位置权限组flag + * @param buttonStatus + * return + */ + public denyHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + return PermissionOption.REVOKE; + } + + /** + * 判断当前权限是否为申请权限 + * @param permission 权限 + * @param callerAppInfo 调用方信息 + * return + */ + public isPermissionPendingGrant(permission: Permission, callerAppInfo: CallerAppInfo): boolean { + let isPendingPermission: boolean = false; + if (callerAppInfo.reqPerms.includes(permission)) { + isPendingPermission = true; + } + return isPendingPermission; + } + + /** + * 预处理权限 + * @param permission 权限 + * @param tokenId 应用token + * return + */ + public async preProcessingPermission(permission: Permission, tokenId: number): Promise { + return []; + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/base/BaseState.ets b/permissionmanager/src/main/ets/common/base/BaseState.ets new file mode 100644 index 0000000000000000000000000000000000000000..a7a360674a4bd987cfb53427a0306941ac629f9b --- /dev/null +++ b/permissionmanager/src/main/ets/common/base/BaseState.ets @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class BaseState { +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/base/BaseViewModel.ets b/permissionmanager/src/main/ets/common/base/BaseViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..5089637edb7352ce93f70ee85fec0e96ca51780a --- /dev/null +++ b/permissionmanager/src/main/ets/common/base/BaseViewModel.ets @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Log } from '../utils/utils'; +import { BaseIntent } from './BaseIntent'; +import { BaseModel } from './BaseModel'; +import { BaseState } from './BaseState'; + +export enum ProcessResult { + FAIL = 0, + SUCCESS = 1 +} + +export abstract class BaseViewModel { + private _model?: M; + private _viewState?: S; + + /** + * 获取界面状态 + * return 界面状态 + */ + public getViewState(): S { + if (this._viewState === null || this._viewState === undefined) { + this._viewState = this.initViewState(); + } + return this._viewState; + } + + /** + * 异步的方式执行intention + * @param intention view的意图 + * return 执行结果 + */ + public async processIntent(intention: BaseIntent): Promise { + Log.info(`start processIntent: ${intention.getIntentTag()}.`); + if (this._model === null || this._model === undefined) { + this._model = this.initModel(); + } + if (this._viewState === null || this._viewState === undefined) { + this._viewState = this.initViewState(); + } + if (this._model !== null && this._viewState !== null) { + try { + return await this.processIntentWithModel(intention, this._model, this._viewState); + } catch (err) { + Log.error(`error when process intention: ${intention.getIntentTag()}.`); + } + } + return ProcessResult.FAIL; + } + + /** + * 同步的方式执行intention + * @param intention view的意图 + * return 执行结果 + */ + public processIntentSync(intention: BaseIntent): ProcessResult { + Log.info(`start processIntent: ${intention.getIntentTag()}.`); + if (this._model === null || this._model === undefined) { + this._model = this.initModel(); + } + if (this._viewState === null || this._viewState === undefined) { + this._viewState = this.initViewState(); + } + if (this._model !== null && this._viewState !== null) { + try { + return this.processIntentWithModelSync(intention, this._model, this._viewState); + } catch (err) { + Log.error(`error when process intention: ${intention.getIntentTag()}.`); + } + } + return ProcessResult.FAIL; + } + + /** + * 初始化逻辑处理类,子类需要实现这个方法 + * return 逻辑处理类 + */ + protected abstract initModel(): M; + + /** + * 初始化界面状态,子类需要实现这个方法 + * return 界面状态 + */ + protected abstract initViewState(): S; + + /** + * 异步的方式处理intention + * @param intention view的意图 + * @param model 逻辑处理类 + * @param viewState 界面状态 + * return 界面状态 + */ + protected abstract processIntentWithModel(intention: BaseIntent, model: M, viewState: S): Promise; + + /** + * 同步的方式处理intention + * @param intention view的意图 + * @param model 逻辑处理类 + * @param viewState 界面状态 + * return 界面状态 + */ + protected processIntentWithModelSync(intention: BaseIntent, model: M, viewState: S): ProcessResult { + return ProcessResult.SUCCESS; + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/components/location.ets b/permissionmanager/src/main/ets/common/components/location.ets index 697467156c53e25d1256656ed7ed43b0b0486366..257aa176848a775b9c1d44b912e912147cadad7b 100644 --- a/permissionmanager/src/main/ets/common/components/location.ets +++ b/permissionmanager/src/main/ets/common/components/location.ets @@ -14,6 +14,7 @@ */ import Constants from '../utils/constant'; +import { GrantDialogViewState } from '../../ServiceExtAbility/GrantDialogViewState'; @Extend(Button)function locationButton() { .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) @@ -25,7 +26,7 @@ import Constants from '../utils/constant'; @Component export struct LocationCanvas { - @Link locationFlag: number; + @Link viewState: GrantDialogViewState; build() { Column() { @@ -35,13 +36,13 @@ export struct LocationCanvas { .height(Constants.FULL_HEIGHT) .scale({ x: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? Constants.LOCATION_CANVAS_ZOOM_SCALE : Constants.LOCATION_CANVAS_INITIAL_SCALE, y: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? Constants.LOCATION_CANVAS_ZOOM_SCALE : Constants.LOCATION_CANVAS_INITIAL_SCALE }) @@ -57,23 +58,24 @@ export struct LocationCanvas { .position({ x: Constants.LOCATION_ICON_POSITION_X, y: Constants.LOCATION_ICON_POSITION_Y }) .opacity( ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? 1 : 0 ) .scale({ x: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? 1 : 0.8, y: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? 1 : 0.8 }) .animation({ duration: Constants.LOCATION_ANIMATION_DURATION / 2, // Animation duration - delay: this.locationFlag == Constants.LOCATION_BOTH_PRECISE ? (Constants.LOCATION_ANIMATION_DURATION / 2) : 0, + delay: this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE ? + (Constants.LOCATION_ANIMATION_DURATION / 2) : 0, curve: Curve.Smooth, // The animation curve playMode: PlayMode.Normal // The animation mode }) @@ -84,12 +86,12 @@ export struct LocationCanvas { .stroke($r('app.color.location_circle_color')) .scale({ x: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? 0 : 1, y: ( - (this.locationFlag == Constants.LOCATION_UPGRADE) || - (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) + (this.viewState.locationFlag == Constants.LOCATION_UPGRADE) || + (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) ) ? 0 : 1 }) .animation({ @@ -98,17 +100,17 @@ export struct LocationCanvas { playMode: PlayMode.Normal // The animation mode }) Row() { - if (this.locationFlag == Constants.LOCATION_BOTH_PRECISE) { + if (this.viewState.locationFlag == Constants.LOCATION_BOTH_PRECISE) { Button($r('app.string.precise_location_on')) .locationButton() .fontColor($r('sys.color.font_emphasize')) - .onClick(() => { this.locationFlag = Constants.LOCATION_BOTH_FUZZY }) + .onClick(() => { this.viewState.locationFlag = Constants.LOCATION_BOTH_FUZZY }) } - if (this.locationFlag == Constants.LOCATION_BOTH_FUZZY) { + if (this.viewState.locationFlag == Constants.LOCATION_BOTH_FUZZY) { Button($r('app.string.precise_location_off')) .locationButton() .fontColor($r('sys.color.font_secondary')) - .onClick(() => { this.locationFlag = Constants.LOCATION_BOTH_PRECISE }) + .onClick(() => { this.viewState.locationFlag = Constants.LOCATION_BOTH_PRECISE }) } }.position({ x: 0, y: 0 }) .width(Constants.FULL_WIDTH) diff --git a/permissionmanager/src/main/ets/common/model/definition.ets b/permissionmanager/src/main/ets/common/model/definition.ets index 368f65e9756181829916f115a1783c70d6951cfc..2de90c22cfea2643bf75c4ca9c283bda00642039 100644 --- a/permissionmanager/src/main/ets/common/model/definition.ets +++ b/permissionmanager/src/main/ets/common/model/definition.ets @@ -65,6 +65,9 @@ export enum PermissionGroup { BLUETOOTH = 'BLUETOOTH', PASTEBOARD = 'PASTEBOARD', FOLDER = 'FOLDER', + DOWNLOAD_DIRECTORY = 'DOWNLOAD_DIRECTORY', + DESKTOP_DIRECTORY = 'DESKTOP_DIRECTORY', + DOCUMENTS_DIRECTORY = 'DOCUMENTS_DIRECTORY', NEARLINK = 'NEARLINK', CUSTOM_SCREEN_CAPTURE = 'CUSTOM_SCREEN_CAPTURE', OTHER = 'OTHER' @@ -77,4 +80,22 @@ export enum ButtonStatus { THIS_TIME_ONLY = 'THIS_TIME_ONLY', ALLOW_THIS_TIME = 'ALLOW_THIS_TIME', ALLOW_ONLY_DURING_USE = 'ALLOW_ONLY_DURING_USE' +} + +export enum ClickOption { + // ALLOW/THIS_TIME_ONLY/ALLOW_THIS_TIME/ALLOW_ONLY_DURING_USE + GRANT = 'GRANT', + // DENY + DENY = 'DENY', + // CANCEL + CANCEL = 'CANCEL' +} + +export enum PermissionOption { + // 允许 + GRANT = 'GRANT', + // 禁止 + REVOKE = 'REVOKE', + // 不操作 + SKIP = 'SKIP' } \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/model/typedef.ets b/permissionmanager/src/main/ets/common/model/typedef.ets index f98e4df1f631d885fc9c0638c635636c189ffcf1..49cd7cae8fbb8fabf4d9f21169120270242a66ae 100644 --- a/permissionmanager/src/main/ets/common/model/typedef.ets +++ b/permissionmanager/src/main/ets/common/model/typedef.ets @@ -12,8 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import rpc from '@ohos.rpc'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import { bundleManager } from '@kit.AbilityKit'; import { Permissions } from '@ohos.abilityAccessCtrl'; -import { ButtonStatus, Permission, PermissionGroup } from './definition'; +import { ButtonStatus, Permission, PermissionGroup, PermissionOption } from './definition'; export class AppInfo { public bundleName: string @@ -459,4 +463,64 @@ export interface CallerBundleInfo { permissionGroup: string[]; token: number; globSwitch: number; +} + +export class Property { + public value: Object + + constructor(value: Object) { + this.value = value + } +} + +export interface CallerAppInfo { + readonly bundleName: string; + readonly tokenId: number; + readonly reqPerms: Permission[]; + readonly reqPermsState: number[]; + readonly reqPermsDetails: bundleManager.ReqPermissionDetail[]; + readonly proxy: rpc.RemoteObject; + grantResult: number[]; + groupWithPermission: Map>; +} + +export class PermissionGroupConfig { + public readonly groupName: PermissionGroup; + public readonly permissions: Permission[]; + public icon: Resource; + public readonly title: ResourceStr; + public readonly readAndWrite?: ResourceStr; + public readonly reason: ResourceStr; + public readonly buttonList: ButtonStatus[]; + + + constructor(param: GroupConfig) { + this.groupName = param.groupName; + this.permissions = param.permissions; + this.icon = param.icon; + this.title = param.title; + this.readAndWrite = param.readAndWrite; + this.reason = param.reason; + this.buttonList = param.buttonList; + } +} + +export interface GroupConfig { + readonly groupName: PermissionGroup; + readonly permissions: Permission[]; + icon: Resource; + readonly title: ResourceStr; + readonly readAndWrite?: ResourceStr; + readonly reason: ResourceStr; + readonly buttonList: ButtonStatus[]; +} + +export interface PermissionWithOption { + permission: Permission; + permissionOption: PermissionOption; +} + +export interface optionAndState { + operationResult: number; + permissionState: abilityAccessCtrl.GrantStatus; } \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/observer/EventObserver.ets b/permissionmanager/src/main/ets/common/observer/EventObserver.ets new file mode 100644 index 0000000000000000000000000000000000000000..70bc403d54ede253b3b068cae7b41ccf16e2a1f9 --- /dev/null +++ b/permissionmanager/src/main/ets/common/observer/EventObserver.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AsyncCallback, BusinessError } from '@ohos.base'; +import { Log } from '../utils/utils'; +import commonEventManager from '@ohos.commonEventManager'; + +export class EventObserver { + private events: string[]; + private subscriber: commonEventManager.CommonEventSubscriber | undefined = undefined; + private subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + events: [] + }; + + constructor(events: string[]) { + this.events = events; + } + + /** + * 注册监听资源变化事件 + * @param callback + * return + */ + public register(callback: AsyncCallback): void { + if (callback === undefined || callback === null) { + Log.error(`callback is undefined.`); + return; + } + try { + if (this.events === undefined || !this.events.length) { + Log.error(`event is undefined.`); + return; + } + this.subscribeInfo.events = this.events; + commonEventManager.createSubscriber(this.subscribeInfo).then(subscriber => { + if (subscriber === undefined) { + Log.error(`subscriber is undefined.`); + return; + } + this.subscriber = subscriber; + try { + commonEventManager.subscribe(this.subscriber, callback); + Log.info(`subscribe success.`); + } catch (err) { + Log.error(`subscribe faild, code: ${err.code}, message: ${err.message}.`); + } + }).catch((err: BusinessError) => { + Log.error(`createSubscriber faild, code: ${err.code}, message: ${err.message}.`); + }) + } catch (error) { + Log.error(`create subscriber faild, code: ${error.code}, message: ${error.message}.`); + } + } + + /** + * 注销监听 + * return + */ + public unregister(): void { + if (this.subscriber === undefined) { + Log.error(`subscriber is undefined.`); + return; + } + try { + commonEventManager.unsubscribe(this.subscriber, err => { + if (err) { + Log.error(`unsubscriber faild, code: ${err.code}, message: ${err.message}.`); + } else { + Log.info(`unsubscriber success.`); + } + }) + } catch (error) { + Log.error(`unsubscriber faild, code: ${error.code}, message: ${error.message}.`); + } + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/AdsStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/AdsStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..476683f118f62fb06147932e4195fc536c0f075d --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/AdsStrategy.ets @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { PermissionUtils } from '../utils/permissionUtils'; +import { Permission, PermissionGroup, PermissionOption } from '../model/definition'; +import { PermissionGroupConfig, PermissionWithOption } from '../model/typedef'; +import Constants from '../utils/constant'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.ADS, + permissions: [Permission.APP_TRACKING_CONSENT], + icon: $r('app.media.track'), + title: '', + reason: '', + buttonList: [] +}) + +export class AdsStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_ADS', appName); + } + + public override async preProcessingPermission( + permission: Permission, tokenId: number + ): Promise { + let permissionWithOptionList: PermissionWithOption[] = []; + if (!groupConfig.permissions.includes(permission)) { + return []; + } + let isPermissionQueryEnabled = await PermissionUtils.isPermissionQueryEnabled(permission); + if (!isPermissionQueryEnabled) { + await PermissionUtils.grantPermissionWithResult(permission, Constants.PERMISSION_FLAG, tokenId); + permissionWithOptionList.push({ permission, permissionOption: PermissionOption.GRANT }); + } + return permissionWithOptionList; + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/AudiosStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/AudiosStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..75e9268931ff9ac75059873e8fdede4ffa873413 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/AudiosStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.AUDIOS, + permissions: [Permission.READ_AUDIO, Permission.WRITE_AUDIO], + icon: $r('app.media.ic_public_audio'), + title: '', + reason: '', + buttonList: [] +}) + +export class AudioStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_audios', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/BluetoothStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/BluetoothStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..660d7b32c6f9515a0b930828036387d3fa231e39 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/BluetoothStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.BLUETOOTH, + permissions: [Permission.ACCESS_BLUETOOTH], + icon: $r('app.media.ic_public_bluetooth'), + title: '', + reason: '', + buttonList: [] +}) + +export class BluetoothStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_bluetooth', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/CalendarStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/CalendarStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..2dddfe861be8ccaec4be5b2c3c7a1fcfa590cc8a --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/CalendarStrategy.ets @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { ButtonStatus, Permission, PermissionGroup, PermissionOption } from '../model/definition'; +import { CallerAppInfo, PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.CALENDAR, + permissions: [Permission.READ_CALENDAR, Permission.WRITE_CALENDAR, Permission.READ_WHOLE_CALENDAR, Permission.WRITE_WHOLE_CALENDAR], + icon: $r('app.media.ic_public_calendar'), + title: '', + reason: '', + buttonList: [] +}) + +export class CalendarStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_calendar', appName); + } + + public override getReadAndWrite(permissions: Set): ResourceStr { + let message: ResourceStr = ''; + let isRead: boolean = permissions.has(Permission.READ_CALENDAR); + let isWrite: boolean = permissions.has(Permission.WRITE_CALENDAR); + + if (isRead) { + message = $r('sys.string.ohos_lab_read_calendar'); + } + if (isWrite) { + message = $r('sys.string.ohos_lab_write_calendar'); + } + if (isRead && isWrite) { + message = ''; + } + return message; + } + + public override grantHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (this.isPermissionPendingGrant(permission, callerAppInfo)) { + return PermissionOption.GRANT; + } + return PermissionOption.SKIP; + } + + public override denyHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (this.isPermissionPendingGrant(permission, callerAppInfo)) { + return PermissionOption.REVOKE; + } + return PermissionOption.SKIP; + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/CameraStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/CameraStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..3ec67bdb29a85ce1a49613039f8cf04914ba93c6 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/CameraStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.CAMERA, + permissions: [Permission.CAMERA], + icon: $r('app.media.ic_public_camera'), + title: '', + reason: '', + buttonList: [] +}) + +export class CameraStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_camera', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/ContactsStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/ContactsStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..6fee6f2a620eb4997d5056b2f026ae7a584f82bf --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/ContactsStrategy.ets @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { ButtonStatus, Permission, PermissionGroup, PermissionOption } from '../model/definition'; +import { CallerAppInfo, PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.CONTACTS, + permissions: [Permission.READ_CONTACTS, Permission.WRITE_CONTACTS], + icon: $r('app.media.ic_public_contacts_group'), + title: '', + reason: '', + buttonList: [] +}) + +export class ContactsStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_contacts', appName); + } + + public override getReadAndWrite(permissions: Set): ResourceStr { + let message: ResourceStr = ''; + let isRead: boolean = permissions.has(Permission.READ_CONTACTS); + let isWrite: boolean = permissions.has(Permission.WRITE_CONTACTS); + + if (isRead) { + message = $r('sys.string.ohos_lab_read_contacts'); + } + if (isWrite) { + message = $r('sys.string.ohos_lab_write_contacts'); + } + if (isRead && isWrite) { + message = ''; + } + return message; + } + + public override grantHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (this.isPermissionPendingGrant(permission, callerAppInfo)) { + return PermissionOption.GRANT; + } + return PermissionOption.SKIP; + } + + public override denyHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (this.isPermissionPendingGrant(permission, callerAppInfo)) { + return PermissionOption.REVOKE; + } + return PermissionOption.SKIP; + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/DesktopDirectoryStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/DesktopDirectoryStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..133b091c410df5ec26ad75bc0067266994a603a6 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/DesktopDirectoryStrategy.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; +import { DeviceUtil } from '../utils/deviceUtil'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.DESKTOP_DIRECTORY, + permissions: [Permission.READ_WRITE_DESKTOP_DIRECTORY], + icon: $r('app.media.ic_public_folder'), + title: '', + reason: '', + buttonList: [] +}) + +export class DesktopDirectoryStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_desktop_folder', appName); + } + + public override isSupport(permission: Permission): boolean { + return DeviceUtil.isPC() || DeviceUtil.isTablet(); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/DistributedDatasyncStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/DistributedDatasyncStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..89efdd3dfa18520d1b2bec04e05db8cef0cf29b4 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/DistributedDatasyncStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.DISTRIBUTED_DATASYNC, + permissions: [Permission.DISTRIBUTED_DATASYNC], + icon: $r('app.media.ic_multi_device_vector'), + title: '', + reason: '', + buttonList: [] +}) + +export class DistributedDatasyncStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_distributed_datasync', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsDirectoryStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsDirectoryStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..e05a2bac2dca4433cebe5d464092b58a78c2dfbd --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsDirectoryStrategy.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; +import { DeviceUtil } from '../utils/deviceUtil'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.DOCUMENTS_DIRECTORY, + permissions: [Permission.READ_WRITE_DOCUMENTS_DIRECTORY], + icon: $r('app.media.ic_public_folder'), + title: '', + reason: '', + buttonList: [] +}) + +export class DocumentsDirectoryStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_document_folder', appName); + } + + public override isSupport(permission: Permission): boolean { + return DeviceUtil.isPC() || DeviceUtil.isTablet(); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..072120572f6ee1ecfbf1fc5bac2328176d89dd2d --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/DocumentsStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.DOCUMENTS, + permissions: [Permission.READ_DOCUMENT, Permission.WRITE_DOCUMENT, Permission.READ_MEDIA, Permission.WRITE_MEDIA], + icon: $r('app.media.ic_public_folder'), + title: '', + reason: '', + buttonList: [] +}) + +export class DocumentsStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_document', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/DownloadDirectoryStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/DownloadDirectoryStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..556e63a7872dd0379a87a412965348d70a3958a6 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/DownloadDirectoryStrategy.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; +import { DeviceUtil } from '../utils/deviceUtil'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.DOWNLOAD_DIRECTORY, + permissions: [Permission.READ_WRITE_DOWNLOAD_DIRECTORY], + icon: $r('app.media.ic_public_folder'), + title: '', + reason: '', + buttonList: [] +}) + +export class DownloadDirectoryStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_download_folder', appName); + } + + public override isSupport(permission: Permission): boolean { + return DeviceUtil.isPC() || DeviceUtil.isTablet(); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/GetInstalledBundleListStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/GetInstalledBundleListStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..008f7af9a23efd04f200128418bc835edad811c2 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/GetInstalledBundleListStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.GET_INSTALLED_BUNDLE_LIST, + permissions: [Permission.GET_INSTALLED_BUNDLE_LIST], + icon: $r('app.media.ic_public_app_list'), + title: '', + reason: '', + buttonList: [] +}) + +export class GetInstalledBundleListStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_appList', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/HealthStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/HealthStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..e77fb1c6fceafd4b4e36166c010c019367c53051 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/HealthStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.HEALTH, + permissions: [Permission.READ_HEALTH_DATA], + icon: $r('app.media.ic_ssensor'), + title: '', + reason: '', + buttonList: [] +}) + +export class HealthStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_health', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/ImageAndVideosStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/ImageAndVideosStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..665d4c310ecda8655d71300e77e85c290e9d9922 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/ImageAndVideosStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.IMAGE_AND_VIDEOS, + permissions: [Permission.READ_IMAGEVIDEO, Permission.WRITE_IMAGEVIDEO, Permission.MEDIA_LOCATION], + icon: $r('app.media.ic_public_picture'), + title: '', + reason: '', + buttonList: [] +}) + +export class ImageAndVideosStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_image_and_videos', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/LocationStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/LocationStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..a6569663556c8574f329dcaaf1a54a9b3839b23f --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/LocationStrategy.ets @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup, ButtonStatus, PermissionOption } from '../model/definition'; +import { CallerAppInfo, PermissionGroupConfig } from '../model/typedef'; +import Constants from '../utils/constant'; + +const fuzzyMarks = [Constants.LOCATION_FUZZY, Constants.LOCATION_BOTH_FUZZY, Constants.LOCATION_BOTH_PRECISE]; +const preciseMarks = [Constants.LOCATION_UPGRADE, Constants.LOCATION_BOTH_PRECISE]; +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.LOCATION, + permissions: [Permission.LOCATION_IN_BACKGROUND, Permission.APPROXIMATELY_LOCATION, Permission.LOCATION], + icon: $r('app.media.ic_public_gps'), + title: '', + reason: '', + buttonList: [] +}) + +export class LocationStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + if (locationFlag == Constants.LOCATION_FUZZY) { + return $r('app.string.access_general_location', appName); + } + if (locationFlag == Constants.LOCATION_UPGRADE) { + return $r('app.string.fuzzy_to_exact', appName); + } + return $r('app.string.group_label_location', appName); + } + + public override getButtonList(): ButtonStatus[] { + return [ButtonStatus.ALLOW_ONLY_DURING_USE, ButtonStatus.ALLOW_THIS_TIME, ButtonStatus.CANCEL]; + } + + public override grantHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + let opt: PermissionOption = PermissionOption.SKIP; + if (permission === Permission.APPROXIMATELY_LOCATION) { + opt = this.handleFuzzyLocationGrant(callerAppInfo, locationFlag, buttonStatus); + } else if (permission === Permission.LOCATION) { + opt = this.handlePreciseLocationGrant(callerAppInfo, locationFlag, buttonStatus); + } else { + opt = PermissionOption.SKIP; + } + return opt; + } + + public override denyHandle( + permission: Permission, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + let opt: PermissionOption = PermissionOption.REVOKE; + if ((locationFlag === Constants.LOCATION_UPGRADE) && (permission !== Permission.LOCATION)) { + opt = PermissionOption.SKIP; + } + return opt; + } + + private handleFuzzyLocationGrant( + callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (fuzzyMarks.includes(locationFlag)) { + return PermissionOption.GRANT; + } else { + return PermissionOption.SKIP; + } + } + + private handlePreciseLocationGrant( + callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus + ): PermissionOption { + if (preciseMarks.includes(locationFlag)) { + return PermissionOption.GRANT; + } else { + return PermissionOption.SKIP; + } + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/MicrophoneStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/MicrophoneStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..26bdc2b4b3e5346d693bd2b8f6e70b3fe5cc05a5 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/MicrophoneStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.MICROPHONE, + permissions: [Permission.MICROPHONE], + icon: $r('app.media.ic_public_voice'), + title: '', + reason: '', + buttonList: [] +}) + +export class MicrophoneStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_microphone', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/NearlinkStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/NearlinkStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..812de6baa6257c95f5cfe630c0a8e713eeb8722d --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/NearlinkStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.NEARLINK, + permissions: [Permission.ACCESS_NEARLINK], + icon: $r('app.media.ic_nearLink'), + title: '', + reason: '', + buttonList: [] +}) + +export class NearlinkStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_nearLink', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/PasteboardStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/PasteboardStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..382da05b2e449fb9782ff5470f8aaaf81f04045e --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/PasteboardStrategy.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup, ButtonStatus } from '../model/definition'; +import { CallerAppInfo, PermissionGroupConfig } from '../model/typedef'; +import common from '@ohos.app.ability.common'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.PASTEBOARD, + permissions: [Permission.READ_PASTEBOARD], + icon: $r('app.media.ic_clipboard'), + title: '', + reason: '', + buttonList: [] +}) + +export class PasteboardStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_pasteboard', appName); + } + + public override getButtonList(): ButtonStatus[] { + return [ButtonStatus.DENY, ButtonStatus.THIS_TIME_ONLY]; + } + + public override getReasonByPermission( + groupToPermissions: Permission[], + permissions: Set, + context: common.ServiceExtensionContext, + callerAppInfo: CallerAppInfo, + pasteBoardName: string + ): ResourceStr { + if (pasteBoardName) { + return $r('app.string.pasteBoard_app', pasteBoardName); + } else { + return $r('app.string.pasteBoard_desc'); + } + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/PermissionGroupManager.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/PermissionGroupManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..1fd9e67923dcac2f25359f83437381bc2d19f514 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/PermissionGroupManager.ets @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { LocationStrategy } from './LocationStrategy'; +import { CameraStrategy } from './CameraStrategy'; +import { MicrophoneStrategy } from './MicrophoneStrategy'; +import { ContactsStrategy } from './ContactsStrategy'; +import { CalendarStrategy } from './CalendarStrategy'; +import { SportStrategy } from './SportStrategy'; +import { HealthStrategy } from './HealthStrategy'; +import { ImageAndVideosStrategy } from './ImageAndVideosStrategy'; +import { AudioStrategy } from './AudiosStrategy'; +import { DocumentsStrategy } from './DocumentsStrategy'; +import { AdsStrategy } from './AdsStrategy'; +import { GetInstalledBundleListStrategy } from './GetInstalledBundleListStrategy'; +import { DistributedDatasyncStrategy } from './DistributedDatasyncStrategy'; +import { BluetoothStrategy } from './BluetoothStrategy'; +import { PasteboardStrategy } from './PasteboardStrategy'; +import { DownloadDirectoryStrategy } from './DownloadDirectoryStrategy'; +import { DesktopDirectoryStrategy } from './DesktopDirectoryStrategy'; +import { DocumentsDirectoryStrategy } from './DocumentsDirectoryStrategy'; +import { NearlinkStrategy } from './NearlinkStrategy'; +import { ScreenCaptureStrategy } from './ScreenCaptureStrategy'; +import { Permission, PermissionGroup, PermissionOption, ButtonStatus, ClickOption } from '../model/definition'; +import { PermissionGroupConfig, CallerAppInfo, PermissionWithOption } from '../model/typedef'; +import { Log } from '../utils/utils'; +import common from '@ohos.app.ability.common'; + +export class PermissionGroupManager { + private permissionStrategies: Map = new Map(); + private static instance: PermissionGroupManager; + + private constructor() { + // 位置信息 + this.permissionStrategies.set(PermissionGroup.LOCATION, new LocationStrategy()); + // 相机 + this.permissionStrategies.set(PermissionGroup.CAMERA, new CameraStrategy()); + // 麦克风 + this.permissionStrategies.set(PermissionGroup.MICROPHONE, new MicrophoneStrategy()); + // 通讯录 + this.permissionStrategies.set(PermissionGroup.CONTACTS, new ContactsStrategy()); + // 日历 + this.permissionStrategies.set(PermissionGroup.CALENDAR, new CalendarStrategy()); + // 运动健身 + this.permissionStrategies.set(PermissionGroup.SPORT, new SportStrategy()); + // 身体传感器 + this.permissionStrategies.set(PermissionGroup.HEALTH, new HealthStrategy()); + // 图片和视频 + this.permissionStrategies.set(PermissionGroup.IMAGE_AND_VIDEOS, new ImageAndVideosStrategy()); + // 音频 + this.permissionStrategies.set(PermissionGroup.AUDIOS, new AudioStrategy()); + // 文件 + this.permissionStrategies.set(PermissionGroup.DOCUMENTS, new DocumentsStrategy()); + // 应用跟踪 + this.permissionStrategies.set(PermissionGroup.ADS, new AdsStrategy()); + // 读取已安装应用列表 + this.permissionStrategies.set(PermissionGroup.GET_INSTALLED_BUNDLE_LIST, new GetInstalledBundleListStrategy()); + // 附近设备 + this.permissionStrategies.set(PermissionGroup.DISTRIBUTED_DATASYNC, new DistributedDatasyncStrategy()); + // 蓝牙 + this.permissionStrategies.set(PermissionGroup.BLUETOOTH, new BluetoothStrategy()); + // 剪贴板 + this.permissionStrategies.set(PermissionGroup.PASTEBOARD, new PasteboardStrategy()); + // 文件夹 + this.permissionStrategies.set(PermissionGroup.DOWNLOAD_DIRECTORY, new DownloadDirectoryStrategy()); + this.permissionStrategies.set(PermissionGroup.DESKTOP_DIRECTORY, new DesktopDirectoryStrategy()); + this.permissionStrategies.set(PermissionGroup.DOCUMENTS_DIRECTORY, new DocumentsDirectoryStrategy()); + // 星闪 + this.permissionStrategies.set(PermissionGroup.NEARLINK, new NearlinkStrategy()); + // 截屏 + this.permissionStrategies.set(PermissionGroup.CUSTOM_SCREEN_CAPTURE, new ScreenCaptureStrategy()); + } + + public static getInstance(): PermissionGroupManager { + if (!PermissionGroupManager.instance) { + PermissionGroupManager.instance = new PermissionGroupManager(); + } + return PermissionGroupManager.instance; + } + + /** + * 获取所有权限组config + * return + */ + public getAllGroupConfigs(): PermissionGroupConfig[] { + let allConfig: PermissionGroupConfig[] = []; + this.permissionStrategies.forEach((strategy, permissionGroup) => { + allConfig.push(strategy.getPermissionGroupConfig()); + }) + return allConfig; + } + + /** + * 根据权限获取所在权限组名 + * @param permission 权限 + * @param allConfigs + * return + */ + public getGroupNameByPermission( + permission: Permission, allConfigs: PermissionGroupConfig[] + ): PermissionGroup | undefined { + for (let index = 0; index < allConfigs.length; index++) { + const element = allConfigs[index]; + if (element.permissions.includes(permission)) { + return element.groupName; + } + } + Log.error(`can not find permissionGroup, permission: ${permission}`); + return undefined; + } + + /** + * 预处理待授权权限 + * @param permission 权限 + * @param groupName 权限组名 + * @param tokenId 应用token + * return + */ + public async preProcessPermission( + permission: Permission, groupName: PermissionGroup, tokenId: number + ): Promise { + const groupHandle = this.permissionStrategies.get(groupName); + if (groupHandle === undefined) { + return []; + } + let result = await groupHandle.preProcessingPermission(permission, tokenId); + return result; + } + + /** + * 根据带申请权限获取所有权限组列表 + * @param callerAppInfo 调用方信息 + * @param context 应用上下文 + * @param appName 应用名 + * @param locationFlag 位置权限组flag + * @param pasteBoardName 剪贴板信息 + * return + */ + public getGroupConfigs( + callerAppInfo: CallerAppInfo, + context: common.ServiceExtensionContext, + appName: string, + locationFlag: number, + pasteBoardName: string + ): PermissionGroupConfig[] { + let groupConfigList: PermissionGroupConfig[] = []; + callerAppInfo.groupWithPermission.forEach((permissions, permissionGroup) => { + let groupHandle = this.permissionStrategies.get(permissionGroup); + if (groupHandle === undefined || groupHandle === null) { + Log.error(`get groupHandle faild, group: ${permissionGroup}.`); + return; + } + let groupConfig: PermissionGroupConfig = groupHandle.getPermissionGroupConfig(); + let title: ResourceStr = groupHandle.getGroupTitle(appName, locationFlag); + let readAndWrite: ResourceStr = groupHandle.getReadAndWrite(permissions); + let reason: ResourceStr = + groupHandle.getReasonByPermission(groupConfig.permissions, permissions, context, callerAppInfo, pasteBoardName); + groupConfigList.push(new PermissionGroupConfig({ + groupName: groupConfig.groupName, + permissions: groupConfig.permissions, + icon: groupConfig.icon, + title, + readAndWrite, + reason, + buttonList: groupHandle.getButtonList() + })) + }) + return groupConfigList; + } + + /** + * 根据点击状态,返回当前权限对应的操作 + * @param permission 权限 + * @param groupName 权限组名 + * @param callerAppInfo 调用方信息 + * @param locationFlag 位置权限组flag + * @param buttonStatus + * return + */ + public getPermissionOption( + permission: Permission, + groupName: PermissionGroup, + callerAppInfo: CallerAppInfo, + locationFlag: number, + buttonStatus: ButtonStatus + ): PermissionOption { + const groupHandle = this.permissionStrategies.get(groupName); + let situation: ClickOption = this.getClickSituation(buttonStatus); + if (groupHandle) { + if (situation === ClickOption.GRANT) { + return groupHandle.grantHandle(permission, callerAppInfo, locationFlag, buttonStatus); + } else if (situation === ClickOption.DENY) { + return groupHandle.denyHandle(permission, callerAppInfo, locationFlag, buttonStatus); + } else { + return PermissionOption.SKIP; + } + } + return PermissionOption.SKIP; + } + + /** + * 处理点击状态 + * @param buttonStatus + * return + */ + public getClickSituation(buttonStatus: ButtonStatus): ClickOption { + switch (buttonStatus) { + case ButtonStatus.ALLOW: + case ButtonStatus.ALLOW_THIS_TIME: + case ButtonStatus.THIS_TIME_ONLY: + case ButtonStatus.ALLOW_ONLY_DURING_USE: + return ClickOption.GRANT; + case ButtonStatus.CANCEL: + return ClickOption.CANCEL; + default: + return ClickOption.DENY; + } + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/ScreenCaptureStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/ScreenCaptureStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..3d20b87e47d8c13f3877f0fc12f55ef926341dc7 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/ScreenCaptureStrategy.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; +import { DeviceUtil } from '../utils/deviceUtil'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.CUSTOM_SCREEN_CAPTURE, + permissions: [Permission.CUSTOM_SCREEN_CAPTURE], + icon: $r('app.media.ic_public_screen_capture'), + title: '', + reason: '', + buttonList: [] +}) + +export class ScreenCaptureStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_screenCapture', appName); + } + + public override isSupport(permission: Permission): boolean { + return DeviceUtil.isPC() || DeviceUtil.isTablet(); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/permissionGroupManager/SportStrategy.ets b/permissionmanager/src/main/ets/common/permissionGroupManager/SportStrategy.ets new file mode 100644 index 0000000000000000000000000000000000000000..64ac5012c883202af249bdfb72745c7695613c17 --- /dev/null +++ b/permissionmanager/src/main/ets/common/permissionGroupManager/SportStrategy.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BasePermissionStrategy } from '../base/BasePermissionStrategy'; +import { Permission, PermissionGroup } from '../model/definition'; +import { PermissionGroupConfig } from '../model/typedef'; + +const groupConfig: PermissionGroupConfig = new PermissionGroupConfig({ + groupName: PermissionGroup.SPORT, + permissions: [Permission.ACTIVITY_MOTION], + icon: $r('app.media.ic_sport'), + title: '', + reason: '', + buttonList: [] +}) + +export class SportStrategy extends BasePermissionStrategy { + public override getPermissionGroupConfig(): PermissionGroupConfig { + return groupConfig; + } + + public override getGroupTitle(appName: string, locationFlag: number): ResourceStr { + return $r('app.string.group_label_sport', appName); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/utils/constant.ets b/permissionmanager/src/main/ets/common/utils/constant.ets index 26d4b4c2b4faf7198a4ad15911e989bfcaaba4fc..12592fdcaa9ca9cd18d04f1cd16d7a036ca6f4f9 100644 --- a/permissionmanager/src/main/ets/common/utils/constant.ets +++ b/permissionmanager/src/main/ets/common/utils/constant.ets @@ -282,32 +282,7 @@ export default class Constants { public static DIALOG_LABEL_LINE_HEIGHT = 14; // request text of dialog - public static DIALOG_REQ_FONT_SIZE = 16; public static DIALOG_REQ_MARGIN_TOP = 16; - public static DIALOG_REQ_MARGIN_LEFT = 24; - public static DIALOG_REQ_MARGIN_RIGHT = 24; - public static DIALOG_REQ_LINE_HEIGHT = 22; - - // description text of dialog - public static DIALOG_DESP_FONT_SIZE = 14; - public static DIALOG_DESP_MARGIN_TOP = 2; - public static DIALOG_DESP_MARGIN_LEFT = 24; - public static DIALOG_DESP_MARGIN_RIGHT = 24; - public static DIALOG_DESP_MARGIN_BOTTOM = 8; - public static DIALOG_DESP_LINE_HEIGHT = 19; - - public static BUTTON_MARGIN_TOP = 8; - public static BUTTON_MARGIN_LEFT = 16; - public static BUTTON_MARGIN_RIGHT = 16; - public static BUTTON_HEIGHT = 40; - - public static DIALOG_PRIVACY_BORDER_RADIUS = 32; - - // initial check status - public static INIT_NEED_TO_WAIT = 0 - public static INIT_NEED_TO_VERIFY = 1 - public static INIT_NEED_TO_TERMINATED = 2 - public static INIT_NEED_TO_REFRESH = 3 public static RESULT_SUCCESS = 1 public static RESULT_FAILURE = 0 diff --git a/permissionmanager/src/main/ets/common/utils/deviceUtil.ets b/permissionmanager/src/main/ets/common/utils/deviceUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..3bb03791d7c213b49c3131fa0a4bc8a53a086ab9 --- /dev/null +++ b/permissionmanager/src/main/ets/common/utils/deviceUtil.ets @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import deviceInfo from '@ohos.deviceInfo'; +import display from '@ohos.display'; + +export class DeviceUtil { + constructor() { + } + + public static isPC(): boolean { + return deviceInfo.deviceType === '2in1'; + } + + public static isTablet(): boolean { + return deviceInfo.deviceType === 'tablet'; + } + + // 折叠屏 + public static isFold(): boolean { + try { + return ((deviceInfo.deviceType === 'default' || deviceInfo.deviceType === 'phone') && display.isFoldable()); + } catch (error) { + return false; + } + } + + // 直板机 + public static isCandyBar(): boolean { + return ((deviceInfo.deviceType === 'default' || deviceInfo.deviceType === 'phone') && !DeviceUtil.isFold()); + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/utils/globalContext.ts b/permissionmanager/src/main/ets/common/utils/globalContext.ts index c3c9266857cb7e6f2f93c29cc45b360a8f181336..53809b430677aed0804b623cda51fd28a5384618 100644 --- a/permissionmanager/src/main/ets/common/utils/globalContext.ts +++ b/permissionmanager/src/main/ets/common/utils/globalContext.ts @@ -48,4 +48,23 @@ export class GlobalContext { set(key: string, objectClass: Object): void { this._objects.set(key, objectClass); } + + public getWindowNum(): number { + return globalThis.windowNum || 0; + } + + public increaseAndGetWindowNum(): number { + globalThis.windowNum ++; + return globalThis.windowNum; + } + + public decreaseAndGetWindowNum(): number { + globalThis.windowNum --; + return globalThis.windowNum; + } + + public setAndGetWindowNum(num: number): number { + globalThis.windowNum = num; + return globalThis.windowNum || 0; + } } \ No newline at end of file diff --git a/permissionmanager/src/main/ets/common/utils/permissionUtils.ets b/permissionmanager/src/main/ets/common/utils/permissionUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..6fff2453aae1d984ffd9e826425fb401d98a1607 --- /dev/null +++ b/permissionmanager/src/main/ets/common/utils/permissionUtils.ets @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Permission } from '../model/definition'; +import { optionAndState } from '../model/typedef'; +import { Log } from '../utils/utils'; +import Constants from '../utils/constant'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; + +export class PermissionUtils { + /** + * 获取权限的弹窗开关状态 + * @param permission 权限 + * return + */ + public static async isPermissionQueryEnabled(permission: Permission): Promise { + let atManager = abilityAccessCtrl.createAtManager(); + try { + let result = await atManager.getPermissionRequestToggleStatus(permission); + Log.info(`permissionRequestToggleStatus is ${result}.`); + return result === abilityAccessCtrl.PermissionRequestToggleStatus.OPEN; + } catch (error) { + Log.error(`getBundleResourceInfo faild, code: ${error.code}, message: ${error.message}.`); + return false; + } + } + + /** + * 授予权限并返回操作结果 + * @param permission 操作权限 + * @param flag 授权flag + * @param tokenId 应用token + * return + */ + public static async grantPermissionWithResult( + permission: Permission, flag: number, tokenId: number + ): Promise { + try { + let atManager = abilityAccessCtrl.createAtManager(); + await atManager.grantUserGrantedPermission(tokenId, permission, flag); + Log.info(`grant permission success, permission: ${permission}.`); + return { + operationResult: Constants.RESULT_SUCCESS, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED + } + } catch (error) { + Log.error(`grant permission faild, permission: ${permission}, code: ${error.code}, message: ${error.message}.`); + return { + operationResult: Constants.RESULT_FAILURE, + permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED + } + } + } + +} \ No newline at end of file diff --git a/permissionmanager/src/main/ets/pages/dialogPlus.ets b/permissionmanager/src/main/ets/pages/dialogPlus.ets index b2f2fcd5ff8623b96dae57a6cb03c48053dc51dc..8cdc2ebd43f403a56f9eff6220e4781de9a95433 100644 --- a/permissionmanager/src/main/ets/pages/dialogPlus.ets +++ b/permissionmanager/src/main/ets/pages/dialogPlus.ets @@ -13,29 +13,20 @@ * limitations under the License. */ -import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; -import bundleManager from '@ohos.bundle.bundleManager'; -import rpc from '@ohos.rpc'; import window from '@ohos.window'; import common from '@ohos.app.ability.common'; -import pasteboard from '@ohos.pasteboard'; -import { BusinessError } from '@ohos.base'; -import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; -import { - Log, - getPermissionGroup, - titleTrim, - getPermissionLabel, - getFontSizeScale, - supportPermission -} from '../common/utils/utils'; -import { Permission, ButtonStatus } from '../common/model/definition'; -import { GroupInfo, WantInfo } from '../common/model/typedef'; -import { GlobalContext } from '../common/utils/globalContext'; import Constants from '../common/utils/constant'; -import { showSubPermissionsGroup, buttonResource } from '../common/model/permissionGroup'; -import { LocationCanvas } from '../common/components/location'; import { MeasureText } from '@kit.ArkUI'; +import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; +import { Log, getFontSizeScale, } from '../common/utils/utils'; +import { EventObserver } from '../common/observer/EventObserver'; +import { PermissionGroup, ButtonStatus } from '../common/model/definition'; +import { CallerAppInfo, PermissionGroupConfig } from '../common/model/typedef'; +import { LocationCanvas } from '../common/components/location'; +import { buttonResource } from '../common/model/permissionGroup'; +import { GrantDialogIntent } from '../ServiceExtAbility/GrantDialogIntent'; +import { GrantDialogViewModel } from '../ServiceExtAbility/GrantDialogViewModel'; +import { GrantDialogViewState } from '../ServiceExtAbility/GrantDialogViewState'; @Extend(Button)function customizeButton() { .buttonStyle(ButtonStyleMode.TEXTUAL) @@ -51,30 +42,31 @@ import { MeasureText } from '@kit.ArkUI'; .maxLines(Constants.SECURITY_HEADER_MAX_LINES) } -const fuzzyMarks = [Constants.LOCATION_FUZZY, Constants.LOCATION_BOTH_FUZZY, Constants.LOCATION_BOTH_PRECISE]; -const preciseMarks = [Constants.LOCATION_UPGRADE, Constants.LOCATION_BOTH_PRECISE]; +const EVENT_BUNDLE_RESOURCES_CHANGED = 'usual.event.BUNDLE_RESOURCES_CHANGED'; let storage = LocalStorage.getShared(); +let callerAppInfo: CallerAppInfo = storage.get('callerAppInfo') as CallerAppInfo; +let win: window.Window = storage.get('win') as window.Window; +let viewModel: GrantDialogViewModel = new GrantDialogViewModel(callerAppInfo, win); @Entry(storage) @Component struct dialogPlusPage { - @LocalStorageLink('want') want: WantInfo = new WantInfo([]); - @LocalStorageLink('win') win: window.Window = {} as window.Window; private context = getContext(this) as common.ServiceExtensionContext; - @State count: number = 0; - @State result: Array = []; - @State accessTokenId: number = 0; - @State initStatus: number = Constants.INIT_NEED_TO_WAIT; - @State reqPerms: Array = []; - @State grantGroups: Array = []; - @State userFixedFlag: number = 2; // means user fixed - @State grantStatus: number = -1; - @State appName: string = ''; - @State locationFlag: number = Constants.LOCATION_NONE; - @State reqPermissionDetails: bundleManager.ReqPermissionDetail[] = []; - @State refresh: number = 0; - @State pasteBoardName: string = ''; - @State isUpdate: number = -1; + private resourceChangeObserver: EventObserver = new EventObserver([EVENT_BUNDLE_RESOURCES_CHANGED]); + @State viewState: GrantDialogViewState = viewModel.getViewState(); + private refreshData = (): void => { + if (callerAppInfo === undefined) { + Log.error(`event error, callerAppInfo is undefined.`); + return; + } + try { + viewModel.processIntent( + new GrantDialogIntent.RefreshIntent(this.context, callerAppInfo) + ); + } catch (error) { + Log.error(`try to refresh data faild, code: ${error.code}, message: ${error.message}.`); + } + } dialogController: CustomDialogController | null = new CustomDialogController({ builder: CustomContentDialog({ @@ -91,11 +83,11 @@ struct dialogPlusPage { Row() { Column() { if (getFontSizeScale()) { - Text($r(this.showTitle(), this.appName)) + Text(this.viewState.grantGroups[this.viewState.curIndex].title) .titleText() .fontSize($r('sys.float.Title_S')) } else { - Text($r(this.showTitle(), this.appName)) + Text(this.viewState.grantGroups[this.viewState.curIndex].title) .titleText() .minFontSize(Constants.TEXT_MIDDLE_FONT_SIZE) .maxFontSize($r('sys.float.Title_S')) @@ -117,14 +109,13 @@ struct dialogPlusPage { buildContent(): void { Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { Column() { - if ((this.initStatus != Constants.INIT_NEED_TO_WAIT) && this.verify()) { - Image(this.currentGroup().icon) + Image(this.viewState.grantGroups[this.viewState.curIndex].icon) .width(Constants.DIALOG_ICON_WIDTH) .height(Constants.DIALOG_ICON_HEIGHT) .fillColor($r('sys.color.font_primary')) .margin({ top: Constants.DIALOG_ICON_MARGIN_TOP }) - if (this.grantGroups.length > 1) { - Text(`${this.count + 1} / ${this.grantGroups.length}`) + if (this.viewState.grantGroups.length > 1) { + Text(`${this.viewState.curIndex + 1} / ${this.viewState.grantGroups.length}`) .fontSize(Constants.DIALOG_LABEL_FONT_SIZE) .fontColor($r('sys.color.font_secondary')) .lineHeight(Constants.DIALOG_LABEL_LINE_HEIGHT) @@ -137,53 +128,43 @@ struct dialogPlusPage { Row() { Flex({ justifyContent: FlexAlign.Center }) { Text() { - if ( - this.currentGroup().name === 'LOCATION' && - ((this.locationFlag == Constants.LOCATION_FUZZY) || - (this.locationFlag == Constants.LOCATION_BOTH_FUZZY)) - ) { - Span($r('app.string.close_exact_position')) - } else if (this.currentGroup().name === 'PASTEBOARD') { - if (this.pasteBoardName) { - Span($r('app.string.pasteBoard_app', this.pasteBoardName)) - } else { - Span($r('app.string.pasteBoard_desc')) - } - } else { - if (this.currentGroup().description.length > 0) { - ForEach(this.currentGroup().description, (item: ResourceStr) => { - Span(item) - }) - Span(this.currentGroup().reason ? $r('app.string.comma') : $r('app.string.period')) + if (this.viewState.grantGroups[this.viewState.curIndex].readAndWrite) { + Span(this.viewState.grantGroups[this.viewState.curIndex].readAndWrite) + Span(this.viewState.grantGroups[this.viewState.curIndex].reason ? $r('app.string.comma') : $r('app.string.period')) } - Span(this.refresh >= 0 ? this.currentGroup().reason : '') - } + Span(this.showReason()) } .textAlign(TextAlign.Start) .fontColor($r('sys.color.font_primary')) .fontSize($r('sys.float.Body_L')) .maxFontScale(Constants.DIALOG_TEXT_MAX_SCALE) .margin({ - left: Constants.DIALOG_DESP_MARGIN_LEFT, - right: Constants.DIALOG_DESP_MARGIN_RIGHT, - bottom: Constants.DIALOG_DESP_MARGIN_BOTTOM + left: Constants.MARGIN_24, + right: Constants.MARGIN_24, + bottom: Constants.MARGIN_8 }) } } - if (this.locationFlag > Constants.LOCATION_NONE && this.currentGroup().name === 'LOCATION') { - LocationCanvas({ locationFlag: $locationFlag }) + if ( + this.viewState.locationFlag > Constants.LOCATION_NONE && + this.viewState.grantGroups[this.viewState.curIndex].groupName === PermissionGroup.LOCATION + ) { + LocationCanvas({ viewState: $viewState }) } } }.constraintSize({ maxHeight: Constants.MAXIMUM_HEADER_HEIGHT }) - if (this.currentGroup().buttons.length <= 2 && this.calculateButtonWidth(this.currentGroup().buttons)) { + if ( + this.viewState.grantGroups[this.viewState.curIndex].buttonList.length <= 2 && + this.calculateButtonWidth(this.viewState.grantGroups[this.viewState.curIndex].buttonList) + ) { //横向布局 Row() { Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { - Button(buttonResource.get(this.currentGroup().buttons[0])) + Button(buttonResource.get(this.viewState.grantGroups[this.viewState.curIndex].buttonList[0])) .customizeButton() .onClick(() => { - this.clickHandle(this.currentGroup().buttons[0]); + this.processClick(this.viewState.grantGroups[this.viewState.curIndex].buttonList[0]); }) Divider() .color($r('sys.color.comp_divider')) @@ -192,33 +173,37 @@ struct dialogPlusPage { .height(Constants.DIVIDER_HEIGHT) .opacity(0.2) .margin({ left: Constants.MARGIN_8, right: Constants.MARGIN_8 }) - Button(buttonResource.get(this.currentGroup().buttons[1])) + Button(buttonResource.get(this.viewState.grantGroups[this.viewState.curIndex].buttonList[1])) .customizeButton() .onClick(() => { - this.clickHandle(this.currentGroup().buttons[1]); + this.processClick(this.viewState.grantGroups[this.viewState.curIndex].buttonList[1]); }) }.margin({ - left: Constants.BUTTON_MARGIN_LEFT, - right: Constants.BUTTON_MARGIN_RIGHT, + left: Constants.MARGIN_16, + right: Constants.MARGIN_16, bottom: Constants.MARGIN_8 }) } } else { //纵向布局 Column() { - ForEach(this.currentGroup().buttons, (buttonStatus: ButtonStatus, idx: number) => { + ForEach( + this.viewState.grantGroups[this.viewState.curIndex].buttonList, (buttonStatus: ButtonStatus, idx: number + ) => { Button(buttonResource.get(buttonStatus)) .customizeButton() .width(Constants.FULL_WIDTH) - .margin({ bottom: idx + 1 < this.currentGroup().buttons.length ? Constants.MARGIN_4 : 0 }) + .margin({ + bottom: idx + 1 < this.viewState.grantGroups[this.viewState.curIndex].buttonList.length ? + Constants.MARGIN_4 : 0 + }) .onClick(() => { - this.clickHandle(buttonStatus); + this.processClick(buttonStatus); }) }) } .padding({ left: Constants.PADDING_16, right: Constants.PADDING_16 }) } - } } .padding({ bottom: Constants.PADDING_8 }) .clip(true) @@ -227,22 +212,16 @@ struct dialogPlusPage { build() {} - showTitle(): string { - let index = this.count >= this.grantGroups.length ? this.grantGroups.length - 1 : this.count; - if (this.grantGroups[index].name == 'LOCATION') { - if (this.locationFlag == Constants.LOCATION_FUZZY) { - return 'app.string.access_general_location'; - } - if (this.locationFlag == Constants.LOCATION_UPGRADE) { - return 'app.string.fuzzy_to_exact'; + showReason(): ResourceStr { + if (this.viewState.grantGroups[this.viewState.curIndex].groupName === PermissionGroup.LOCATION) { + if ( + (this.viewState.locationFlag === Constants.LOCATION_FUZZY) || + (this.viewState.locationFlag === Constants.LOCATION_BOTH_FUZZY) + ) { + return $r('app.string.close_exact_position'); } } - return this.grantGroups[index].label; - } - - currentGroup(): GroupInfo { - let index = this.count >= this.grantGroups.length ? this.grantGroups.length - 1 : this.count; - return this.grantGroups[index]; + return this.viewState.grantGroups[this.viewState.curIndex].reason; } calculateButtonWidth(buttonStatus: ButtonStatus[]): boolean { @@ -261,354 +240,31 @@ struct dialogPlusPage { return true; } - clickHandle(buttonStatus: ButtonStatus) { - switch (buttonStatus) { - case ButtonStatus.ALLOW: - this.privacyAccept(this.grantGroups[this.count], this.accessTokenId, this.reqPerms, this.userFixedFlag); - return; - case ButtonStatus.DENY: - this.privacyCancel(this.grantGroups[this.count], this.accessTokenId, this.reqPerms, this.userFixedFlag); - return; - case ButtonStatus.CANCEL: - this.count ++; - return; - case ButtonStatus.THIS_TIME_ONLY: - this.privacyAccept( - this.grantGroups[this.count], this.accessTokenId, this.reqPerms, Constants.PERMISSION_ALLOW_THIS_TIME - ); - return; - case ButtonStatus.ALLOW_THIS_TIME: - this.privacyAccept( - this.grantGroups[this.count], this.accessTokenId, this.reqPerms, Constants.PERMISSION_ALLOW_THIS_TIME - ); - return; - case ButtonStatus.ALLOW_ONLY_DURING_USE: - this.privacyAccept(this.grantGroups[this.count], this.accessTokenId, this.reqPerms, this.userFixedFlag); - return; - } - } - - async privacyAccept(group: GroupInfo, accessTokenId: number, permissionList: string[], userFixedFlag: number) { - let num = 0; - group.permissions.forEach(async permission => { - this.grantStatus = -1; - if (showSubPermissionsGroup.indexOf(group.name) == -1) { - if (group.name == 'LOCATION') { - if (fuzzyMarks.includes(this.locationFlag) && permission === Permission.APPROXIMATELY_LOCATION) { - await this.operationPermission(true, accessTokenId, permission, userFixedFlag); - } - if (preciseMarks.includes(this.locationFlag) && permission === Permission.LOCATION) { - await this.operationPermission(true, accessTokenId, permission, userFixedFlag); - } - } else { - await this.operationPermission(true, accessTokenId, permission, userFixedFlag); - } - } else { - if (permissionList.includes(permission)) { - await this.operationPermission(true, accessTokenId, permission, userFixedFlag); - } - } - if (this.grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { - permissionList.forEach((req, idx) => { - if (req == permission) { - this.result[idx] = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; - } - }) - } - num ++; - if (num == group.permissions.length) { - this.count ++; - } + aboutToAppear() { + viewModel.processIntent(new GrantDialogIntent.InitIntent(this.context)).then(result => { + this.dialogController?.open(); }) - } - - async privacyCancel(group: GroupInfo, accessTokenId: number, permissionList: string[], userFixedFlag: number) { - group.permissions.forEach(async permission => { - if (showSubPermissionsGroup.indexOf(group.name) == -1) { - await this.operationPermission(false, accessTokenId, permission, userFixedFlag); - } else { - if (permissionList.includes(permission)) { - await this.operationPermission(false, accessTokenId, permission, userFixedFlag); - } - } + this.context.eventHub.on('refresh', () => { + this.refreshData(); }) - this.count ++; - } - - async operationPermission(status: boolean, token: number, permission: Permissions, flag: number) { - if (status) { - try { - Log.info('grantUserGrantedPermission: ' + permission); - await abilityAccessCtrl.createAtManager().grantUserGrantedPermission(token, permission, flag).then(() => { - this.grantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; - }) - } catch (err) { - Log.error('failed to grant permission: ' + permission); - } - } else { - try { - Log.info('revokeUserGrantedPermission: ' + permission) - await abilityAccessCtrl.createAtManager().revokeUserGrantedPermission(token, permission, flag); - } catch (err) { - Log.error('failed to revoke permission:' + permission); - } - } - } - - aboutToAppear() { - this.count = 0; - this.initStatus = Constants.INIT_NEED_TO_WAIT; - this.result = []; - this.reqPerms = this.want.parameters['ohos.user.grant.permission']; - this.accessTokenId = this.want.parameters['ohos.aafwk.param.callerToken']; - if (this.reqPerms == undefined || this.accessTokenId == undefined || this.reqPerms.length == 0) { - Log.info('invalid parameters'); - this.initStatus = Constants.INIT_NEED_TO_TERMINATED; - return; - } - Log.info(`request permission: ${JSON.stringify(this.reqPerms)}.`); - Log.info('permission state=' + JSON.stringify(this.want.parameters['ohos.user.grant.permission.state'])); - this.result = new Array(this.reqPerms.length).fill(-1); - this.getPasteBoardInfo(); - let bundleName: string = this.want.parameters['ohos.aafwk.param.callerBundleName']; - try { - bundleManager.getBundleInfo(bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION) - .then(bundleInfo => { - this.reqPermissionDetails = bundleInfo.reqPermissionDetails; - this.getGrantGroups(this.want.parameters['ohos.user.grant.permission.state']); - this.getApplicationName(bundleName); - this.dialogController?.open(); - }).catch((err: BusinessError) => { - Log.error('getBundleInfo error :' + JSON.stringify(err)); - this.initStatus = Constants.INIT_NEED_TO_TERMINATED; - }) - } catch (err) { - Log.error('getBundleInfo error :' + JSON.stringify(err)); - this.initStatus = Constants.INIT_NEED_TO_TERMINATED; - } + this.resourceChangeObserver.register(this.refreshData); } aboutToDisappear() { this.dialogController = null; + this.context.eventHub.off('refresh'); + this.resourceChangeObserver.unregister(); } - onPageShow() { - if (this.isUpdate > 0) { - this.getApplicationName(this.want.parameters['ohos.aafwk.param.callerBundleName']) - } - this.isUpdate ++; - } - - getPasteBoardInfo() { - if (this.reqPerms.includes(Permission.READ_PASTEBOARD)) { - let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); - this.pasteBoardName = systemPasteboard.getDataSource(); - } - } - - getGrantGroups(stateGroup: number[]) { - if (this.reqPerms.includes(Permission.APPROXIMATELY_LOCATION)) { - this.locationFlag = Constants.LOCATION_FUZZY; - if (this.reqPerms.includes(Permission.LOCATION)) { - this.locationFlag = Constants.LOCATION_BOTH_PRECISE; - let fuzzyIndex = this.reqPerms.indexOf(Permission.APPROXIMATELY_LOCATION); - if (stateGroup[fuzzyIndex] == Constants.PASS_OPER) { - this.locationFlag = Constants.LOCATION_UPGRADE; - } - } - } else if (this.reqPerms.includes(Permission.LOCATION)) { - this.locationFlag = Constants.LOCATION_UPGRADE; - } - - this.reqPerms.forEach(async (permission, idx) => { - if (permission === Permission.APP_TRACKING_CONSENT) { - let toggleStatus = await this.appTrackHandle(idx); - if (toggleStatus === abilityAccessCtrl.PermissionRequestToggleStatus.CLOSED) { - return; - } - } - if (stateGroup[idx] == Constants.PASS_OPER) { - Log.info('permission has been fixed:' + permission); - this.result[idx] = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; - } else if (stateGroup[idx] == Constants.DYNAMIC_OPER) { - let supportPermissions = supportPermission(); - if (!supportPermissions.includes(permission)) { - Log.info('The permission does not exist or is not supported by the current device: ' + permission); - } else { - this.addGroup(permission); - } - } - }) - this.initStatus = Constants.INIT_NEED_TO_VERIFY; - } - - async appTrackHandle(index: number): Promise { - try { - let acManager = abilityAccessCtrl.createAtManager(); - let toggleStatus = await acManager.getPermissionRequestToggleStatus(Permission.APP_TRACKING_CONSENT); - Log.info(`APP_TRACKING_CONSENT toggleStatus: ${toggleStatus}.`); - if (toggleStatus === abilityAccessCtrl.PermissionRequestToggleStatus.CLOSED) { - await acManager.grantUserGrantedPermission( - this.accessTokenId, Permission.APP_TRACKING_CONSENT, this.userFixedFlag - ); - this.result[index] = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; - Log.info('APP_TRACKING_CONSENT grant success.'); - } - return toggleStatus; - } catch (err) { - Log.error(`APP_TRACKING_CONSENT getToggleStatus or grant fail: ${JSON.stringify(err)}`); - return abilityAccessCtrl.PermissionRequestToggleStatus.OPEN; - } - } - - addGroup(permission: Permission) { - let group = getPermissionGroup(permission); - if (group.name === 'FOLDER') { - switch (permission) { - case Permission.READ_WRITE_DOWNLOAD_DIRECTORY: - let downloadGroup = new GroupInfo( - group.name, group.groupName, 'app.string.group_label_download_folder', group.icon, group.description, - group.reason, [Permission.READ_WRITE_DOWNLOAD_DIRECTORY], group.buttons, group.isShow - ) - this.grantGroups.push(downloadGroup); - break; - case Permission.READ_WRITE_DESKTOP_DIRECTORY: - let desktopGroup = new GroupInfo( - group.name, group.groupName, 'app.string.group_label_desktop_folder', group.icon, group.description, - group.reason, [Permission.READ_WRITE_DESKTOP_DIRECTORY], group.buttons, group.isShow - ) - this.grantGroups.push(desktopGroup); - break; - case Permission.READ_WRITE_DOCUMENTS_DIRECTORY: - let documentGroup = new GroupInfo( - group.name, group.groupName, 'app.string.group_label_document_folder', group.icon, group.description, - group.reason, [Permission.READ_WRITE_DOCUMENTS_DIRECTORY], group.buttons, group.isShow - ) - this.grantGroups.push(documentGroup); - break; - } - return - } - let exist = this.grantGroups.find(grantGroup => grantGroup.name == group.name); - if (showSubPermissionsGroup.indexOf(group.name) != -1) { - let label = getPermissionLabel(permission) - if (!exist) { - group.description.push(label); - this.grantGroups.push(group); - } else { - if (exist.description.indexOf(label) == -1) { - exist.description.push($r('app.string.and')); - exist.description.push(label); - } - } - } else { - if (!exist) { - this.grantGroups.push(group); - } - } - } - - getApplicationName(bundleName: string) { - Log.info('getApplicationName bundleName:' + bundleName); - bundleManager.getApplicationInfo(bundleName, bundleManager.ApplicationFlag.GET_APPLICATION_INFO_DEFAULT) - .then(applicationInfo => { - let context = this.context.createBundleContext(bundleName); - context.resourceManager.getStringValue(applicationInfo.labelId, (err, value) => { - if (value == undefined) { - this.appName = titleTrim(applicationInfo.label); - } else { - this.appName = titleTrim(value); - } - Log.info('hap label:' + applicationInfo.label + ', value:' + this.appName); - }) - }).catch((err: BusinessError) => { - Log.error('applicationInfo error :' + err); - this.initStatus = Constants.INIT_NEED_TO_TERMINATED; - }) - this.grantGroups.forEach((group) => { - group.reason = ''; - this.getReason(group, bundleName); - }) - } - - getReason(group: GroupInfo, bundleName: string) { - group.permissions.forEach(permission => { - if (this.reqPerms.indexOf(permission) != -1) { - this.reqPermissionDetails.forEach(reqPermissionDetail => { - if (reqPermissionDetail.name == permission) { - Log.info('reqPermissionDetail: ' + JSON.stringify(reqPermissionDetail)); - let context = this.context.createModuleContext(bundleName, reqPermissionDetail.moduleName); - context.resourceManager.getStringValue(reqPermissionDetail.reasonId, (err, value) => { - if (value !== undefined && group.reason === '') { - group.reason = value.slice(Constants.START_SUBSCRIPT, Constants.END_SUBSCRIPT); - this.refresh ++; - } - this.initStatus = Constants.INIT_NEED_TO_REFRESH; - }) - } - }) - } - }) - } - - verify() { - if ((this.initStatus == Constants.INIT_NEED_TO_TERMINATED) || (this.count >= this.grantGroups.length)) { - this.answerRequest(); - this.initStatus = Constants.INIT_NEED_TO_WAIT; - return false; - } - return true; - } - - answerRequest() { - let ret: number = Constants.RESULT_SUCCESS; - if (this.initStatus == Constants.INIT_NEED_TO_TERMINATED) { - ret = Constants.RESULT_FAILURE; + processClick(buttonStatus: ButtonStatus) { + let groupConfig: PermissionGroupConfig = this.viewState.grantGroups[this.viewState.curIndex]; + if (callerAppInfo === undefined) { + Log.error(`processClick faild, callerAppInfo is undefined.`); + return; } - this.answer(ret, this.reqPerms); - } - - answer(ret: number, reqPerms: string[]) { - Log.info('code:' + ret + ', perms=' + JSON.stringify(reqPerms) + ', result=' + JSON.stringify(this.result)); - let perms: string[] = []; - let results: number[] = []; - reqPerms.forEach(perm => { - perms.push(perm); - }) - this.result.forEach(result => { - results.push(result); - }) - let option = new rpc.MessageOption(); - let data = new rpc.MessageSequence(); - let setDialogData = new rpc.MessageSequence(); - let reply = new rpc.MessageSequence(); - Promise.all([ - data.writeInterfaceToken(Constants.ACCESS_TOKEN), - data.writeStringArray(perms), - data.writeIntArray(results), - setDialogData.writeInterfaceToken(Constants.ACCESS_TOKEN), - ]).then(() => { - let proxy = this.want.parameters['ohos.ability.params.callback'].value as rpc.RemoteObject; - proxy.sendMessageRequest(Constants.RESULT_CODE, data, reply, option); - proxy.sendMessageRequest(Constants.RESULT_CODE_1, setDialogData, reply, option); - }).catch(() => { - Log.error('write result failed!'); - }).finally(() => { - data.reclaim(); - reply.reclaim(); - setDialogData.reclaim(); - this.destruction(); - }) + let clickIntent: GrantDialogIntent.ClickIntent = + new GrantDialogIntent.ClickIntent(this.context, groupConfig, callerAppInfo, buttonStatus); + viewModel.processIntent(clickIntent); } - destruction() { - let windowNum: number = GlobalContext.load('windowNum'); - windowNum --; - Log.info('windowNum:' + windowNum); - GlobalContext.store('windowNum', windowNum); - this.win.destroyWindow(); - if (windowNum == 0) { - this.context.terminateSelf(); - } - } } \ No newline at end of file diff --git a/permissionmanager/src/main/module.json b/permissionmanager/src/main/module.json index cf61872d62abc3bea306a9415ebe7e4a569a20c4..b4e8048db26bf10c1b83ad2677734340dee39ce8 100644 --- a/permissionmanager/src/main/module.json +++ b/permissionmanager/src/main/module.json @@ -55,7 +55,7 @@ { "icon": "$media:app_icon", "name": "com.ohos.permissionmanager.GrantAbility", - "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ts", + "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets", "type": "service", "exported": true }, diff --git a/permissionmanager/src/main/module.json5 b/permissionmanager/src/main/module.json5 index 4b48992d7dcd5de02a5c477834f4f0779b740f6e..3dc50f0f9856b69d4bfe5241868ca8fa2831b965 100644 --- a/permissionmanager/src/main/module.json5 +++ b/permissionmanager/src/main/module.json5 @@ -67,7 +67,7 @@ { "icon": "$media:app_icon", "name": "com.ohos.permissionmanager.GrantAbility", - "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ts", + "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets", "type": "service", "exported": true },