/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
* 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 { KeyboardAvoidMode } from '@ohos.arkui.UIContext';
import display from '@ohos.display';
import measure from '@ohos.measure';
import window from '@ohos.window';
import plugin from '@ohos.pluginComponent';
import taskpool from '@ohos.taskpool';
import { BusinessError } from '@ohos.base';
import Want from '@ohos.app.ability.Want';
import { emitter } from '@kit.BasicServicesKit';
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
import Settings from '@ohos.settings';
import uiAppearance from '@ohos.uiAppearance';
import { PackagesConstant } from '../constant/PackagesConstant';
import { LogUtil } from '../utils/LogUtil';
import { SettingsDataUtils } from '../utils/SettingsDataUtils';
import { DeviceUtil } from '../utils/BaseUtils';
import { DisplayConstant } from '../constant/DisplayConstant';
import { DisplayManager, EVENT_ID_FOLD_STATUS_CHANGE } from '../displaymanager/DisplayManager';
import { EVENT_ID_ON_WINDOW_STAGE_CHANGED } from '../event/types';
import { ResourceUtil } from '../utils/ResourceUtil';
import { MainConstants } from '../constant/MainConstants';
const TAG: string = 'WindowManager : ';
const WINDOW_BACKGROUND_COLOR: string = '#F1F3F5';
const WINDOW_DEFAULT_CORNERS: number = 24;
const WINDOW_BG_WHITE: string = '#FFFFFF';
const WINDOW_BG_BLACK: string = '#000000';
const WINDOW_BG_APP: string = '#F1F3F5';
const VP_TO_PX_SCALE: number = 160;
const COLOR_MODE_DARK: number = 0;
const DIALOG_WINDOW_ULTRA_THICK_WHITE: string = '#F1F3F5';
const DIALOG_WINDOW_ULTRA_THICK_BLACK: string = '#202224';
const SETTINGS_NAVBAR_WIDTH_MIN : number = 280;
const SETTINGS_NAVBAR_WIDTH_MAX : number = 344;
const SETTINGS_NAVBAR_RATIO : number = 0.3337;
const SETTINGS_CONTENT_WIDTH : number = 1080;
const SETTINGS_CONTENT_DISTANCE : number = 24;
const GRID_ROW_MARGIN: number = 48;
const GRID_ROW_GUTTER: number = 24;
const GRID_ROW_COLUMNS: number = 12;
const GRID_COLUMN_HOME_SPAN: number = 4.5;
const GRID_COLUMN_HOME_GUTTER_WIDTH: number = GRID_ROW_GUTTER * 4;
const GRID_CONTENT_MARGIN_RATIO: number = 0.75;
const GRID_COLUMN_WIDTH_KEY: string = 'grid_row_column_width_key';
const GRID_ROW_HOME_WIDTH_KEY: string = 'grid_row_home_width_key';
const GRID_ROW_MARGIN_FULL_PAGE_KEY: string = 'grid_row_margin_full_page_key';
export interface WindowInfo {
name: string,
width: number,
height: number,
windowType: number,
loadContent: string,
radius?: number,
}
/**
* 主窗口系统规避区域参数
*/
export interface AvoidAreaParams {
type: window.AvoidAreaType,
area: window.AvoidArea
}
interface PushParametersI extends plugin.PushParameters {
owner: Want;
}
/**
* 避让区方向
*/
export enum AvoidAreaDirect {
TOP,
BOTTOM
}
export enum BreakPoint {
SM = 'sm',
LG = 'lg',
MD = 'md'
}
export enum LayoutConstants {
WIDTH_RATIO = 0.4,
A_MIN_WIDTH = 280,
A_MAX_WIDTH = 480,
B_MIN_WIDTH = 360,
SM_MAX_WIDTH = 600,
MD_MAX_WIDTH = 962,
}
/**
* 窗口功能管理类
*
* @since 2022-12-30
*/
export class WindowManager {
private mainWindow: window.Window | null = null;
private currentBreakingPoint: BreakPoint = BreakPoint.SM;
/**
* 获取窗口管理类对象
*
* @return 窗口管理类对象单一实例
*/
static getInstance(): WindowManager {
if (AppStorage.get<WindowManager>('WindowManager') == null) {
AppStorage.setOrCreate<WindowManager>('WindowManager', new WindowManager());
}
return AppStorage.get<WindowManager>('WindowManager') as WindowManager;
}
/**
* 调整窗口属性,包括宽,高,位置,加载内容等
*/
private async adjustWindowParam(win: window.Window, windowInfo: WindowInfo): Promise<void> {
try {
win.setCornerRadius(windowInfo.radius ?? WINDOW_DEFAULT_CORNERS);
await this.resizeWindow(win, windowInfo.width, windowInfo.height);
await win.setUIContent(windowInfo.loadContent);
} catch (businessError) {
LogUtil.error(`${TAG} adjustWindowParam error: ${businessError?.message}`);
}
}
async resizeWindow(win: window.Window, width: number, height: number): Promise<void> {
if (!win) {
return;
}
try {
let windowRect = win.getWindowProperties().windowRect;
if (windowRect.width === width && windowRect.height === height) {
LogUtil.info(`${TAG} window size is same, don't need resize`);
return;
}
await win.resize(width, height);
let dis = display.getDefaultDisplaySync();
let moveWidth: number = (dis.width - width) / 2;
let moveHeight: number = (dis.height - height) / 2;
LogUtil.info(`${TAG} resizeWindow moveWindowTo, height: ${moveWidth}, width: ${moveHeight}`);
await win.moveWindowTo(moveWidth, moveHeight);
} catch (businessError) {
LogUtil.error(`${TAG} resizeWindow error: ${businessError?.message}`);
}
}
/**
* 创建一个新的窗口
*/
async createWindow(context: Context, windowInfo: WindowInfo): Promise<window.Window | void> {
if (!windowInfo) {
return;
}
LogUtil.info(`${TAG} createWindow, name: ${windowInfo.name} windowType: ${windowInfo.windowType} loadContent: ${windowInfo.loadContent}`);
let config: window.Configuration = { name: windowInfo.name, windowType: windowInfo.windowType, ctx: context };
let win: window.Window;
try {
win = await window.createWindow(config);
await this.adjustWindowParam(win, windowInfo);
win.setShadow(50, '#4d000000', 0, 10);
WindowManager.setWindowBackgroundColor(WindowManager.getDialogWindowBackgroundColor(), win);
await win.show();
return win;
} catch (businessError) {
LogUtil.error(`${TAG} createWindow error: ${businessError?.message}`);
}
}
createWindowIfAbsent(context: Context, windowInfo: WindowInfo, isPrivacyMode: boolean = false): void {
if (!windowInfo) {
return;
}
LogUtil.info(`${TAG} create, name ${windowInfo.name}`);
window.find(windowInfo.name).then(win => {
this.adjustWindowParam(win, windowInfo).then(() => {
win.setShadow(50, '#4d000000', 0, 10);
WindowManager.setWindowBackgroundColor(WindowManager.getDialogWindowBackgroundColor(), win);
win.show();
if (isPrivacyMode) {
this.setWindowPrivacyMode(win, true);
}
})
}).catch((error: BusinessError) => {
LogUtil.info(`${TAG} ${windowInfo?.name} window is not created, because ${error}`);
this.createWindow(context, windowInfo).then((win) => {
if (isPrivacyMode) {
this.setWindowPrivacyMode(win as window.Window, true);
}
});
});
}
changeMainWindowColor(bgColor: string): void {
this.getMainWindow();
this.mainWindow?.setWindowBackgroundColor(bgColor);
}
async showWindow(name: string): Promise<void> {
let win = this.findWindow(name);
if (!win) {
LogUtil.info(`${TAG} showWindow, window is null`);
return;
}
LogUtil.info(`${TAG} showWindow, show the name: ${name}`);
try {
await win.showWindow();
} catch (error) {
LogUtil.error(`${TAG} show window error, because ${error}`);
}
}
async hideWindow(name: string): Promise<void> {
let win = this.findWindow(name);
if (!win) {
LogUtil.info(`${TAG} hideWindow, window is null`);
return;
}
LogUtil.info(`${TAG} hideWindow, hide the name: ${name}`);
try {
await win.hide();
} catch (error) {
LogUtil.error(`${TAG} hide window error, because ${error}`);
}
}
async destroyWindow(name: string, isPrivacyMode: boolean = false): Promise<void> {
let win = this.findWindow(name);
if (!win) {
LogUtil.info(`${TAG} destroyWindow, window is null`);
return;
}
LogUtil.info(`${TAG} destroyWindow, destroy the name: ${name}`);
try {
await win.destroyWindow();
this.setWindowPrivacyMode(win, isPrivacyMode);
} catch (error) {
LogUtil.error(`${TAG} destory window error, because ${error}`);
}
}
findWindow(name: string): window.Window {
try {
return window.findWindow(name);
} catch (error) {
LogUtil.error(`${TAG} ${name} window is not found, because ${error}`);
}
return window.findWindow(name);
}
static isWindowShowing(name: string): boolean {
try {
return window.findWindow(name).isWindowShowing();
} catch (error) {
LogUtil.error(`${TAG} ${name} window is not found, because ${error}`);
}
return false;
}
static async setNavBarWidth(width: number): Promise<void> {
LogUtil.info(`${TAG} size.width is ${width}`);
try {
WindowManager.getInstance().updateBreakPointByWindWidth(px2vp(width))
let task: taskpool.Task = new taskpool.Task(getNavBarWidth, width);
let calNavBarWidth: number = await taskpool.execute(task, taskpool.Priority.MEDIUM) as number;
AppStorage.setOrCreate('calNavBarWidth', calNavBarWidth);
LogUtil.info(`${TAG} navigation bar width: ${calNavBarWidth}`);
} catch (error) {
LogUtil.error(`${TAG} getNavBarWidth task pool failed: ${error?.message}`);
}
}
/**
* 根据当前窗口宽度获取PC设置nav宽度
*
* @param currentWindowWidth 当前窗口宽度
* @returns 设置nav宽度
*/
static getPcSettingsNavBarWidth(currentWindowWidth: number): number {
let width: number = currentWindowWidth * SETTINGS_NAVBAR_RATIO;
if (Number.isNaN(width) || width < SETTINGS_NAVBAR_WIDTH_MIN) {
width = SETTINGS_NAVBAR_WIDTH_MIN;
} else if (width > SETTINGS_NAVBAR_WIDTH_MAX) {
width = SETTINGS_NAVBAR_WIDTH_MAX;
}
return width;
}
/**
* 根据当前窗口宽度获取PC设置NavDestination的左右padding
*
* @param currentWindowWidth 当前窗口宽度
* @returns 设置NavDestination的左右padding
*/
static getPcSettingsPagePadding(currentWindowWidth: number): number {
let more: number = currentWindowWidth - SETTINGS_NAVBAR_WIDTH_MAX - SETTINGS_CONTENT_WIDTH -
SETTINGS_CONTENT_DISTANCE * 2;
return more > 0 ? more / 2 : 0;
}
/**
* 根据宽度计算当前的断点类型,当断点类型发生改变则触发分栏宽度刷新
* @param windowWidth 单位为vp
*/
public updateBreakPointByWindWidth(windowWidthVp: number): void {
LogUtil.info(`${TAG} updateBreakPointByWindWidth, windowWidthVp: ${windowWidthVp}`);
if (!windowWidthVp) {
return;
}
let newBreakpoint: BreakPoint;
if (windowWidthVp < LayoutConstants.SM_MAX_WIDTH) {
newBreakpoint = BreakPoint.SM; // 竖屏手机
} else if (windowWidthVp < LayoutConstants.MD_MAX_WIDTH) {
newBreakpoint = BreakPoint.MD; // 折叠屏
} else {
newBreakpoint = BreakPoint.LG; // 超大屏幕
}
if (this.currentBreakingPoint !== newBreakpoint) {
// 记录当前断点值
this.currentBreakingPoint = newBreakpoint;
this.updateNavBarWidth(windowWidthVp);
}
LogUtil.info(`${TAG} updateBreakPointByWindWidth currentBreakingPoint: ${this.currentBreakingPoint}`);
}
private updateNavBarWidth(windowWidthVp: number): void {
let navBarWidth: number = windowWidthVp * LayoutConstants.WIDTH_RATIO;
if (this.currentBreakingPoint === BreakPoint.LG) {
navBarWidth = LayoutConstants.A_MAX_WIDTH;
AppStorage.setOrCreate('navBarWidth', navBarWidth);
} else if (this.currentBreakingPoint === BreakPoint.MD) {
AppStorage.setOrCreate('navBarWidth', navBarWidth);
}
LogUtil.info(`${TAG} setNavBarWidth winWidth: ${this.currentBreakingPoint} ${windowWidthVp} ${navBarWidth}`);
}
/**
* 设置沉浸式状态栏
*
* @param stage 窗口管理器
* @param properties 状态栏属性
* @param bgColor 状态栏背景
*/
static setSystemBarProperties(stage: window.WindowStage, properties: window.SystemBarProperties, bgColor: string) {
if (!stage || !properties) {
return;
}
stage.getMainWindow().then(windowInstance => {
windowInstance.setWindowSystemBarProperties(properties);
windowInstance.setWindowBackgroundColor(bgColor);
})
}
/**
* 设置沉浸式状态栏,不设置背景色
*
* @param stage 窗口管理器
* @param properties 状态栏属性
* @param bgColor 状态栏背景
*/
static setSystemBarPropertiesOnly(stage: window.WindowStage, properties: window.SystemBarProperties) {
if (!stage || !properties) {
return;
}
stage.getMainWindow().then(windowInstance => {
windowInstance.setWindowSystemBarProperties(properties);
}).catch((err: BusinessError) => {
LogUtil.error(`${TAG} setSystemBarPropertiesOnly failed: err.code${err?.code} err.message:${err?.message}`);
})
}
/**
* 设置是否屏幕常亮
*
* @param stage 窗口管理器
* @param isKeepScreenOn 是否常亮
*/
static setKeepScreenOn(stage: window.WindowStage, isKeepScreenOn: boolean): void {
if (!stage) {
LogUtil.error(`${TAG} setKeepScreenOn stage is invalid`);
return;
}
LogUtil.info(`${TAG} setKeepScreenOn in`);
stage.getMainWindow().then(windowInstance => {
windowInstance.setWindowKeepScreenOn(isKeepScreenOn);
LogUtil.info(`${TAG} setKeepScreenOn success ${isKeepScreenOn}`);
});
}
// 关闭plugin弹窗
static closePluginWindow(pluginTemplateName: string, canDestroyWindow: boolean): void {
plugin.push({
owner: {
bundleName: PackagesConstant.SETTINGS_BUNDLE_NAME
},
want: {
bundleName: PackagesConstant.SETTINGS_BUNDLE_NAME
},
name: pluginTemplateName,
data: {},
// 请求关闭弹窗
extraData: {
requestCloseWindow: true,
requestDestroyWindow: canDestroyWindow
}
} as PushParametersI, () => {
LogUtil.info(`${TAG} close plugin window complete. name is ${pluginTemplateName}`);
});
}
// 更新plugin弹窗高度
static updatePluginWindowHeight(pluginTemplateName: string, height: number): void {
LogUtil.info(`${TAG} updatePluginWindowHeight name is ${pluginTemplateName}, height: ${height}`);
plugin.push({
owner: {
bundleName: PackagesConstant.SETTINGS_BUNDLE_NAME
},
want: {
bundleName: PackagesConstant.SETTINGS_BUNDLE_NAME
},
name: pluginTemplateName,
data: {},
// 请求更新弹窗高度
extraData: {
requestWindowHeight: height
}
} as PushParametersI, () => {
LogUtil.info(`${TAG} update plugin window height complete. name is ${pluginTemplateName}`);
});
}
/**
* 给窗口设置安全标记
*
* @param window Stage模型window容器
* @param isPrivacyMode 是否设置成隐私模式
*/
setWindowPrivacyMode(window: window.Window, isPrivacyMode: boolean): void {
LogUtil.info(`${TAG} setPrivacyMode enter : ${isPrivacyMode}}`);
window?.setWindowPrivacyMode(isPrivacyMode).then(() => {
}).catch((err: BusinessError) => {
LogUtil.error(`Failed to set the window to privacy mode. Cause: ${err?.message}`);
});
}
/**
* get main window
*/
private getMainWindow(): void {
if (this.mainWindow) {
LogUtil.info(`${TAG} getMainWindow already exists, mainWindow: ${this.mainWindow === null}`);
return;
}
try {
let windowStage: window.WindowStage | undefined = this.getCurrentWindow();
if (!windowStage) {
LogUtil.warn(`${TAG} getMainWindow windowStage is null`);
return;
}
this.mainWindow = windowStage.getMainWindowSync();
LogUtil.info(`${TAG} getMainWindow success`);
} catch (businessError) {
LogUtil.error(`${TAG} getMainWindow error. Cause: ${businessError?.message}`);
}
LogUtil.info(`${TAG} getMainWindow mainWindow: ${this.mainWindow === null}`);
}
/**
* 设置右三键的颜色和大小
* @param isDarkStyle true:深色模式
*/
static setDecorButtonStyle(isDarkStyle: boolean): void {
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
LogUtil.warn(`${TAG} getMainWindow windowStage is null`);
return;
}
let style: window.DecorButtonStyle = {
colorMode: isDarkStyle ? ConfigurationConstant.ColorMode.COLOR_MODE_DARK :
ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT,
buttonBackgroundSize: 40,
closeButtonRightMargin: 16
}
try {
windowData.setDecorButtonStyle(style);
} catch (err) {
LogUtil.error(`${TAG} setDecorButtonStyle error: ${err?.message} , code: ${err?.code}`);
}
}
/**
* 开启固定态输入法窗口软键盘高度变化的监听
*
* @param windowStage Stage模型window容器
*/
onKeyboardHeightChange(callback: (data: number) => void): void {
LogUtil.info(`${TAG} onKeyboardHeightChange, getCurrentWindow`);
try {
this.getMainWindow();
if (!this.mainWindow) {
LogUtil.warn(`${TAG} onKeyboardHeightChange, mainWindow is null`);
return;
}
this.mainWindow.on('keyboardHeightChange', (data) => {
LogUtil.info(`${TAG} Succeeded in enabling the listener for keyboard height changes.`);
callback(data);
});
} catch (exception) {
LogUtil.error(`${TAG} Failed to enable the listener for keyboard height changes, ${exception?.message}`);
}
}
/**
* 关闭固定态输入法窗口软键盘高度变化的监听
*
* @param windowStage Stage模型window容器
*/
offKeyboardHeightChange(): void {
try {
this.getMainWindow();
if (!this.mainWindow) {
LogUtil.warn(`${TAG} onKeyboardHeightChange, mainWindow is null`);
return;
}
this.mainWindow.off('keyboardHeightChange');
} catch (exception) {
LogUtil.error(`Failed to disable the listener for keyboard height changes, ${exception?.message}`);
}
}
/**
* 获取当前窗口信息
*/
private getCurrentWindow(): window.WindowStage | undefined {
if (LocalStorage?.GetShared()?.has('externalWindowStage')) {
LogUtil.info('getCurrentWindow from externalWindowStage');
return LocalStorage?.GetShared().get('externalWindowStage');
}
if (LocalStorage?.GetShared()?.has('windowStage')) {
LogUtil.info('getCurrentWindow from windowStage');
return LocalStorage?.GetShared().get('windowStage');
}
LogUtil.warn('getCurrentWindow empty');
return undefined;
}
static setMainWindowBackgroundColor(windowStage: window.WindowStage, color: string): void {
windowStage.getMainWindow().then((data) => {
AppStorage.setOrCreate<window.Window>('mainWindow', data);
LogUtil.showWarn(TAG, `set current window id : ${data?.getWindowProperties()?.id}`);
data.setWindowBackgroundColor(color);
let windowRect: window.Rect = data.getWindowProperties().windowRect;
WindowManager.setNavBarWidth(windowRect.width);
AppStorage.setOrCreate<window.Size>('windowSize', { width: windowRect.width, height: windowRect.height });
}).catch((err: BusinessError) => {
LogUtil.error(`${TAG} GetMainWindow Failed: ${err?.message}`);
});
}
/**
* 获取避让区域尺寸
*
* @param windowStage Stage模型window容器
* @param callback 查询结果回调
*/
static getStatusBarSize(windowStage: window.WindowStage, callback: (w: number, h: number) => void): void {
windowStage.getMainWindow().then(async (mainWindow) => {
const topRect = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect || {
width: 0,
height: 0
};
callback(topRect.width, topRect.height);
}).catch((err: BusinessError) => {
LogUtil.error(`${TAG} GetMainWindow Failed: ${err?.message}`);
});
}
/**
* 获取避让区的高度
*
* @param windowStage Stage模型window容器
* @param avoidAreaDirect 哪个方向的避让区
* @param avoidAreaType 规避区域的类型
* @returns 避让区的高度
*/
static getAvoidAreaHeight(windowStage: window.WindowStage, avoidAreaDirect: AvoidAreaDirect,
avoidAreaType: window.AvoidAreaType): number {
let height = 0;
try {
let avoidArea = windowStage.getMainWindowSync().getWindowAvoidArea(avoidAreaType);
switch (avoidAreaDirect) {
case AvoidAreaDirect.TOP:
height = avoidArea.topRect.height;
break;
case AvoidAreaDirect.BOTTOM:
height = avoidArea.bottomRect.height;
break;
default:
break;
}
} catch (err) {
LogUtil.error(`${TAG} getAvoidAreaHeight Failed: ${err?.message}`);
}
return height;
}
/**
* 监听主窗口系统规避区变化
*
* @param windowStage Stage模型window容器
* @param callback 听主窗口系统规避区变化回调
*/
static onAvoidAreaChange(windowStage: window.WindowStage, callback: (data: AvoidAreaParams) => void) {
let windowClass: window.Window | undefined = undefined;
windowStage.getMainWindow((err: BusinessError, data) => {
const errCode: number = err.code;
if (errCode) {
LogUtil.error(`${TAG} Failed to obtain the main window. Cause code: ${err?.code}, message: ${err?.message}`);
return;
}
windowClass = data;
try {
windowClass.on('avoidAreaChange', callback);
} catch (exception) {
LogUtil.error(`${TAG} Failed to listen avoidAreaChange. ` +
`Cause code: ${exception?.code}, message: ${exception?.message}`);
}
});
}
/**
* 取消监听主窗口系统规避区变化
*
* @param windowStage Stage模型window容器
* @param callback 取消监听主窗口系统规避区变化回调
*/
static offAvoidAreaChange(windowStage: window.WindowStage, callback: (data: AvoidAreaParams) => void) {
let windowClass: window.Window | undefined = undefined;
windowStage.getMainWindow((err: BusinessError, data) => {
const errCode: number = err.code;
if (errCode) {
LogUtil.error(`${TAG} Failed to obtain the main window. Cause code: ${err?.code}, message: ${err?.message}`);
return;
}
windowClass = data;
try {
windowClass.off('avoidAreaChange', callback);
} catch (exception) {
LogUtil.error(`${TAG} Failed to listen avoidAreaChange. ` +
`Cause code: ${exception?.code}, message: ${exception?.message}`);
}
});
}
/**
* Loads content and set global windowStage info
* @param windowStage window manager
* @param pageUrl path of the page to which the content will be loaded
* @param externalNavigationUrl navigation url
*/
static loadContent(windowStage: window.WindowStage, pageUrl: string, externalNavigationUrl: string): void {
LogUtil.info(`${TAG} loadContent, pageUrl: ${pageUrl}, externalNavigationUrl: ${externalNavigationUrl}`);
AppStorage.setOrCreate<window.WindowStage>('windowStage', windowStage);
let localStorage: LocalStorage = new LocalStorage({
'externalWindowStage': windowStage,
} as Record<string, window.WindowStage>);
windowStage?.loadContent(pageUrl, localStorage).then(() => {
WindowManager.setMainWindowBackgroundColor(windowStage, WINDOW_BACKGROUND_COLOR);
});
AppStorage.SetOrCreate('externalNavigationUrl', externalNavigationUrl);
}
/**
* set keyboard avoid mode
*
* @param value the mode of keyboard avoid
*/
setKeyboardAvoidMode(value: KeyboardAvoidMode): void {
this.getCurrentWindow()?.getMainWindowSync().getUIContext().setKeyboardAvoidMode(value);
}
/**
* 为当前窗口添加或删除安全水印标志
*
* @param enable 是否对窗口添加标志位。true表示添加,false表示删除
*/
async setWaterMarkFlag(enable: boolean): Promise<void> {
LogUtil.info(`setWaterMarkFlag, enable: ${enable}`);
if (!Boolean(AppStorage.get<window.WindowStage>('windowStage')).valueOf()) {
LogUtil.error('setWaterMarkFlag error, windowStage is empty');
return;
}
try {
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
const stage = AppStorage.get<window.WindowStage>('windowStage') as window.WindowStage;
windowData = await stage.getMainWindow();
}
setWaterMarkFlag(windowData, enable);
} catch (exception) {
LogUtil.error(`Failed to set water mark flag of window. Cause: ${exception?.message}`);
}
}
/**
* 设置窗口的显示方向属性
*
* @param stage 窗口管理器
* @param orientation 窗口显示方向
*/
static async setWindowOrientation(stage: window.WindowStage, orientation: window.Orientation): Promise<void> {
if (!stage || !orientation) {
return;
}
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
windowData = await stage.getMainWindow();
}
WindowManager.setPreferredOrientation(windowData, orientation);
}
static async setWindowProperties(context: Context, windowStage: window.WindowStage): Promise<void> {
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
windowData = await windowStage.getMainWindow();
}
try {
// 第一次会错过监听
let windowStatusType = windowData?.getWindowStatus();
if (windowStatusType) {
LogUtil.info(`${TAG} getWindowStatus, window status type is ${windowStatusType}`);
AppStorage.setOrCreate<number>('windowMode', windowStatusType);
SettingsDataUtils.setSettingsDataWithContext(context, 'settings.windowMode', String(windowStatusType));
}
} catch (err) {
LogUtil.error(`${TAG} setWindowProperties failed. Error code: ${err.code} message: ${err.message}`);
}
Promise.resolve(windowData.on('windowStatusChange', (windowStatusType: window.WindowStatusType) => {
LogUtil.info(`${TAG} windowStatusChange, window type is ${windowStatusType}`);
AppStorage.setOrCreate<number>('windowMode', windowStatusType);
SettingsDataUtils.setSettingsDataWithContext(context, 'settings.windowMode', String(windowStatusType));
}));
}
/**
* 适配深色模式
* @param colorModeConfig 深色模式配置
*/
static async setSystemBar(colorModeConfig: ConfigurationConstant.ColorMode): Promise<void> {
AppStorage.setOrCreate<number>('changeMode', colorModeConfig);
let isLightColorMode: boolean = colorModeConfig === ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
const properties: window.SystemBarProperties = {
statusBarContentColor: isLightColorMode ? WINDOW_BG_BLACK : WINDOW_BG_WHITE,
navigationBarContentColor: isLightColorMode ? WINDOW_BG_BLACK : WINDOW_BG_WHITE,
};
let bgColor: string = isLightColorMode ? WINDOW_BG_APP : WINDOW_BG_BLACK;
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
let stage: window.WindowStage = AppStorage.get<window.WindowStage>('windowStage') as window.WindowStage;
windowData = await stage.getMainWindow();
}
LogUtil.warn(`${TAG} setWindowBackgroundColor: bgColor is ${bgColor}`);
windowData = await WindowManager.adjustWindow(windowData);
WindowManager.setWindowBackgroundColor(bgColor, windowData);
WindowManager.setWindowSystemBarProperties(properties, windowData);
}
/**
* 若当前使用的window不是主window,则将其替换成主window,当前文件是在common层, 因此还是先以 mainWindow 作为key来获取,避免其他类型
* 产品调用该方法时导致获取的window异常
*
* @param windowData 根据key为'mainWindow'从AppStorage中获取到 window对象
* @returns 调整后的window对象
*/
static async adjustWindow(windowData: window.Window): Promise<window.Window> {
let mainWindow: window.Window | undefined = AppStorage.get<window.Window>(MainConstants.MAIN_ABILITY_WINDOW);
if (!mainWindow) {
const stage: window.WindowStage | undefined =
AppStorage.get<window.WindowStage>(MainConstants.MAIN_WINDOW_STAGE) as window.WindowStage;
if (stage) {
mainWindow = await stage.getMainWindow();
}
}
const windowId: number | undefined = windowData?.getWindowProperties()?.id;
const mainWindowId: number | undefined = mainWindow?.getWindowProperties()?.id;
LogUtil.showInfo(TAG, `windowId: ${windowId}, mainWindowId: ${mainWindowId}`);
if (mainWindow && windowId !== mainWindowId) {
windowData = mainWindow;
}
return windowData;
}
static setMainWindowBgColorByColorMode(windowStage: window.WindowStage,
colorModeConfig: ConfigurationConstant.ColorMode): void {
let color = colorModeConfig === ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT ? WINDOW_BG_APP : WINDOW_BG_BLACK;
windowStage.getMainWindow().then((data) => {
AppStorage.setOrCreate<window.Window>('mainWindow', data);
// ExternalWifiSettingsAbility会导致AppStorage中的window和windowstage被覆盖,临时方案是将主window多存一份
AppStorage.setOrCreate<window.Window>(MainConstants.MAIN_ABILITY_WINDOW, data);
LogUtil.showWarn(TAG, `set current window id : ${data?.getWindowProperties()?.id}`);
WindowManager.setWindowBackgroundColor(color, data);
let windowRect: window.Rect = data.getWindowProperties().windowRect;
WindowManager.setNavBarWidth(windowRect.width);
AppStorage.setOrCreate<window.Size>('windowSize', { width: windowRect.width, height: windowRect.height });
try {
AppStorage.setOrCreate<number>('windowTitleHeight', data?.getWindowDecorHeight() ?? 0);
} catch (err) {
LogUtil.error(`${TAG} getWindowDecorHeight fail: code: ${err?.code} message: ${err?.message}`);
}
LogUtil.showInfo(TAG, `backgroundColor: ${color}, windowSize: (${windowRect.width}, ${windowRect.height})`);
}).catch((err: BusinessError) => {
LogUtil.error(`${TAG} getMainWindow fail: code: ${err?.code} message: ${err?.message}`);
});
}
static setWindowBackgroundColor(color: string, window: window.Window): void {
try {
setBgColor(window, color);
} catch (err) {
LogUtil.error(`${TAG} setWindowBackgroundColor fail, code: ${err?.code} message: ${err?.message}`);
}
}
static setWindowSystemBarProperties(properties: window.SystemBarProperties, window: window.Window): void {
try {
setSystemBar(window, properties);
} catch (err) {
LogUtil.error(`${TAG} setWindowSystemBarProperties fail, code: ${err?.code} message: ${err?.message}`);
}
}
/**
* 隐藏或显示浮窗
*
* @param shouldHide true表示隐藏,false表示取消隐藏
* @returns
*/
static async hideOrShowNonSystemFloatingWindows(shouldHide: boolean): Promise<void> {
let windowData: window.Window | undefined = AppStorage.get<window.Window>('mainWindow');
if (!windowData) {
let windowStage: window.WindowStage = AppStorage.get<window.WindowStage>('windowStage') as window.WindowStage;
windowData = await windowStage.getMainWindow();
}
try {
LogUtil.showInfo(TAG, `hideOrShowNonSystemFloatingWindows ${shouldHide}`);
hideOrShowFloatingWindows(windowData, shouldHide);
} catch (err) {
LogUtil.error(`${TAG} hideOrShowNonSystemFloatingWindows fail, code: ${err?.code} message: ${err?.message}`);
}
}
static setPreferredOrientation(window: window.Window, orientation: window.Orientation): void {
try {
setOrientation(window, orientation);
} catch (err) {
LogUtil.error(`${TAG} setPreferredOrientation fail, code: ${err?.code} message: ${err?.message}`);
}
}
static async adjustOrientation(): Promise<void> {
try {
const status: display.FoldStatus = await display.getFoldStatus();
DisplayManager.getInstance().setFoldStatus(status as number);
} catch (e) {
LogUtil.error(`${TAG} set orientation error message: ${e?.message}`);
}
}
static async registerFoldChangeListener(): Promise<void> {
if (!DeviceUtil.isFoldable()) {
return;
}
try {
display.on('foldStatusChange', (status: number) => {
LogUtil.info(`on fold status changed: ${status}`);
DisplayManager.getInstance().setFoldStatus(status);
emitter.emit({
eventId: EVENT_ID_FOLD_STATUS_CHANGE,
priority: emitter.EventPriority.IMMEDIATE,
}, {
data: {
EVENT_KEY_FOLD_STATUS_CHANGE: status,
}
});
})
} catch (err) {
LogUtil.error(`${TAG} register foldStatusChange: code: ${err?.code} message: ${err?.message}`);
}
}
static unregisterFoldChangeListener(): void {
if (DeviceUtil.isFoldable()) {
try {
display.off('foldStatusChange');
} catch (e) {
LogUtil.error(`${TAG} unregister foldStatusChange: ${e?.message}`);
}
}
}
static async registerWindowStageListener(): Promise<void> {
try {
const stage: window.WindowStage = AppStorage.get<window.WindowStage>('windowStage') as window.WindowStage;
if (stage) {
stage.on('windowStageEvent', (event: window.WindowStageEventType) => {
LogUtil.info(`${TAG} on window stage changed: ${event}`);
emitter.emit({
eventId: EVENT_ID_ON_WINDOW_STAGE_CHANGED,
priority: emitter.EventPriority.IMMEDIATE,
}, {
data: {
KEY_ID_FINGERPRINT_DB_UPDATE: event,
}
});
});
}
} catch (err) {
LogUtil.error(`${TAG} register window stage listener failed: code: ${err?.code} message: ${err?.message}`);
}
}
static unregisterWindowStageListener(): void {
try {
const stage: window.WindowStage = AppStorage.get<window.WindowStage>('windowStage') as window.WindowStage;
if (stage) {
stage?.off('windowStageEvent');
}
} catch (err) {
LogUtil.error(`${TAG} unregister window stage listener failed: code: ${err?.code} message: ${err?.message}`);
}
}
/**
* vp转化成px,不受窗口属性影响
*
* @param value 传入的vp值
* @returns 根据当前DPI转化后的的px值
*/
static vpToPx(value: number): number {
try {
let dpi: number = display.getDefaultDisplaySync().densityDPI;
let pxValue: number = dpi / VP_TO_PX_SCALE * value;
return pxValue;
} catch (err) {
LogUtil.error(`${TAG} vpToPx failed: code: ${err?.code}`);
return value;
}
}
/**
* 字体过大的情况需要为wifi window弹框添加额外高度,避免截断
*
* @returns 字体变大后额外增加的高度
*/
static getExtraFontScaleHeight(isHiLink?: boolean): number {
let value: number = 0;
const extraHeight: number = 18;
try {
let currentFontScaleSize: number = Number(SettingsDataUtils
.getSettingsDataDomain(Settings.display.FONT_SCALE, DisplayConstant.DEFAULT_FONT_SIZE_SCALE,
Settings.domainName.USER_PROPERTY));
// 字体放大后,需要额外增加高度让弹框window不截断
if (currentFontScaleSize > DisplayConstant.TEXT_FONT_SIZE_LARGE) {
value = extraHeight;
}
if (isHiLink) {
let hiLinkContentText: string = ResourceUtil.getFormatStringByNameSync('wifi_hlink_description', '');
let hiLinkContentSize: SizeOptions = measure.measureTextSize({
textContent: hiLinkContentText,
fontSize: $r('sys.float.Body_M'),
fontWeight: FontWeight.Regular,
fontFamily: 'HarmonyHeiTi'
});
let contentWidth: number = vp2px(352);
let lines: number = Number(hiLinkContentSize.width) / contentWidth;
value = lines > 2 ? px2vp(Number(hiLinkContentSize.height)) + 6 : 0;
}
return value;
} catch (err) {
LogUtil.error(`${TAG} getExtraFontPadding failed: code: ${err?.code}`);
return value;
}
}
/**
* 获取 Window 窗口背景色
*
*/
public static getDialogWindowBackgroundColor(): string {
try {
LogUtil.info(`${TAG} getDarkMode sucess`);
return uiAppearance.getDarkMode() === COLOR_MODE_DARK ?
DIALOG_WINDOW_ULTRA_THICK_BLACK : DIALOG_WINDOW_ULTRA_THICK_WHITE;
} catch (err) {
LogUtil.error(`${TAG} getDarkMode fail`);
return DIALOG_WINDOW_ULTRA_THICK_WHITE;
}
}
/**
* 智慧屏navigation不支持设置栅格,手动计算navBarWidth
*
* @param value 窗宽
*/
public static updateGridColumnsTV(windowWidth: number): void {
let pageWidth: Record<string, number> = WindowManager.getHomeNavWidthTV(windowWidth);
AppStorage.setOrCreate<number>(GRID_COLUMN_WIDTH_KEY, pageWidth.columnWidth);
AppStorage.setOrCreate<number>(GRID_ROW_MARGIN_FULL_PAGE_KEY,
pageWidth.columnWidth * GRID_CONTENT_MARGIN_RATIO);
AppStorage.setOrCreate<number>(GRID_ROW_HOME_WIDTH_KEY, pageWidth.homeNavWidth);
}
/**
* 获取智慧屏navigation宽
* @param value 窗宽
* @returns 智慧屏navigation宽
*/
public static getHomeNavWidthTV(windowWidth: number): Record<string, number> {
let densityPixels: number = display.getDefaultDisplaySync().densityPixels;
if (densityPixels === 0) {
LogUtil.error(`${TAG} densityPixels is 0`);
return {
'columnWidth': 0,
'homeNavWidth': 0
};
}
let windowWidthVp: number = windowWidth / densityPixels;
let columnWidth: number = (windowWidthVp - GRID_ROW_MARGIN * 2 -
(GRID_ROW_COLUMNS - 1) * GRID_ROW_GUTTER) / GRID_ROW_COLUMNS;
let homeNavWidth: number = columnWidth * GRID_COLUMN_HOME_SPAN +
GRID_COLUMN_HOME_GUTTER_WIDTH + GRID_ROW_MARGIN;
return {
'columnWidth': columnWidth,
'homeNavWidth': homeNavWidth
};
}
}
@Concurrent
export async function getNavBarWidth(width: number, context: Context): Promise<number> {
const widthScale: number = 0.4;
const maxNavBarWidth: number = 480;
const VP_TO_PX_SCALE: number = 160;
const DPI_VALUE_NULL: string = '';
const DISPLAY_DPI: string = 'system_default_dpi_value';
let calNavBarWidth: number = 0;
try {
let dpiValue: string = SettingsDataUtils.getSettingsDataWithContext(context, DISPLAY_DPI, '');
LogUtil.info(`getNavBarWidth dpiValue is ${dpiValue}`);
if (dpiValue === DPI_VALUE_NULL) {
calNavBarWidth = Math.min(width * widthScale, maxNavBarWidth * display.getDefaultDisplaySync()
.densityDPI / VP_TO_PX_SCALE);
} else {
calNavBarWidth = Math.min(width * widthScale, maxNavBarWidth * Number.parseInt(dpiValue) / VP_TO_PX_SCALE);
}
LogUtil.info(`getNavBarWidth navigation bar width: ${calNavBarWidth}`);
} catch (error) {
LogUtil.error(`getNavBarWidth failed: ${error?.message}`);
}
return calNavBarWidth;
}
@Concurrent
function setBgColor(window: window.Window, color: string): void {
window.setWindowBackgroundColor(color);
}
@Concurrent
function setSystemBar(window: window.Window, properties: window.SystemBarProperties): void {
window.setWindowSystemBarProperties(properties);
}
@Concurrent
function hideOrShowFloatingWindows(window: window.Window, shouldHide: boolean): void {
window.hideNonSystemFloatingWindows(shouldHide);
}
@Concurrent
function setOrientation(window: window.Window, orientation: window.Orientation): void {
window.setPreferredOrientation(orientation);
}
@Concurrent
function setWaterMarkFlag(window: window.Window, enable: boolean): void {
window.setWaterMarkFlag(enable);
}