自定义保存控件提供系统提示

Signed-off-by: li-li-wang <wangliliang5@huawei.com>
This commit is contained in:
li-li-wang
2025-05-05 20:45:43 +08:00
parent e622cf05b0
commit df4b28102b
8 changed files with 258 additions and 12 deletions
+2 -2
View File
@@ -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"
+2 -2
View File
@@ -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"
@@ -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 (notifyType === NotifyType.Toast) {
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,22 @@ 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<void> {
try {
const win = await window.createWindow({ ctx: this.context, name, windowType });
let property: Record<string, Object> = { '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.setSystemAvoidAreaEnabled(true);
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)} `);
}
}
};
@@ -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%';
}
@@ -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'] ?? false;
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
})
}
}
@@ -27,6 +27,10 @@
{
"name": "local_background_color",
"value": "#3194F7"
},
{
"name": "outline",
"value": "#1A000000"
}
]
}
@@ -10,6 +10,7 @@
"pages/dialogPlus",
"pages/globalSwitch",
"pages/securityDialog",
"pages/securityToast",
"pages/transition",
"PermissionSheet/PermissionStateSheetDialog",
"PermissionSheet/GlobalSwitchSheetDialog"
@@ -0,0 +1,8 @@
{
"color": [
{
"name": "outline",
"value": "#66000000"
}
]
}