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