1/* 2 * Copyright (c) 2023 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 bundleResourceManager from '@ohos.bundle.bundleResourceManager'; 17import display from '@ohos.display'; 18import window from '@ohos.window'; 19import { titleTrim } from '../common/utils'; 20import Constants from '../common/constant'; 21import fs from '@ohos.file.fs'; 22import configPolicy from '@ohos.configPolicy'; 23import { EnableNotificationDialog } from '../ServiceExtAbility/NotificationServiceExtAbility'; 24import { Callback} from '@ohos.base'; 25import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 26 27const TAG = 'NotificationDialog_Service '; 28const permission: Record<string, Resource> = { 29 'label': $r('app.string.group_label_notification'), 30 'icon': $r('app.media.ic_public_ring'), 31 'reason': $r('app.string.reason'), 32}; 33 34let storage = LocalStorage.getShared(); 35 36@Entry(storage) 37@Component 38struct NotificationDialogPage { 39 @StorageLink('isUpdate') isUpdate: number = -1; 40 privacyDialogController: CustomDialogController = new CustomDialogController({ 41 builder: PermissionDialog({ isUpdate: $isUpdate }), 42 autoCancel: false, 43 alignment: DialogAlignment.TopStart, 44 customStyle: false, 45 onWillDismiss: (dismissDialogAction: DismissDialogAction) => { 46 console.info(TAG, `dialog onWillDismiss reason= : ${JSON.stringify(dismissDialogAction.reason)}`); 47 }, 48 width: '466px', 49 height: '466px' 50 }); 51 52 build() {} 53 54 aboutToAppear() { 55 this.privacyDialogController.open(); 56 } 57 58 onPageShow() { 59 this.isUpdate++; 60 } 61} 62 63@CustomDialog 64struct PermissionDialog { 65 @State appName: string = ''; 66 @State isBottomPopover: boolean = true; 67 @StorageLink('clicked') clicked: boolean = false; 68 @Link @Watch('updateOnPageShow') isUpdate: number; 69 dialog?: EnableNotificationDialog; 70 session?: UIExtensionContentSession; 71 controller?: CustomDialogController; 72 73 build() { 74 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 75 Column() { 76 Image(permission.icon) 77 .width('92px') 78 .height('92px') 79 .margin({ 80 top: '2%' 81 }) 82 83 Scroll() { 84 Column() { 85 Row() { 86 Flex({ justifyContent: FlexAlign.Center }) { 87 Text() { 88 Span(getContext(this).resourceManager.getStringSync(permission.label.id).replace('%s', this.appName)) 89 } 90 .fontSize('38px') 91 .fontColor('#FFFFFF') 92 .fontWeight(500) 93 .lineHeight('45px') 94 .margin({ 95 top: '4.1%', 96 left: '11.2%', 97 right: '11.2%' 98 }) 99 .textAlign(TextAlign.Center) 100 } 101 } 102 103 Row() { 104 Flex({ justifyContent: FlexAlign.Center }) { 105 Button($r('app.string.ALLOW')) 106 .onClick(async (): Promise<void> => { 107 await this.enableNotification(true); 108 }) 109 .backgroundColor('#1F71FF') 110 .width('232px') 111 .height('80px') 112 .fontColor('#FFFFFF') 113 .fontSize('30px') 114 .fontWeight(500) 115 } 116 .margin({top:'4.1%'}) 117 } 118 119 Row() { 120 Flex({ justifyContent: FlexAlign.Center }) { 121 Button($r('app.string.BAN')) 122 .onClick(async (): Promise<void> => { 123 await this.enableNotification(false); 124 }) 125 .backgroundColor('#255EA1FF') 126 .width('232px') 127 .height('80px') 128 .fontColor('#5EA1FF') 129 .fontSize('30px') 130 .fontWeight(500) 131 } 132 .margin({top:'4.1%'}) 133 } 134 } 135 } 136 .scrollable(ScrollDirection.Vertical) 137 .scrollBar(BarState.Off) 138 } 139 } 140 .margin({ 141 top: 0, 142 left: 0, 143 right: 0, 144 bottom: 0 145 }) 146 .width('100%') 147 .height('100%') 148 .backgroundColor('#35000000') 149 .backgroundEffect({ 150 radius: 20, 151 saturation: 50, 152 adaptiveColor: AdaptiveColor.AVERAGE, 153 blurOptions: { 154 grayscale: [35, 35] 155 } 156 }) 157 158 } 159 160 async updateApplicationName(bundleName: string): Promise<void> { 161 console.info(TAG, `updateApplicationName bundleName: ${bundleName}`); 162 try { 163 let bundleFlags = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_ALL; 164 let resourceInfo = bundleResourceManager.getBundleResourceInfo(bundleName, bundleFlags); 165 console.info(TAG, `applicationName name : ${JSON.stringify(resourceInfo.label)}`); 166 let appName = resourceInfo.label; 167 this.appName = titleTrim(appName); 168 console.info(TAG, `hap label: ${this.appName}`); 169 } catch (err) { 170 console.error(TAG, `applicationName error : ${err?.code}`); 171 } 172 } 173 174 async updateIsBottomPopover(): Promise<void> { 175 let dis = display.getDefaultDisplaySync(); 176 let isVertical = dis.width <= dis.height; 177 try { 178 if (display.isFoldable()) { 179 let foldStatus = display.getFoldStatus(); 180 if (foldStatus == display.FoldStatus.FOLD_STATUS_EXPANDED || 181 foldStatus == display.FoldStatus.FOLD_STATUS_HALF_FOLDED) { 182 this.isBottomPopover = false; 183 return; 184 } 185 } 186 } catch (err) { 187 console.error(TAG, 'Failed to get the device foldable status. Code: ${err?.code}'); 188 } 189 190 // read ccm configs 191 let isBottomPopoverTemp = false; 192 try { 193 let filePaths = await configPolicy.getCfgFiles(Constants.CCM_CONFIG_PATH); 194 for (let i = 0; i < filePaths.length; i++) { 195 let res = fs.accessSync(filePaths[i]); 196 if (res) { 197 let fileContent = fs.readTextSync(filePaths[i]); 198 let config: NotificationConfig = JSON.parse(fileContent); 199 if (config.notificationAuthorizationWindow != undefined) { 200 let windowConfig: NotificationAuthorizationWindow = config.notificationAuthorizationWindow; 201 if (windowConfig.isBottomPopover != undefined) { 202 isBottomPopoverTemp = windowConfig.isBottomPopover; 203 } 204 } 205 } 206 } 207 } catch (error) { 208 console.log(TAG, 'Failed get ccm files, Cause: ${err?.code}'); 209 } 210 this.isBottomPopover = isBottomPopoverTemp && isVertical; 211 } 212 213 async updateStatus(): Promise<void> { 214 let bundleNameObj = this.dialog?.want.parameters?.bundleName; 215 let bundleName = bundleNameObj ? bundleNameObj.toString() : ''; 216 await this.updateApplicationName(bundleName); 217 await this.updateIsBottomPopover(); 218 } 219 220 async updateOnPageShow(): Promise<void> { 221 if (this.isUpdate > 0) { 222 await this.updateStatus(); 223 } 224 } 225 226 async aboutToAppear(): Promise<void> { 227 this.dialog = storage.get('dialog') as EnableNotificationDialog; 228 this.session = storage.get('session') as UIExtensionContentSession; 229 try { 230 await this.updateStatus(); 231 } catch (err) { 232 console.error(TAG, `aboutToAppear error : ${err?.code}`); 233 await this.dialog?.destroyException(); 234 await this.session?.terminateSelf(); 235 } 236 } 237 238 async aboutToDisappear(): Promise<void> { 239 console.info(TAG, `aboutToDisappear`); 240 this.session?.terminateSelf(); 241 } 242 243 async enableNotification(enabled: boolean): Promise<void> { 244 console.info(TAG, `NotificationDialog enableNotification: ${enabled}`); 245 try { 246 await this.dialog?.publishButtonClickedEvent(enabled); 247 this.clicked = true; 248 } catch (err) { 249 console.error(TAG, `NotificationDialog enable error, code is ${err?.code}`); 250 await this.dialog?.destroyException(); 251 } finally { 252 await this.dialog?.subWindow?.destroyWindow(); 253 this.session?.terminateSelf(); 254 } 255 } 256} 257 258interface NotificationConfig { 259 notificationAuthorizationWindow: NotificationAuthorizationWindow; 260} 261 262interface NotificationAuthorizationWindow { 263 isBottomPopover: boolean; 264} 265 266interface NotificationConfigAbc { 267 deviceType: DeviceType; 268} 269 270interface DeviceType { 271 isWatch: boolean; 272} 273