/*
* Copyright (c) Huawei Device 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 display from '@ohos.display';
import {
LogDomain,
LogHelper,
Trace
} from '@ohos/basicutils';
import { DeviceHelper } from '@ohos/frameworkwrapper';
import {
SCBPropertyChangeReason,
SCBScreenProperty,
SCBScreenSessionManager,
SCBSceneSessionManager
} from '@ohos/windowscene';
import { AbstractObserverManager } from '../base/AbstractObserverManager';
import { GlobalStatusCache, StatusCacheConst } from '../base/GlobalStatusCache';
import { DisplayUtils } from '../utils/DisplayUtils';
const TAG: string = 'DisplayStateManager';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.KG, TAG);
/**
* 系统显示变化监听
*/
export interface DisplayStateListener {
/**
* 屏幕旋转状态发生改变
*/
onOrientationChange?: (orientation: number) => void;
/**
* 屏幕尺寸发生改变
*/
onScreenSizeChange?: (width: number, height: number) => void;
/**
* 屏幕属性发生改变
*/
onScreenPropertyChange?: (screenProperty: SCBScreenProperty, reason: SCBPropertyChangeReason) => void;
/**
* 屏幕折叠状态发生改变(透传)
*/
onFoldStateChange?: (isFoldExpand: boolean) => void;
/**
* 折叠状态变化监听
*
* @param foldStatus 折叠状态
*/
onFoldStatusChange?: (foldStatus: number) => void;
}
export interface ChangeParams {
isPropertyChange?: boolean;
isWidthChange?: boolean;
isHeightChange?: boolean;
isOrientationChange?: boolean;
isFoldChange?: boolean;
isFoldStatusChange?: boolean;
}
/**
* 系统显示变化管理类
*/
export class DisplayStateManager extends AbstractObserverManager<DisplayStateListener> {
private static sInstance: DisplayStateManager;
/**
* 获取屏幕状态变化管理单例
*
* @returns 屏幕状态变化管理
*/
public static getInstance(): DisplayStateManager {
if (DisplayStateManager.sInstance == null) {
DisplayStateManager.sInstance = new DisplayStateManager();
}
return DisplayStateManager.sInstance;
}
private _persistentId: number | undefined = 0;
private _screenProperty?: SCBScreenProperty;
private _reason: SCBPropertyChangeReason = SCBPropertyChangeReason.UNDEFINED;
private _isFoldExpanded: boolean = false;
private _foldStatus: number = DisplayUtils.getFoldStatus();
private _screenWidth: number = 0;
private _screenHeight: number = 0;
private _screenRotation: number = 0;
private readonly _screenPropertyChangeCallback =
(screenProperty: SCBScreenProperty, reason: SCBPropertyChangeReason): void => {
Trace.start(Trace.CORE_METHOD_SCREEN_PROPERTY_CHANGE_KEYGUARD);
this.updateScreenProperty(screenProperty, reason);
Trace.end(Trace.CORE_METHOD_SCREEN_PROPERTY_CHANGE_KEYGUARD);
};
protected doInit(params?: number): void {
this._persistentId = params;
SCBScreenSessionManager.getInstance().registerScreenPropertyChangeCallbacks(
this._screenPropertyChangeCallback, undefined, false, this._persistentId);
this.updateFoldExpandState();
}
protected doRelease(): void {
SCBScreenSessionManager.getInstance().unRegisterScreenPropertyChangeCallbacks(
this._screenPropertyChangeCallback, undefined, false, this._persistentId);
}
/**
* 无条件设置屏幕属性
*/
public setScreenProperty(screenProperty: SCBScreenProperty): void {
this.updateScreenProperty(screenProperty);
}
public getFoldStatus(): number {
return this._foldStatus;
}
/**
* 注册屏幕显示变化监听
*
* @param listener 监听器
*/
public registerObserver(
listener: DisplayStateListener,
isCallbackImmediately: boolean = true,
highPriory: boolean = false
): void {
if (!listener) {
return;
}
super.registerObserver(listener, isCallbackImmediately, highPriory);
if (isCallbackImmediately) {
// 注册的时候默认立即通知一次
log.showDebug('notifyScreenDisplayState immediate');
// 注册时默认通知注册者,而不是所有
this.notifyCurrentStateToListener(listener, {
isPropertyChange: true,
isWidthChange: true,
isHeightChange: true,
isOrientationChange: true,
isFoldChange: true,
isFoldStatusChange: true
});
}
}
/**
* 获取当前屏幕是否为折叠状态
*/
public isFoldExpanded(): boolean {
return this._isFoldExpanded;
}
/**
* 获取当前屏幕是否为竖屏
*/
public isPortrait(): boolean {
return this._screenRotation === display.Orientation.PORTRAIT ||
this._screenRotation === display.Orientation.PORTRAIT_INVERTED;
}
/**
* 获取当前屏幕属性
*
* @returns 屏幕属性
*/
public getScreenProperty(): SCBScreenProperty | undefined {
return this._screenProperty;
}
/**
* 获取锁屏页面可旋转状态
*
* @return 是否可旋转
*/
public getScreenSessionRotatable(): boolean {
let systemSceneSession = SCBSceneSessionManager.getInstance().getSystemSceneSessionWithId(this._persistentId);
let rotateAble: boolean | undefined = systemSceneSession?.isRotatable ||
(!DeviceHelper.isSmallFoldProduct() && this._isFoldExpanded);
return rotateAble ?? false;
}
/**
* 更新折叠状态
*/
public updateFoldStatus(): void {
this.broadcastStateChange({
isFoldChange: this.updateFoldExpandState(),
isFoldStatusChange: this.updateFoldStatusState()
});
}
private updateScreenProperty(screenProperty: SCBScreenProperty, reason?: SCBPropertyChangeReason): void {
let isWidthChange: boolean = this.updateWidth(screenProperty);
let isHeightChange: boolean = this.updateHeight(screenProperty);
let isOrientationChange: boolean = this.updateOrientation();
let isFoldChange: boolean = this.updateFoldExpandState(screenProperty);
let isFoldStatusChange: boolean = this.updateFoldStatusState();
this._reason = reason ?? SCBPropertyChangeReason.UNDEFINED;
this._screenProperty = screenProperty;
this._screenWidth = screenProperty.width;
this._screenHeight = screenProperty.height;
this._screenRotation = DisplayUtils.getDefaultDisplaySync()?.orientation ?? 0;
this.broadcastStateChange({
isPropertyChange: true,
isWidthChange: isWidthChange,
isHeightChange: isHeightChange,
isOrientationChange: isOrientationChange,
isFoldChange: isFoldChange,
isFoldStatusChange: isFoldStatusChange,
});
}
private updateWidth(screenProperty: SCBScreenProperty): boolean {
if (!this._screenProperty || (screenProperty.width !== this._screenWidth)) {
GlobalStatusCache.getInstance().setStatus(StatusCacheConst.SCREEN_WIDTH_KEY, screenProperty.width);
return true;
}
return false;
}
private updateHeight(screenProperty: SCBScreenProperty): boolean {
if (!this._screenProperty || (screenProperty.height !== this._screenHeight)) {
GlobalStatusCache.getInstance().setStatus(StatusCacheConst.SCREEN_HEIGHT_KEY, screenProperty.height);
return true;
}
return false;
}
private updateOrientation(): boolean {
try {
return display.getDefaultDisplaySync().orientation !== this._screenRotation;
} catch (e) {
log.showError('getDefaultDisplaySync failed');
return false;
}
}
private updateFoldExpandState(screenProperty?: SCBScreenProperty): boolean {
if (!DisplayUtils.isFoldAble()) {
return false;
}
log.showInfo(`updateFoldExpandState`);
let isFoldExpanded: boolean = DisplayUtils.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED ||
DisplayUtils.getFoldStatus() === display.FoldStatus.FOLD_STATUS_HALF_FOLDED ||
DisplayUtils.isUltraScreenGStatus(screenProperty);
let isFoldChange: boolean = this._isFoldExpanded !== isFoldExpanded;
this._isFoldExpanded = isFoldExpanded;
return isFoldChange;
}
private updateFoldStatusState(): boolean {
if (!DisplayUtils.isFoldAble()) {
return false;
}
log.showInfo(`updateFoldStatusState`);
let foldStatus: number = DisplayUtils.getFoldStatus();
let isFoldStatusChange: boolean = this._foldStatus !== foldStatus;
this._foldStatus = foldStatus;
return isFoldStatusChange;
}
private broadcastStateChange(params: ChangeParams): void {
if (!this.hasObserver()) {
log.showError('empty observer, skip broadcast');
return;
}
this.printCurrentDisplayState(params);
this.broadcastDataChange(observer => this.notifyScreenDisplayStateChange(observer, params));
}
private printCurrentDisplayState(params: ChangeParams, immediately: boolean = false): void {
log.showInfo(`broadcastStateChange` + (immediately ? ` immediately` : ``) +
` isWidthChange ${!!params.isWidthChange},` + ` isHeightChange ${!!params.isHeightChange},` +
(!!params.isWidthChange || !!params.isHeightChange ?
` width ${this._screenProperty?.width} height ${this._screenProperty?.height},` : ``) +
` isOrientationChange ${!!params.isOrientationChange}` +
(!!params.isOrientationChange ? ` rotation ${this._screenRotation},` : ``) +
` isFoldChange ${!!params.isFoldChange},` +
(!!params.isFoldChange ? ` isFoldExpanded ${this._isFoldExpanded},` : ``) +
` isFoldStatusChange ${!!params.isFoldStatusChange}` +
(!!params.isFoldStatusChange ? ` foldStatus ${this._foldStatus}` : ``));
}
private notifyCurrentStateToListener(self: DisplayStateListener, params: ChangeParams): void {
this.printCurrentDisplayState(params, true);
this.notifyScreenDisplayStateChange(self, params);
}
private notifyScreenDisplayStateChange(listener: DisplayStateListener, changeParams: ChangeParams): void {
if (!!this._screenProperty && !!changeParams.isPropertyChange) {
listener.onScreenPropertyChange?.(this._screenProperty, this._reason);
if (!!changeParams.isOrientationChange) {
listener.onOrientationChange?.(this._screenRotation);
}
if (!!changeParams.isWidthChange || !!changeParams.isHeightChange) {
listener.onScreenSizeChange?.(this._screenWidth, this._screenHeight);
}
}
if (!!changeParams.isFoldChange) {
listener.onFoldStateChange?.(this._isFoldExpanded);
}
if (!!changeParams.isFoldStatusChange) {
listener.onFoldStatusChange?.(this._foldStatus);
}
}
}