1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; 17import { BusinessError } from '@ohos.base'; 18import audio from '@ohos.multimedia.audio'; 19import camera from '@ohos.multimedia.camera'; 20import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; 21import { Log, PermissionDialogException, PermissionDialogReturn, GlobalDialogReturn } from '../common/utils/utils'; 22import { GroupInfo, WantInfo } from '../common/model/typedef'; 23import { GlobalContext } from '../common/utils/globalContext'; 24import Constants from '../common/utils/constant'; 25import { globalGroup, groups } from '../common/model/permissionGroup'; 26import bundleManager from '@ohos.bundle.bundleManager'; 27import { getCallerBundleInfo } from './PermissionStateSheetDialog'; 28import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 29import common from '@ohos.app.ability.common'; 30import { PermissionGroup } from '../common/model/definition'; 31 32 33let globalIsOn: boolean; 34let session: UIExtensionContentSession; 35let bundleName = ''; 36 37@Entry({ useSharedStorage: true }) 38@Component 39struct GlobalSwitchSheetDialog { 40 @LocalStorageLink('want') want: Want | null = null; 41 @LocalStorageLink('session') session: UIExtensionContentSession = {} as UIExtensionContentSession; 42 private context = this.getUIContext().getHostContext() as common.UIExtensionContext; 43 private muteSuppoted = false; 44 private groupName: PermissionGroup = PermissionGroup.OTHER; 45 dialogController: CustomDialogController | null = new CustomDialogController({ 46 builder: CustomContentDialog({ 47 contentBuilder: () => { 48 this.buildDialog(); 49 }, 50 contentAreaPadding: {left: 0, right: 0} 51 }), 52 offset: { dx: 0, dy: `-${GlobalContext.load('avoidAreaHeight') as string}` }, // unit included in globalContext 53 alignment: DialogAlignment.Bottom, 54 customStyle: false, 55 isModal: true, 56 width: '100%', 57 autoCancel: false, 58 cornerRadius: { 59 topLeft: 32, 60 topRight: 32, 61 bottomLeft: 0, 62 bottomRight: 0 63 }, 64 cancel: () => { 65 GlobalDialogReturn(false, session); 66 this.context.terminateSelf(); 67 this.dialogController?.close(); 68 } 69 }); 70 71 @Builder 72 buildDialog() { 73 applicationItem({ 74 isMuteSupported: this.muteSuppoted, 75 currentGroup: this.groupName 76 }) 77 } 78 79 async aboutToAppear() { 80 session = this.session; 81 Log.info('GlobalSwitchSheetDialog aboutToAppear'); 82 Log.info('GlobalSwitchSheetDialog getWant' + JSON.stringify(this.want)); 83 let callerBundle = getCallerBundleInfo(this.want as Object as WantInfo); 84 Log.info('GlobalSwitchSheetDialog bundleName ' + callerBundle.bundleName); 85 let globalSwitch = callerBundle.globSwitch; 86 bundleName = callerBundle.bundleName; 87 88 if (globalSwitch === Constants.GLOBAL_SWITCH_CAMERA) { 89 this.groupName = PermissionGroup.CAMERA; 90 } else if (globalSwitch === Constants.GLOBAL_SWITCH_MICROPHONE) { 91 this.groupName = PermissionGroup.MICROPHONE; 92 } else { 93 Log.error('global switch not suppoted'); 94 PermissionDialogException(Constants.ERR_GLOBAL_SWITCH_NOT_SUPPORTED, session); 95 this.context.terminateSelf(); 96 return; 97 } 98 99 if (this.groupName == 'MICROPHONE') { 100 let audioManager = audio.getAudioManager(); 101 let audioVolumeManager = audioManager.getVolumeManager(); 102 let groupid = audio.DEFAULT_VOLUME_GROUP_ID; 103 let audioVolumeGroupManager = await audioVolumeManager.getVolumeGroupManager(groupid); 104 this.muteSuppoted = true; 105 globalIsOn = !audioVolumeGroupManager.isPersistentMicMute(); 106 } else { 107 let cameraManager = camera.getCameraManager(this.context); 108 this.muteSuppoted = cameraManager.isCameraMuteSupported(); 109 globalIsOn = !cameraManager.isCameraMuted(); 110 } 111 112 if (this.muteSuppoted == false) { 113 Log.error('global switch muted'); 114 PermissionDialogException(Constants.ERR_GLOBAL_SWITCH_NOT_SUPPORTED, session); 115 this.context.terminateSelf(); 116 return; 117 } 118 119 if (globalIsOn) { 120 Log.error('global switch is on'); 121 PermissionDialogException(Constants.ERR_GLOBAL_SWITCH_IS_ON, session); 122 this.context.terminateSelf(); 123 return; 124 } 125 126 Log.info('isMuted ' + globalIsOn); 127 this.dialogController?.open(); 128 } 129 130 onPageShow(): void { 131 Log.info('GlobalSwitchSheetDialog onPageShow' ); 132 } 133 134 build() { 135 } 136} 137 138@CustomDialog 139struct applicationItem { 140 private context = this.getUIContext().getHostContext() as common.UIExtensionContext; 141 @State globalIsOn: boolean = false; 142 @State backTitle: string = ''; 143 @State groupInfo: GroupInfo = new GroupInfo(PermissionGroup.OTHER, '', '', '', [], '', [], [], false); 144 @State currentGroup: PermissionGroup = PermissionGroup.OTHER; 145 @State isMuteSupported: boolean = false; 146 @State appName: ResourceStr = ''; 147 private controller: CustomDialogController; 148 149 dialogController: CustomDialogController | null = new CustomDialogController({ 150 builder: CustomContentDialog({ 151 contentBuilder: () => { 152 this.buildContent(); 153 }, 154 contentAreaPadding: { left: Constants.PADDING_24, right: Constants.PADDING_24 }, 155 buttons: [ 156 { 157 value: $r('app.string.cancel'), 158 buttonStyle: ButtonStyleMode.TEXTUAL, 159 action: () => { 160 Log.info('global cancel'); 161 if (this.dialogController !== null) { 162 this.dialogController.close(); 163 } 164 } 165 }, 166 { 167 value: $r('app.string.close'), 168 buttonStyle: ButtonStyleMode.TEXTUAL, 169 action: () => { 170 Log.info('global accept'); 171 if (this.currentGroup == 'MICROPHONE') { 172 let audioManager = audio.getAudioManager(); 173 let audioVolumeManager = audioManager.getVolumeManager(); 174 audioVolumeManager.getVolumeGroupManager(audio.DEFAULT_VOLUME_GROUP_ID).then(audioVolumeGroupManager => { 175 audioVolumeGroupManager.setMicMutePersistent(true, audio.PolicyType.PRIVACY).then(() => { 176 Log.info('microphone muted'); 177 if (this.dialogController !== null) { 178 this.dialogController.close(); 179 } 180 }) 181 }) 182 } else { 183 let cameraManager = camera.getCameraManager(this.context); 184 cameraManager.muteCameraPersistent(true, camera.PolicyType.PRIVACY); 185 if (this.dialogController !== null) { 186 this.dialogController.close(); 187 } 188 } 189 } 190 } 191 ], 192 }), 193 autoCancel: false 194 }); 195 196 globalListen() { 197 if (this.currentGroup == 'CAMERA') { 198 let cameraManager = camera.getCameraManager(this.context); 199 cameraManager.on('cameraMute', (err, curMuted) => { 200 Log.info('curMuted: ' + JSON.stringify(curMuted) + ' err: ' + JSON.stringify(err)); 201 this.globalIsOn = !curMuted; 202 }) 203 } else { 204 let audioManager = audio.getAudioManager(); 205 let audioVolumeManager = audioManager.getVolumeManager(); 206 let groupId = audio.DEFAULT_VOLUME_GROUP_ID; 207 audioVolumeManager.getVolumeGroupManager(groupId).then(audioVolumeGroupManager => { 208 audioVolumeGroupManager.on('micStateChange', micStateChange => { 209 Log.info('micStateChange: ' + JSON.stringify(micStateChange)); 210 this.globalIsOn = !micStateChange.mute; 211 }) 212 }) 213 } 214 } 215 216 @Builder 217 buildContent(): void { 218 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 219 Column() { 220 Text(this.currentGroup == 'CAMERA' ? $r('app.string.close_camera') : $r('app.string.close_microphone')) 221 .fontSize(Constants.TEXT_BIG_FONT_SIZE) 222 .fontColor($r('sys.color.font_primary')) 223 .fontWeight(FontWeight.Medium) 224 .lineHeight(Constants.TEXT_BIG_LINE_HEIGHT) 225 .width(Constants.FULL_WIDTH) 226 .padding({ top: Constants.PADDING_14, bottom: Constants.PADDING_14 }) 227 Text( 228 this.currentGroup == 'CAMERA' ? 229 $r('app.string.close_camera_desc') : 230 $r('app.string.close_microphone_desc') 231 ) 232 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 233 .fontColor($r('sys.color.font_primary')) 234 .lineHeight(Constants.TEXT_LINE_HEIGHT) 235 } 236 .clip(true) 237 } 238 } 239 240 /** 241 * Grant permissions to the app 242 * @param {Number} accessTokenId 243 * @param {String} permission permission name 244 * @param {Number} index Array index to modify permission status 245 */ 246 grantUserGrantedPermission(accessTokenId: number, permission: Permissions, resolve: (value: number) => void) { 247 abilityAccessCtrl.createAtManager().grantUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG) 248 .then(() => { 249 resolve(0); 250 }).catch((error: BusinessError) => { 251 resolve(-1); 252 Log.error('grantUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 253 }) 254 } 255 256 /** 257 * Deauthorize the app 258 * @param {Number} accessTokenId 259 * @param {String} permission permission name 260 * @param {Number} index Array index to modify permission status 261 */ 262 revokeUserGrantedPermission(accessTokenId: number, permission: Permissions, resolve: (value: number) => void) { 263 abilityAccessCtrl.createAtManager().revokeUserGrantedPermission( 264 accessTokenId, permission, Constants.PERMISSION_FLAG 265 ).then(() => { 266 resolve(0); 267 }).catch((error: BusinessError) => { 268 resolve(-1); 269 Log.error('revokeUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 270 }) 271 } 272 273 /** 274 * Lifecycle function, executed when the page is initialized 275 */ 276 async aboutToAppear() { 277 try { 278 let bundleInfo = await bundleManager.getBundleInfo( 279 bundleName, 280 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); 281 let manager = this.context.createBundleContext(bundleName).resourceManager; 282 this.appName = await manager.getStringValue(bundleInfo.appInfo.labelId); 283 } catch (e) { 284 Log.error('get appName failed ' + JSON.stringify(e)); 285 } 286 groups.forEach(group => { 287 if (group.name === this.currentGroup) { 288 this.groupInfo = group; 289 } 290 }) 291 this.backTitle = this.groupInfo.label; 292 this.globalIsOn = globalIsOn; 293 if (globalGroup.indexOf(this.currentGroup) !== -1) { 294 this.globalListen(); 295 } 296 } 297 298 aboutToDisappear() { 299 this.dialogController = null; 300 } 301 302 build() { 303 Column() { 304 Row() { 305 Flex({ alignItems: ItemAlign.Start, justifyContent: FlexAlign.Start }) { 306 Column() { 307 Column() { 308 Text($r(this.backTitle, this.appName)) 309 .align(Alignment.Start) 310 .fontColor($r('sys.color.font_primary')) 311 .maxLines(Constants.MAXIMUM_HEADER_LINES) 312 .textOverflow({ overflow: TextOverflow.Ellipsis }) 313 .fontSize(Constants.TEXT_BIG_FONT_SIZE) 314 .flexGrow(Constants.FLEX_GROW) 315 .fontWeight(FontWeight.Bold) 316 .padding({left: Constants.PADDING_10, top: Constants.PADDING_20}) 317 } 318 .width(Constants.FULL_WIDTH) 319 .alignItems(HorizontalAlign.Start) 320 if (globalGroup.indexOf(this.currentGroup) !== -1 && this.isMuteSupported === true) { 321 Row() { 322 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 323 Text(this.currentGroup == 'CAMERA' ? $r('app.string.camera') : $r('app.string.microphone')) 324 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE).fontColor($r('sys.color.font_primary')) 325 .fontWeight(FontWeight.Medium) 326 Row() { 327 Toggle({ type: ToggleType.Switch, isOn: this.globalIsOn }) 328 .selectedColor($r('sys.color.icon_emphasize')) 329 .switchPointColor($r('sys.color.comp_background_primary_contrary')) 330 .onChange((isOn: boolean) => { 331 if (isOn) { 332 if (this.currentGroup == 'CAMERA') { 333 let cameraManager = camera.getCameraManager(this.context); 334 cameraManager.muteCameraPersistent(false, camera.PolicyType.PRIVACY); 335 GlobalDialogReturn(true, session); 336 this.context.terminateSelf(); 337 } else { 338 let audioManager = audio.getAudioManager(); 339 let audioVolumeManager = audioManager.getVolumeManager(); 340 let groupId = audio.DEFAULT_VOLUME_GROUP_ID; 341 audioVolumeManager.getVolumeGroupManager(groupId).then(audioVolumeGroupManager => { 342 audioVolumeGroupManager.setMicMutePersistent(false, audio.PolicyType.PRIVACY); 343 GlobalDialogReturn(true, session); 344 this.context.terminateSelf(); 345 }) 346 } 347 } 348 }) 349 Row().onClick(() => { 350 if (this.dialogController !== null) { 351 this.dialogController.open(); 352 } 353 }) 354 .width(Constants.DEFAULT_SLIDER_WIDTH).height(Constants.DEFAULT_SLIDER_HEIGHT) 355 .position({ x: this.globalIsOn ? 0 : Constants.OFFSET, y: 0 }) 356 }.clip(true) 357 }.height(Constants.LISTITEM_ROW_HEIGHT) 358 .padding({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 359 }.padding({ top: Constants.LIST_PADDING_TOP, bottom: Constants.LIST_PADDING_BOTTOM }) 360 .backgroundColor($r('sys.color.comp_background_list_card')) 361 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 362 .margin({ top: Constants.MARGIN_16 }) 363 } 364 }.padding({ left: Constants.AUTHORITY_LISTITEM_PADDING_LEFT }) 365 }.flexGrow(Constants.FLEX_GROW) 366 } 367 } 368 } 369}