• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
20  titleTrim,
21  calContainerWidth,
22  getFontSizeScale,
23  sourceToVp,
24  getLimitFontSize } from '../common/utils';
25import Constants from '../common/constant';
26import fs from '@ohos.file.fs';
27import configPolicy from '@ohos.configPolicy';
28import { EnableNotificationDialog } from '../ServiceExtAbility/NotificationServiceExtAbility';
29import { Callback} from '@ohos.base';
30import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
31import i18n from '@ohos.i18n';
32import { MeasureOptions } from '@ohos.measure';
33import { MeasureUtils } from '@ohos.arkui.UIContext';
34import common from '@ohos.app.ability.common';
35
36const TAG = 'NotificationDialog_Service ';
37
38const LAN_EN = 'These may include banners, sounds and more. You can change this in Settings.';
39const LAN_CN = '通知提醒方式可能包括横幅、响铃等。可前往“设置” 更改。';
40const LAN_HK = '通知提醒方式可能包括橫幅、響鬧等。可前往「設定」更改。';
41const LAN_TW = '通知提醒方式可能包括橫幅、響鈴等。可前往「設定」變更。';
42const LAN_BO = 'བརྡ་ཐོ་དྲན་སྐུལ་གྱི་ཐབས་ལམ་ལ་སྒོ་ལྕགས་བརྙན་ཡོལ་དང་། འཕྲེད་བྱང་། དྲིལ་སྒྲ། སྦིར་བརྡ་སོགས་ཚུད་སྲིད། ༼སྒྲིག་འགོད་༽ལ་བསྐྱོད་ནས་བཅོས་ཆོག';
43const LAN_UG = 'قۇلۇپ ئېكرانى، بالداق، ئاۋاز ۋە تىترەش شۇلارنىڭ ئىچىدە. تەڭشەكتىن ئۆزگەرتەلەيسىز.';
44
45const permission: Record<string, Resource> = {
46  'label': $r('app.string.group_label_notification'),
47  'icon': $r('app.media.ic_public_ring'),
48  'reason': $r('app.string.reason'),
49};
50
51let storage = LocalStorage.getShared();
52
53@Extend(Button) function customizeButton() {
54  .type(ButtonType.Normal)
55  .backgroundColor($r('sys.color.comp_background_tertiary'))
56  .fontColor($r('sys.color.font_emphasize'))
57  .fontSize(
58    getLimitFontSize(
59      sourceToVp($r('sys.float.Body_L')),
60      getFontSizeScale(getContext(this) as common.UIAbilityContext, Constants.FONT_SCALE_MAX))
61  )
62  .fontWeight(FontWeight.Medium)
63  .height(Constants.PC_BUTTON_HEIGHT)
64  .width('50%')
65  .borderRadius(Constants.PC_RDIUS_8)
66  .flexGrow(Constants.FLEX_GROW)
67}
68
69@Entry(storage)
70@Component
71struct NotificationDialogPage {
72  @StorageLink('isUpdate') isUpdate: number = 0;
73
74  privacyDialogController: CustomDialogController = new CustomDialogController({
75    builder: PermissionDialog({ isUpdate: $isUpdate }),
76    autoCancel: false,
77    alignment: DialogAlignment.Center,
78    customStyle: true,
79    cornerRadius: Constants.PC_RDIUS_16,
80    maskColor: $r('sys.color.ohos_id_color_mask_thin'),
81    onWillDismiss: (dismissDialogAction: DismissDialogAction) => {
82      console.info(TAG, `dialog onWillDismiss reason= : ${JSON.stringify(dismissDialogAction.reason)}`);
83    }
84  });
85
86  build() {}
87
88  aboutToAppear() {
89    this.privacyDialogController.open();
90  }
91
92  onPageShow() {
93  }
94}
95
96@CustomDialog
97struct PermissionDialog {
98  @State appName: string = '';
99  @State content: string = LAN_CN;
100  @State naviHeight: number = 0;
101  @State isBottomPopover: boolean = false;
102  @StorageLink('clicked') clicked: boolean = false;
103  @Link @Watch('updateOnPageShow') isUpdate: number;
104  dialog?: EnableNotificationDialog;
105  session?: UIExtensionContentSession;
106  controller?: CustomDialogController;
107  @State titleContainerWidth: string | number = 'auto';
108
109  build() {
110    Row() {
111      Flex({ justifyContent: FlexAlign.Center, alignItems: this.isBottomPopover ? ItemAlign.End : ItemAlign.Center }) {
112        Column() {
113          Scroll() {
114            Column() {
115              Row() {
116                Image(permission.icon)
117                .width(Constants.DIALOG_ICON_WIDTH)
118                .height(Constants.DIALOG_ICON_HEIGHT)
119                .margin({
120                  top: Constants.PC_ICON_MARGIN_TOP
121                })
122                .draggable(false)
123              }
124              Row() {
125                Flex({ justifyContent: FlexAlign.Center }) {
126                  Text($r('app.string.group_label_notification', this.appName))
127                  .fontSize($r('sys.float.Title_S'))
128                  .fontColor($r('sys.color.font_primary'))
129                  .fontWeight(FontWeight.Bold)
130                  .minFontSize(
131                    getLimitFontSize(Constants.TITLE_MIN_FONT_SIZE,
132                      getFontSizeScale(getContext(this) as common.UIAbilityContext, Constants.FONT_SCALE_MAX))
133                  )
134                  .maxFontSize(
135                    getLimitFontSize(sourceToVp($r('sys.float.Title_S')),
136                      getFontSizeScale(getContext(this) as common.UIAbilityContext, Constants.FONT_SCALE_MAX))
137                  )
138                  .heightAdaptivePolicy(TextHeightAdaptivePolicy.MAX_LINES_FIRST)
139                  .maxLines(2)
140                  .textOverflow({overflow: TextOverflow.Ellipsis})
141                  .width(this.titleContainerWidth)
142                  .textAlign(TextAlign.Center)
143                }
144                .margin({
145                  left: Constants.PC_TITLE_MARGIN_SIDE,
146                  right: Constants.PC_TITLE_MARGIN_SIDE,
147                  top: Constants.PC_TITLE_MARGIN_TOP,
148                })
149                .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
150                    let containerWidth = newValue.width as number;
151                    let options: MeasureOptions = {
152                      textContent: $r('app.string.group_label_notification', this.appName),
153                      fontSize: getLimitFontSize(sourceToVp($r('sys.float.Title_S')),
154                      getFontSizeScale(getContext(this) as common.UIAbilityContext, Constants.FONT_SCALE_MAX)),
155                      fontWeight: FontWeight.Bold,
156                    };
157                    this.titleContainerWidth = calContainerWidth(containerWidth, options,
158                      Constants.CROSS_LINE_RATIO, this.getUIContext().getMeasureUtils());
159                    console.info(TAG, `onSizeChange titleContainerWidth: ${this.titleContainerWidth}`);
160                  })
161              }
162              Row() {
163                Flex({ justifyContent: FlexAlign.Center }) {
164                  Text() {
165                    Span(this.content)
166                  }
167                  .fontSize(
168                    getLimitFontSize(sourceToVp($r('sys.float.Body_L')),
169                      getFontSizeScale(getContext(this) as common.UIAbilityContext))
170                  )
171                  .fontWeight(FontWeight.Medium)
172                  .fontColor($r('sys.color.font_primary'))
173                  .margin({
174                    left: Constants.PC_CONTENT_MARGIN_SIDE,
175                    right: Constants.PC_CONTENT_MARGIN_SIDE,
176                    top: Constants.PC_CONTENT_MARGIN_TOP
177                  })
178                }
179              }
180              Row() {
181                Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
182                  Button($r('app.string.BAN'))
183                    .onClick(async (): Promise<void> => {
184                    await this.enableNotification(false);
185                    })
186                    .customizeButton()
187                  Divider()
188                    .color($r('sys.color.comp_divider'))
189                    .vertical(true)
190                    .height(Constants.DIVIDER_HEIGHT)
191                    .strokeWidth(Constants.DIVIDER_WIDTH)
192                    .margin({left: Constants.BUTTON_LEFT, right: Constants.BUTTON_RIGHT})
193                  Button($r('app.string.ALLOW'))
194                    .onClick(async (): Promise<void> => {
195                      await this.enableNotification(true);
196                    })
197                    .customizeButton()
198                }
199                .margin({
200                  left: Constants.PC_OPERATE_MARGIN_SIDE,
201                  right: Constants.PC_OPERATE_MARGIN_SIDE,
202                  top: Constants.PC_OPERATE_MARGIN_TOP,
203                  bottom: Constants.PC_OPERATE_MARGIN_BUTTOM
204                })
205              }
206            }
207          }
208          .constraintSize({ maxHeight: Constants.MAXIMUM_HEADER_HEIGHT })
209        }
210        .borderRadius(Constants.PC_RDIUS_16)
211        .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK)
212        .width(Constants.PC_DIALOG_WIDTH)
213        .outline({
214            width: 1,
215            color: Constants.PC_OUTLINE_COLOR,
216            radius: Constants.PC_RDIUS_16
217          })
218        .clip(true)
219      }
220      .width(Constants.FULL_WIDTH)
221      .height(Constants.FULL_HEIGHT)
222    }
223  }
224
225  async aboutToAppear(): Promise<void> {
226    this.dialog = storage.get('dialog') as EnableNotificationDialog;
227    this.session = storage.get('session') as UIExtensionContentSession;
228    this.updateAvoidWindow();
229    this.updateSubWindowSize();
230    try {
231      await this.updateStatus();
232    } catch (err) {
233      console.error(TAG, `aboutToAppear error : ${err?.code}`);
234      await this.dialog?.destroyException();
235      await this.session?.terminateSelf();
236    }
237  }
238
239  async aboutToDisappear(): Promise<void> {
240    console.info(TAG, `aboutToDisappear`);
241    this.session?.terminateSelf();
242  }
243
244  async updateOnPageShow(): Promise<void> {
245    if (this.isUpdate > 0) {
246      await this.updateStatus();
247    }
248  }
249
250  async updateStatus(): Promise<void> {
251    let bundleNameObj = this.dialog?.want.parameters?.bundleName;
252    let bundleName = bundleNameObj ? bundleNameObj.toString() : '';
253    await this.updateApplicationName(bundleName);
254  }
255
256  async updateApplicationName(bundleName: string): Promise<void> {
257    console.info(TAG, `updateApplicationName bundleName: ${bundleName}`);
258    try {
259      let bundleFlags = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_ALL;
260      let resourceInfo = bundleResourceManager.getBundleResourceInfo(bundleName, bundleFlags);
261      console.info(TAG, `applicationName name : ${JSON.stringify(resourceInfo.label)}`);
262      let appName = resourceInfo.label;
263      this.appName = titleTrim(appName);
264      console.info(TAG, `hap label: ${this.appName}`);
265
266      let systemLanguage: string = i18n.System.getSystemLanguage();
267      console.info(TAG, `language: ${systemLanguage}`);
268      if (systemLanguage.indexOf('zh-Hans') != -1) {
269        this.content = LAN_CN;
270      } else if (systemLanguage.indexOf('zh-Hant') != -1) {
271        this.content = LAN_TW;
272      } else if (systemLanguage.indexOf('en') != -1) {
273        this.content = LAN_EN;
274      } else if (systemLanguage.indexOf('ug') != -1) {
275        this.content = LAN_UG;
276      } else if (systemLanguage.indexOf('bo') != -1) {
277        this.content = LAN_BO;
278      }
279
280    } catch (err) {
281      console.error(TAG, `applicationName error : ${err?.code}`);
282    }
283  }
284
285  updateAvoidWindow(): void {
286    let type = window.AvoidAreaType.TYPE_SYSTEM;
287    try {
288      this.dialog?.extensionWindow.on('avoidAreaChange', (data): void => {
289        if (data.type == window.AvoidAreaType.TYPE_SYSTEM) {
290          console.info(TAG, `avoidAreaChange: ${JSON.stringify(data)}`);
291          this.naviHeight = data.area.bottomRect.height;
292        }
293      });
294      let avoidArea = this.dialog?.extensionWindow.getWindowAvoidArea(type);
295      if (avoidArea != undefined) {
296        console.info(TAG, `avoidArea: ${JSON.stringify(avoidArea)}`);
297        this.naviHeight = avoidArea.bottomRect.height;
298      }
299    } catch (err) {
300      console.error(TAG, `Failed to obtain the area. Cause: ${err?.code}`);
301    }
302  }
303
304  updateSubWindowSize(): void {
305    try {
306      this.dialog?.extensionWindow.on('windowSizeChange', (data):void => {
307        let windowRect = this.dialog?.extensionWindow.properties?.uiExtensionHostWindowProxyRect;
308        console.info(TAG, `windowSizeChange event, size = ${windowRect?.left}-${windowRect?.top}-${windowRect?.width}-${windowRect?.height}`);
309        this.dialog?.subWindow?.moveWindowTo(windowRect?.left, windowRect?.top);
310        this.dialog?.subWindow?.resize(windowRect?.width, windowRect?.height);
311      });
312    } catch (err) {
313      console.error(TAG, `updateSubWindowSize error. Cause: ${err?.code}`);
314    }
315  }
316
317  async enableNotification(enabled: boolean): Promise<void> {
318    console.info(TAG, `NotificationDialog enableNotification: ${enabled}`);
319    try {
320      await this.dialog?.publishButtonClickedEvent(enabled);
321      this.clicked = true;
322    } catch (err) {
323      console.error(TAG, `NotificationDialog enable error, code is ${err?.code}`);
324      await this.dialog?.destroyException();
325    } finally {
326      await this.dialog?.subWindow?.destroyWindow();
327      this.session?.terminateSelf();
328    }
329  }
330}