/*
* Copyright (c) 2026 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 { UIContext, window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { sensor } from '@kit.SensorServiceKit';
import { WindowInfo } from '../../model/WindowInfo';
const TAG = '[WindowUtil]'
export enum ImmersiveType {
NORMAL,
IMMERSIVE,
FULLSCREEN_IMMERSIVE
}
export class WindowOperationClass {
public uiContext?: UIContext;
public mainWindow: window.Window;
public mainWindowInfo: WindowInfo = new WindowInfo();
public onStatusTypeChange: (statusType: window.WindowStatusType) => void = (statusType: window.WindowStatusType) => {
this.mainWindowInfo.windowStatusType = statusType;
}
// Callback function after window resize.
public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
this.mainWindowInfo.windowSize = windowSize;
if (this.uiContext) {
this.mainWindowInfo.widthBp = this.uiContext.getWindowWidthBreakpoint();
this.mainWindowInfo.heightBp = this.uiContext.getWindowHeightBreakpoint();
}
};
// [Start avoid_area]
// Callback when the system safe area insets change.
public onAvoidAreaChange: (avoidOptions: window.AvoidAreaOptions) => void =
(avoidOptions: window.AvoidAreaOptions) => {
if (avoidOptions.type === window.AvoidAreaType.TYPE_SYSTEM) {
this.mainWindowInfo.AvoidSystem = avoidOptions.area;
} else if (avoidOptions.type === window.AvoidAreaType.TYPE_CUTOUT) {
this.mainWindowInfo.AvoidCutout = avoidOptions.area;
} else if (avoidOptions.type === window.AvoidAreaType.TYPE_SYSTEM_GESTURE) {
this.mainWindowInfo.AvoidSystemGesture = avoidOptions.area;
} else if (avoidOptions.type === window.AvoidAreaType.TYPE_KEYBOARD) {
this.mainWindowInfo.AvoidKeyboard = avoidOptions.area;
} else if (avoidOptions.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
this.mainWindowInfo.AvoidNavigationIndicator = avoidOptions.area;
}
};
// [End avoid_area]
constructor(mainWindow: window.Window) {
this.mainWindow = mainWindow;
}
async setImmersiveType(type: ImmersiveType): Promise<void> {
if (type === ImmersiveType.NORMAL) {
this.mainWindow.setWindowLayoutFullScreen(false)
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
});
this.setSystemBarEnabled(true);
// Only used after the function loadContent() or setUIContent().
try {
this.mainWindow.setWindowDecorVisible(true);
} catch (err) {
hilog.error(0x0000, TAG,
`Failed to set Window Decor Visible true. Code: ${err.code}, message: ${err.message}`);
}
this.recover();
} else if (type === ImmersiveType.IMMERSIVE) {
this.mainWindow.setWindowLayoutFullScreen(true)
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
});
this.setSystemBarEnabled(true);
try {
this.mainWindow.setWindowDecorVisible(true);
} catch (err) {
hilog.error(0x0000, TAG,
`Failed to set Window Decor Visible false. Code: ${err.code}, message: ${err.message}`);
}
this.recover();
} else if (type === ImmersiveType.FULLSCREEN_IMMERSIVE) {
this.mainWindow.setWindowLayoutFullScreen(true)
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
});
try {
// Maximized state of free window mode on PC devices.
if (this.mainWindow.getWindowStatus() === window.WindowStatusType.MAXIMIZE ||
(this.mainWindow.getWindowStatus() === window.WindowStatusType.FLOATING &&
this.mainWindow.getWindowDecorHeight() !== 0)) {
this.mainWindow.maximize()
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in maximizing the window.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG,
`Failed to maximize the window. Code: ${err.code}, message: ${err.message}`);
});
}
} catch (err) {
hilog.info(0x0000, TAG, `Failed to get WindowStatus. Code: ${err.code}, message: ${err.message}`);
}
this.setSystemBarEnabled(false);
try {
this.mainWindow.setWindowDecorVisible(false);
} catch (err) {
hilog.error(0x0000, TAG,
`Failed to set Window Decor Visible false. Code: ${err.code}, message: ${err.message}`);
}
}
this.mainWindowInfo.isImmersive = type;
}
setUIContext(): void {
try {
this.uiContext = this.mainWindow.getUIContext();
} catch (err) {
hilog.info(0x0000, TAG, `Failed to get UIContext. Code: ${err.code}, message: ${err.message}`);
}
}
async setSystemBarEnabled(isVisible: boolean): Promise<void> {
this.mainWindow.setSpecificSystemBarEnabled('status', isVisible)
.then(() => {
hilog.info(0x0000, TAG,
`Succeeded in setting status bar to be invisible ${isVisible ? 'visible' : 'invisible'}.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG,
`Failed to set status bar to be invisible. Code: ${err.code}, message: ${err.message}`);
});
this.mainWindow.setSpecificSystemBarEnabled('navigationIndicator', isVisible)
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in setting navigation indicator to be invisible.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG,
`Failed to set navigation indicator to be invisible. Code: ${err.code}, message: ${err.message}`);
});
}
async recover(): Promise<void> {
try {
if (this.mainWindow.getWindowStatus() === window.WindowStatusType.FULL_SCREEN) {
this.mainWindow.recover()
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in recovering the window.`);
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `Failed to recover the window. Code: ${err.code}, message: ${err.message}`);
});
}
} catch (err) {
hilog.info(0x0000, TAG, `Failed to get WindowStatus. Code: ${err.code}, message: ${err.message}`);
}
}
async setWindowOrientation(orientation: window.Orientation): Promise<void> {
this.mainWindow.setPreferredOrientation(orientation)
.then(() => {
hilog.info(0x0000, TAG, `Succeeded in setting window orientation.`);
// Update window orientation.
this.mainWindowInfo.orientation = orientation;
})
.catch((err: BusinessError) => {
hilog.error(0x0000, TAG, `Failed to set window orientation. Code: ${err.code}, message: ${err.message}`);
});
}
updateWindowInfo(): void {
try {
// First time get window status.
this.mainWindowInfo.windowStatusType = this.mainWindow.getWindowStatus();
this.mainWindow.on('windowStatusChange', this.onStatusTypeChange);
// First time get window size.
let width: number = this.mainWindow.getWindowProperties().windowRect.width;
let height: number = this.mainWindow.getWindowProperties().windowRect.height;
let windowSize: window.Size = {
width: width,
height: height
}
this.mainWindowInfo.windowSize = windowSize;
// First time get width/height breakpoint.
this.mainWindowInfo.widthBp = this.uiContext!.getWindowWidthBreakpoint();
this.mainWindowInfo.heightBp = this.uiContext!.getWindowHeightBreakpoint();
// Register for window size change monitoring, update window size and width/height breakpoint.
this.mainWindow.on('windowSizeChange', this.onWindowSizeChange);
// First time get avoid area infos.
this.mainWindowInfo.AvoidSystem = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
this.mainWindowInfo.AvoidNavigationIndicator =
this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.mainWindowInfo.AvoidCutout = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT);
this.mainWindowInfo.AvoidSystemGesture =
this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM_GESTURE);
this.mainWindowInfo.AvoidKeyboard = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_KEYBOARD);
this.mainWindow.on('avoidAreaChange', this.onAvoidAreaChange);
} catch (err) {
hilog.error(0x0000, TAG, `Failed to update WindowInfo. Code: ${err.code}, message: ${err.message}`);
}
}
async release(): Promise<void> {
try {
this.mainWindow.off('windowStatusChange');
this.mainWindow.off('windowSizeChange');
this.mainWindow.off('avoidAreaChange');
} catch (err) {
hilog.error(0x0000, TAG, `Failed to off mainWindow events. Code: ${err.code}, message: ${err.message}`);
}
try {
sensor.off(sensor.SensorId.GRAVITY);
} catch (err) {
hilog.error(0x0000, TAG, `Failed to off sensor gravity event. Code: ${err.code}, message: ${err.message}`);
}
}
}