From 4718cb59f3f29a69bcd5cbf049872aa7204f5d0e Mon Sep 17 00:00:00 2001 From: li-li-wang Date: Mon, 12 May 2025 16:44:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E6=8E=A7=E4=BB=B6=E6=8F=90=E4=BE=9B=E7=B3=BB=E7=BB=9F=E6=8F=90?= =?UTF-8?q?=E7=A4=BA-rk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: li-li-wang --- AppScope/app.json | 4 +- AppScope/app.json5 | 4 +- .../SecurityExtAbility/SecurityExtAbility.ts | 39 +++- .../src/main/ets/common/utils/constant.ets | 12 + .../src/main/ets/pages/securityToast.ets | 205 ++++++++++++++++++ .../main/resources/base/element/color.json | 4 + .../resources/base/profile/main_pages.json | 1 + .../main/resources/dark/element/color.json | 8 + 8 files changed, 268 insertions(+), 9 deletions(-) create mode 100644 permissionmanager/src/main/ets/pages/securityToast.ets create mode 100644 permissionmanager/src/main/resources/dark/element/color.json diff --git a/AppScope/app.json b/AppScope/app.json index 1953285..954471a 100644 --- a/AppScope/app.json +++ b/AppScope/app.json @@ -4,8 +4,8 @@ "app": { "bundleName": "com.ohos.permissionmanager", "vendor": "example", - "versionCode": 1000080, - "versionName": "1.8.0", + "versionCode": 1000081, + "versionName": "1.8.1", "icon": "$media:app_icon", "label": "$string:app_name", "configuration": "$profile:configuration" diff --git a/AppScope/app.json5 b/AppScope/app.json5 index eedb458..e2d3b96 100644 --- a/AppScope/app.json5 +++ b/AppScope/app.json5 @@ -16,8 +16,8 @@ "app": { "bundleName": "com.ohos.permissionmanager", "vendor": "example", - "versionCode": 1000080, - "versionName": "1.8.0", + "versionCode": 1000081, + "versionName": "1.8.1", "icon": "$media:app_icon", "label": "$string:app_name", "configuration": "$profile:configuration" diff --git a/permissionmanager/src/main/ets/SecurityExtAbility/SecurityExtAbility.ts b/permissionmanager/src/main/ets/SecurityExtAbility/SecurityExtAbility.ts index 1f0f912..81b9899 100644 --- a/permissionmanager/src/main/ets/SecurityExtAbility/SecurityExtAbility.ts +++ b/permissionmanager/src/main/ets/SecurityExtAbility/SecurityExtAbility.ts @@ -15,14 +15,17 @@ import extension from '@ohos.app.ability.ServiceExtensionAbility'; import window from '@ohos.window'; -import display from '@ohos.display'; import { GlobalContext } from '../common/utils/globalContext'; import { Configuration } from '@ohos.app.ability.Configuration'; -import deviceInfo from '@ohos.deviceInfo'; const TAG = 'PermissionManager_Log:'; const BG_COLOR = '#00000000'; +enum NotifyType { + Toast = 1, + Dialog = 0 +} + export default class SecurityExtensionAbility extends extension { /** * Lifecycle function, called back when a service extension is started for initialization. @@ -49,7 +52,16 @@ export default class SecurityExtensionAbility extends extension { width: width, height: height }; - this.createWindow('SecurityDialog' + startId, window.WindowType.TYPE_DIALOG, navigationBarRect, want); + if (true) { + try { + startId > 1 && window.findWindow(`SecurityToast${startId - 1}`).destroyWindow(); + } catch (exception) { + console.error(`Failed to find the Window. Cause code: ${exception.code}, message: ${exception.message}`); + } + this.createToast('SecurityToast' + startId, window.WindowType.TYPE_SYSTEM_TOAST, navigationBarRect, want); + } else { + this.createWindow('SecurityDialog' + startId, window.WindowType.TYPE_DIALOG, navigationBarRect, want); + } } catch (exception) { console.error(TAG + 'Failed to obtain the default display object. Code: ' + JSON.stringify(exception)); }; @@ -98,8 +110,8 @@ export default class SecurityExtensionAbility extends extension { }); try { await win.setFollowParentWindowLayoutEnabled(true); - } catch(error) { - console.error(TAG + `setFollowParentWindowLayoutEnabled error: ${JSON.stringify(error)}`); + } catch (error) { + console.error(TAG + `setFollowParentWindowLayoutEnabled error: ${JSON.stringify(error)}.`); await win.moveWindowTo(rect.left, rect.top); await win.resize(rect.width, rect.height); }; @@ -113,4 +125,21 @@ export default class SecurityExtensionAbility extends extension { console.error(TAG + `window create failed! err: ${JSON.stringify(err)}`); } } + + private async createToast(name: string, windowType, rect, want): Promise { + try { + const win = await window.createWindow({ ctx: this.context, name, windowType }); + let property: Record = { 'want': want, 'win': win }; + let storage: LocalStorage = new LocalStorage(property); + await win.moveWindowTo(rect.left, rect.top); + await win.resize(rect.width, rect.height); + await win.loadContent('pages/securityToast', storage); + win.setWindowBackgroundColor(BG_COLOR); + await win.setWindowTouchable(false); + await win.setWindowFocusable(false); + await win.showWindow(); + } catch (error) { + console.error(TAG + `window create failed! err: ${JSON.stringify(error)} `); + } + } }; diff --git a/permissionmanager/src/main/ets/common/utils/constant.ets b/permissionmanager/src/main/ets/common/utils/constant.ets index 12592fd..f0a421e 100644 --- a/permissionmanager/src/main/ets/common/utils/constant.ets +++ b/permissionmanager/src/main/ets/common/utils/constant.ets @@ -350,4 +350,16 @@ export default class Constants { public static CUSTOMIZE_BUTTON_WIDTH = 152; public static CUSTOMIZE_BUTTON_TEXT_MIN_SIZE = 12; public static CUSTOMIZE_BUTTON_TEXT_MAX_SIZE = 15; + + // SecurityToast + public static TOAST_ANIMATION_OFFSET = 80; + public static TOAST_ANIMATION_TIMEOUT_START = 100; + public static TOAST_ANIMATION_TIMEOUT_END = 3100; + public static TOAST_ANIMATION_TIMEOUT_ID = 3500; + public static TOAST_ICON_FONTSIZE_4 = 4; + public static TOAST_ICON_FONTSIZE_2 = 2; + public static TOAST_ICON_FONTSIZE_16 = 16; + public static TOAST_ICON_FONTSIZE_14 = 14; + public static TOAST_MAX_FONT_SCALE = 1; + public static TOAST_CONSTRAINT_SIZE_MAX_WIDTH = 400; } diff --git a/permissionmanager/src/main/ets/pages/securityToast.ets b/permissionmanager/src/main/ets/pages/securityToast.ets new file mode 100644 index 0000000..30aba92 --- /dev/null +++ b/permissionmanager/src/main/ets/pages/securityToast.ets @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import window from '@ohos.window'; +import display from '@ohos.display'; +import common from '@ohos.app.ability.common'; +import accessibility from '@ohos.accessibility'; +import { Log } from '../common/utils/utils'; +import { DeviceUtil } from '../common/utils/deviceUtil'; +import Constants from '../common/utils/constant'; +import { WantInfo } from '../common/model/typedef'; +import { BusinessError } from '@ohos.base'; + +@Entry({ useSharedStorage: true }) +@Component +struct SecurityToast { + private context = this.getUIContext().getHostContext() as common.UIServiceExtensionContext; + @LocalStorageLink('want') want: WantInfo = new WantInfo([]); + @LocalStorageLink('win') win: window.Window = {} as window.Window; + @State alpha: number = 0; + @State positionY: number = 0; + @State setPosition: boolean = false; + @State setOffset: number = Constants.TOAST_ANIMATION_OFFSET; + @State windowWidth: number = 0; + @State windowHeight: number = 0; + @State rotationInit: number = 0; + @State densityDPIInit: number = 0; + @State bottomRectHeight: number = 0; + @State topRectHeight: number = 0; + + getAnnounceForAccessibility() { + try { + let textAnnounce = + this.getUIContext().getHostContext()?.resourceManager.getStringSync($r('app.string.SecurityTitle_mediaFiles') + .id); + let eventInfo: accessibility.EventInfo = ({ + type: 'announceForAccessibility', + bundleName: 'com.ohos.permissionmanager', + triggerAction: 'common', + textAnnouncedForAccessibility: textAnnounce + }); + accessibility.sendAccessibilityEvent(eventInfo) + .catch((error: Error) => { + Log.error(`sendAnnounceForAccessibilityEvent fail: ${error.message}`); + return false; + }); + } catch (error) { + Log.error(`sendAccessibilityEvent failed, error code: ${error.code}, message: ${error.message}.`); + } + } + + getWinAvoidArea() { + let enabled = true; + let promise = this.win.setSystemAvoidAreaEnabled(enabled); + promise.then(() => { + let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR; + let avoidArea = this.win?.getWindowAvoidArea(type); + this.bottomRectHeight = avoidArea.bottomRect.height; + Log.info(`bottomRectHeight++++${JSON.stringify(avoidArea)}`); + + let type1 = window.AvoidAreaType.TYPE_SYSTEM; + let avoidArea1 = this.win?.getWindowAvoidArea(type1); + this.topRectHeight = avoidArea1.topRect.height; + Log.info(`topRectHeight++++${JSON.stringify(avoidArea1)}`); + }).catch((err: BusinessError) => { + Log.error(`Failed to obtain the system window avoid area. Cause code: ${err.code}, message: ${err.message}`); + }); + } + + aboutToAppear(): void { + Log.info('onAboutToAppear'); + this.rotationInit = display.getDefaultDisplaySync().orientation; + this.densityDPIInit = display.getDefaultDisplaySync().densityDPI; + this.screenMonitor(); + this.getWinAvoidArea(); + let timeoutStart = setTimeout(() => { + this.alpha = 1; + this.positionY = 15; + clearTimeout(timeoutStart); + }, Constants.TOAST_ANIMATION_TIMEOUT_START); + let timeoutEnd = setTimeout(() => { + this.alpha = 0; + this.positionY = -15; + clearTimeout(timeoutEnd); + }, Constants.TOAST_ANIMATION_TIMEOUT_END); + let timeoutID = setTimeout(() => { + this.win?.destroyWindow(); + clearTimeout(timeoutID); + }, Constants.TOAST_ANIMATION_TIMEOUT_ID); + } + + aboutToDisappear(): void { + try { + display.off('foldStatusChange'); + display.off('change'); + } catch (exception) { + Log.error(`Failed to unregister callback. Cause code: ${exception.code}, message: ${exception.message}`); + } + } + + getIconFontSize(): number { + try { + let fontSize = this.getUIContext().px2vp(this.context.resourceManager.getNumber($r('sys.float.Caption_L').id)); + return DeviceUtil.isPC() ? fontSize + Constants.TOAST_ICON_FONTSIZE_4 : + fontSize + Constants.TOAST_ICON_FONTSIZE_2; + } catch (error) { + Log.error(`getNumberByName failed, error code: ${error.code}, message: ${error.message}.`); + return DeviceUtil.isPC() ? Constants.TOAST_ICON_FONTSIZE_16 : Constants.TOAST_ICON_FONTSIZE_14; + } + } + + screenMonitor() { + try { + display.on('foldStatusChange', (data: display.FoldStatus) => { + Log.info(`Listening enabled. Data: ${JSON.stringify(data)}`); + this.win.destroyWindow(); + }); + } catch (exception) { + Log.error(`Failed to register callback. Code: ${JSON.stringify(exception)}`); + } + + try { + display.on('change', (data: number) => { + Log.info(`Listening enabled. Data: ${JSON.stringify(data)}`); + let rotation: number = display.getDefaultDisplaySync().orientation; + let densityDPI = display.getDefaultDisplaySync().densityDPI; + if (rotation !== this.rotationInit) { + this.rotationInit = rotation; + this.win.destroyWindow(); + return; + } + if (this.densityDPIInit !== densityDPI) { + this.densityDPIInit = densityDPI; + this.win.destroyWindow(); + } + }); + } catch (exception) { + Log.error(`Failed to register callback. Code: ${JSON.stringify(exception)}`); + } + } + + build() { + Column() { + Flex({ alignItems: ItemAlign.Center }) { + SymbolGlyph($r('sys.symbol.security_shield')) + .maxFontScale(Constants.TOAST_MAX_FONT_SCALE) + .minFontScale(Constants.TOAST_MAX_FONT_SCALE) + .fontSize(this.getIconFontSize()) + .fontColor([$r('sys.color.icon_primary'), $r('sys.color.icon_emphasize')]) + .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR) + Blank() + .width('4vp') + Text($r('app.string.SecurityTitle_mediaFiles')) + .maxFontScale(Constants.TOAST_MAX_FONT_SCALE) + .minFontScale(Constants.TOAST_MAX_FONT_SCALE) + .fontSize($r('sys.float.Body_M')) + .fontColor($r('sys.color.font_primary')) + .onAppear(() => { + this.getAnnounceForAccessibility(); + }) + } + .opacity(this.alpha) + .offset({ bottom: this.positionY }) + .animation({ + duration: Constants.TOAST_ANIMATION_TIMEOUT_START, + curve: 'cubic-bezier(0.2, 0, 0.1, 1)', + }) + .width('auto') + .constraintSize({ maxWidth: Constants.TOAST_CONSTRAINT_SIZE_MAX_WIDTH }) + .margin(this.setPosition ? { top: 112 + px2vp(this.topRectHeight) } : + { bottom: 80 + px2vp(this.bottomRectHeight) }) + .padding(DeviceUtil.isPC() ? $r('sys.float.padding_level4') : { + top: Constants.PADDING_8, + right: Constants.PADDING_16, + bottom: Constants.PADDING_8, + left: Constants.PADDING_16 + }) + .shadow(DeviceUtil.isPC() ? ShadowStyle.OUTER_DEFAULT_SM : ShadowStyle.OUTER_DEFAULT_MD) + .borderRadius(DeviceUtil.isPC() ? $r('sys.float.corner_radius_level4') : $r('sys.float.corner_radius_level9')) + .backgroundBlurStyle(DeviceUtil.isPC() ? BlurStyle.COMPONENT_REGULAR : BlurStyle.COMPONENT_ULTRA_THICK) + .border(DeviceUtil.isPC() ? { width: '1px', color: '#33ffffff' } : {}) + .outline(DeviceUtil.isPC() ? + { width: '1px', color: $r('app.color.outline'), radius: $r('sys.float.corner_radius_level4') } : {}) + } + .width('100%') + .height('100%') + .justifyContent(this.setPosition ? FlexAlign.Start : FlexAlign.End) + .padding({ + left: (DeviceUtil.isPC() || DeviceUtil.isTablet()) ? Constants.PADDING_24 : Constants.PADDING_16, + right: (DeviceUtil.isPC() || DeviceUtil.isTablet()) ? Constants.PADDING_24 : Constants.PADDING_16 + }) + } +} \ No newline at end of file diff --git a/permissionmanager/src/main/resources/base/element/color.json b/permissionmanager/src/main/resources/base/element/color.json index 353356b..656bdbb 100644 --- a/permissionmanager/src/main/resources/base/element/color.json +++ b/permissionmanager/src/main/resources/base/element/color.json @@ -27,6 +27,10 @@ { "name": "local_background_color", "value": "#3194F7" + }, + { + "name": "outline", + "value": "#1A000000" } ] } \ No newline at end of file diff --git a/permissionmanager/src/main/resources/base/profile/main_pages.json b/permissionmanager/src/main/resources/base/profile/main_pages.json index 2de3e89..9515dd2 100644 --- a/permissionmanager/src/main/resources/base/profile/main_pages.json +++ b/permissionmanager/src/main/resources/base/profile/main_pages.json @@ -10,6 +10,7 @@ "pages/dialogPlus", "pages/globalSwitch", "pages/securityDialog", + "pages/securityToast", "pages/transition", "PermissionSheet/PermissionStateSheetDialog", "PermissionSheet/GlobalSwitchSheetDialog" diff --git a/permissionmanager/src/main/resources/dark/element/color.json b/permissionmanager/src/main/resources/dark/element/color.json new file mode 100644 index 0000000..41b7a03 --- /dev/null +++ b/permissionmanager/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "outline", + "value": "#66000000" + } + ] +} \ No newline at end of file -- Gitee