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