From 16c357231a25bdd602bc6ae8ef45390847392825 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 | 52 ++++- .../src/main/ets/common/utils/constant.ets | 18 ++ .../src/main/ets/pages/securityToast.ets | 183 ++++++++++++++++++ .../main/resources/base/element/color.json | 4 + .../resources/base/profile/main_pages.json | 1 + .../main/resources/dark/element/color.json | 8 + 8 files changed, 262 insertions(+), 12 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..6435e72 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. @@ -41,15 +44,26 @@ export default class SecurityExtensionAbility extends extension { console.info(TAG + 'want: ' + JSON.stringify(want)); try { - let width = want.parameters['ohos.display.width']; - let height = want.parameters['ohos.display.height']; + let width = want.parameters['ohos.display.width'] ?? 0; + let height = want.parameters['ohos.display.height'] ?? 0; + let top = want.parameters['ohos.display.top'] ?? 0; let navigationBarRect = { left: 0, - top: 0, + top: top, width: width, height: height }; - this.createWindow('SecurityDialog' + startId, window.WindowType.TYPE_DIALOG, navigationBarRect, want); + let notifyType = want.parameters['ohos.ability.notify.type'] ?? 0; + if (true) { + try { + startId > 1 && window.findWindow(`SaveButtonTip${startId - 1}`).destroyWindow(); + } catch (exception) { + console.error(`Failed to find the Window. Cause code: ${exception.code}, message: ${exception.message}`); + } + this.createToast('SaveButtonTip' + 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 +112,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 +127,26 @@ 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); + try { + await win.setSystemAvoidAreaEnabled(true); + } catch (error) { + console.error(TAG + `setSystemAvoidAreaEnabled error: ${JSON.stringify(error)}.`); + }; + 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..aa08e9d 100644 --- a/permissionmanager/src/main/ets/common/utils/constant.ets +++ b/permissionmanager/src/main/ets/common/utils/constant.ets @@ -350,4 +350,22 @@ 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_SYMBOLGLYPH_FONTSIZE = 16; + public static TOAST_POSITION_Y_UP = 15; + public static TOAST_POSITION_Y_UNDER = -15; + 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; + public static TOAST_CONSTRAINT_SIZE_MIN_HEIGHT = 36; + public static TOAST_COLUMN_WIDTH = '100%'; + public static TOAST_COLUMN_HEIGHT = '100%'; } diff --git a/permissionmanager/src/main/ets/pages/securityToast.ets b/permissionmanager/src/main/ets/pages/securityToast.ets new file mode 100644 index 0000000..beec8a3 --- /dev/null +++ b/permissionmanager/src/main/ets/pages/securityToast.ets @@ -0,0 +1,183 @@ +/* + * 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 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'; + +@Entry({ useSharedStorage: true }) +@Component +struct SecurityToast { + @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 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 type = window.AvoidAreaType.TYPE_SYSTEM; + let avoidArea = this.win?.getWindowAvoidArea(type); + this.topRectHeight = avoidArea.topRect.height; + } + + aboutToAppear(): void { + Log.info('onAboutToAppear'); + this.rotationInit = display.getDefaultDisplaySync().orientation; + this.densityDPIInit = display.getDefaultDisplaySync().densityDPI; + this.setPosition = this.want.parameters['ohos.toast.position'] ?? true; + this.positionY = this.setPosition ? Constants.TOAST_POSITION_Y_UP : Constants.TOAST_POSITION_Y_UNDER; + this.setOffset = this.want.parameters['ohos.toast.offset'] ?? Constants.TOAST_ANIMATION_OFFSET; + this.screenMonitor(); + this.getWinAvoidArea(); + let timeoutStart = setTimeout(() => { + this.alpha = 1; + this.positionY = 0; + clearTimeout(timeoutStart); + }, Constants.TOAST_ANIMATION_TIMEOUT_START); + let timeoutEnd = setTimeout(() => { + this.alpha = 0; + this.positionY = this.setPosition ? Constants.TOAST_POSITION_Y_UP : Constants.TOAST_POSITION_Y_UNDER; + 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}`); + } + } + + 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(Constants.TOAST_SYMBOLGLYPH_FONTSIZE) + .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, + minHeight: Constants.TOAST_CONSTRAINT_SIZE_MIN_HEIGHT + }) + .margin(this.setPosition ? { top: this.setOffset - px2vp(this.topRectHeight) } : { bottom: this.setOffset }) + .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(Constants.TOAST_COLUMN_WIDTH) + .height(Constants.TOAST_COLUMN_HEIGHT) + .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