• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { Log } from '../../utils/Log';
17import { Constants } from './Constants';
18import { UiUtil } from '../../utils/UiUtil';
19import { BroadCast } from '../../utils/BroadCast';
20import type window from '@ohos.window';
21
22const TAG: string = 'common_ScreenManager';
23
24type SystemBarKeys = 'status' | 'navigation';
25
26export enum ColumnSize {
27  COLUMN_ONE_POINT_FIVE = 1.5,
28  COLUMN_TWO = 2,
29  COLUMN_FOUR = 4,
30  COLUMN_SIX = 6,
31  COLUMN_EIGHT = 8,
32  COLUMN_TWELVE = 12
33}
34
35enum ScreenWidth {
36  WIDTH_MEDIUM = 520,
37  WIDTH_LARGE = 840
38}
39
40enum WindowMode {
41  UNDEFINED = 1,
42  FULLSCREEN,
43  PRIMARY,
44  SECONDARY,
45  FLOATING
46}
47
48export class ScreenManager {
49  static readonly ON_WIN_SIZE_CHANGED: string = 'on_win_size_changed';
50  static readonly ON_SPLIT_MODE_CHANGED: string = 'on_split_mode_changed';
51  static readonly ON_LEFT_BLANK_CHANGED: string = 'on_left_blank_changed';
52  static readonly DEFAULT_WIDTH: number = 1920;
53  static readonly DEFAULT_HEIGHT: number = 1080;
54
55  // no interface is available to obtain the height of WindowDecor. WindowDecor px Height: 56.
56  static readonly WIN_DECOR_HEIGHT_PX: number = 56;
57
58  private winWidth: number = 0.0;
59  private winHeight: number = 0.0;
60  private statusBarHeight: number = 0;
61  private naviBarHeight: number = 0;
62  private leftBlank: [number, number, number, number] = [0, 0, 0, 0];
63  private broadcast: BroadCast = new BroadCast();
64  private mainWindow: window.Window = undefined;
65  private columns: number = ColumnSize.COLUMN_FOUR;
66
67  // Default orientation for Pc
68  private horizontal: boolean = true;
69
70  // Default sidebar for Pc
71  private sidebar: boolean = true;
72  private windowMode: WindowMode = WindowMode.UNDEFINED;
73  private isFullScreen: boolean = true;
74
75  private constructor() {
76    Log.info(TAG, 'constructor');
77    this.horizontal = false;
78    this.sidebar = false;
79  }
80
81  static getInstance(): ScreenManager {
82    if (AppStorage.get(Constants.APP_KEY_SCREEN_MANAGER) == null) {
83      AppStorage.SetOrCreate(Constants.APP_KEY_SCREEN_MANAGER, new ScreenManager());
84    }
85    let manager: ScreenManager = AppStorage.get(Constants.APP_KEY_SCREEN_MANAGER);
86    return manager;
87  }
88
89  initializationSize(win: window.Window): Promise<void> {
90    this.mainWindow = win;
91    let properties: window.WindowProperties = win.getWindowProperties();
92    this.isFullScreen = properties.isFullScreen;
93
94    // Area data obtained through the system interface,
95    // There is a possibility that the area data is incorrect.
96    const statusBarHeight: number = properties && properties.windowRect ? properties.windowRect.top : this.statusBarHeight;
97    AppStorage.SetOrCreate<number>('statusBarHeight', statusBarHeight);
98    return new Promise<void>((resolve, reject) => {
99      if (!properties || !properties.windowRect) {
100        reject();
101      }
102      let size: window.Rect = properties.windowRect;
103      Log.info(TAG, `display screen windowRect: ${JSON.stringify(size)}`);
104      this.winWidth = px2vp(size.width);
105      this.winHeight = px2vp(size.height);
106      Log.info(TAG, `init winSize: ${size.width}*${size.height} px, ${this.winWidth}*${this.winHeight} vp`);
107      if (this.winWidth < ScreenWidth.WIDTH_MEDIUM) {
108        this.columns = ColumnSize.COLUMN_FOUR;
109      } else if (this.winWidth >= ScreenWidth.WIDTH_MEDIUM && this.winWidth < ScreenWidth.WIDTH_LARGE) {
110        this.columns = ColumnSize.COLUMN_EIGHT;
111      } else {
112        this.columns = ColumnSize.COLUMN_TWELVE;
113      }
114      this.emit(ScreenManager.ON_WIN_SIZE_CHANGED, [size]);
115      resolve();
116    });
117  }
118
119  /**
120   * Add Listeners
121   *
122   * @param event
123   * @param fn
124   */
125  on(event: string, fn: Function): void {
126    this.broadcast.on(event, fn);
127  }
128
129  /**
130   * Delete Listeners
131   *
132   * @param event
133   * @param fn
134   */
135  off(event: string, fn: Function): void {
136    this.broadcast.off(event, fn);
137  }
138
139  setWinWidth(width: number): void{
140    this.winWidth = width;
141  }
142
143  // Unit:vp
144  getWinWidth(): number {
145    return this.winWidth;
146  }
147
148  // Unit:vp
149  getWinHeight(): number {
150    return this.winHeight;
151  }
152
153  // Returns the width of the layout area (LayoutWidth = WindowWidth).
154  getWinLayoutWidth(): number {
155    return this.winWidth;
156  }
157
158  // Returns the height of the layout area (LayoutHeight = WindowHeight - WindowDecorHeight).
159  getWinLayoutHeight(): number {
160    let deviceTp: string = AppStorage.get('deviceType') as string;
161    Log.debug(TAG, `deviceTp=${deviceTp}, isFull=${this.isFullScreen}, winH=${this.winHeight}`);
162    if (deviceTp === Constants.DEFAULT_DEVICE_TYPE) {
163      return this.winHeight;
164    }
165    let winDecorHeight: number = this.isFullScreen ? 0 : px2vp(ScreenManager.WIN_DECOR_HEIGHT_PX);
166    return this.winHeight - winDecorHeight;
167  }
168
169  getStatusBarHeight(): number {
170    return this.statusBarHeight;
171  }
172
173  getNaviBarHeight(): number {
174    return this.naviBarHeight;
175  }
176
177  initWindowMode(): void {
178    Log.debug(TAG, `start to initialize photos application window mode: ${this.windowMode}`);
179    this.checkWindowMode();
180    this.mainWindow && this.setMainWindow();
181  }
182
183  destroyWindowMode(): void {
184    Log.debug(TAG, `start to destory photos application window mode: ${this.windowMode}`);
185    try {
186      this.mainWindow.off('windowSizeChange');
187    } catch (error) {
188      Log.error(TAG, `destroy window error: ${error}`);
189    }
190  }
191
192  isSplitMode(): boolean {
193    return (WindowMode.PRIMARY === this.windowMode || WindowMode.SECONDARY === this.windowMode);
194  }
195
196  async checkWindowMode(): Promise<void> {
197    let before = this.windowMode;
198    let windowStage: window.WindowStage = AppStorage.get<window.WindowStage>('photosWindowStage');
199    // @ts-ignore
200    let mode: WindowMode = await windowStage.getWindowMode() as WindowMode;
201    Log.info(TAG, `photos application before/current window mode: ${before}/${mode}`);
202
203    if (before == mode) {
204      return;
205    }
206    this.windowMode = mode;
207    if (WindowMode.FULLSCREEN == this.windowMode) {
208      this.setFullScreen();
209    } else {
210      this.setSplitScreen();
211    }
212  }
213
214  setMainWindow(): void {
215    Log.debug(TAG, 'setMainWindow');
216    this.mainWindow.on('windowSizeChange', (data: window.Size) => {
217      Log.debug(TAG, `windowSizeChange ${JSON.stringify(data)}`);
218      try {
219        let properties: window.WindowProperties = this.mainWindow.getWindowProperties();
220        this.isFullScreen = properties.isFullScreen;
221      } catch (exception) {
222        Log.error(TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(exception));
223      }
224      this.onWinSizeChanged(data);
225    })
226    this.mainWindow.getProperties().then((prop: window.WindowProperties) => {
227      Log.info(TAG, `Window prop: ${JSON.stringify(prop)}`);
228      this.onWinSizeChanged(prop.windowRect);
229    });
230  }
231
232  destroyMainWindow(): void {
233    this.mainWindow.off('windowSizeChange');
234  }
235
236  getAvoidArea(): void {
237    let topWindow: window.Window = this.getMainWindow();
238    topWindow.getAvoidArea(0, (err, data: window.AvoidArea) => {
239      Log.info(TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(data));
240      this.onLeftBlankChanged(data);
241    });
242  }
243
244  setFullScreen(): void {
245    let topWindow: window.Window = this.getMainWindow();
246    Log.debug(TAG, 'getTopWindow start');
247    try {
248      topWindow.setLayoutFullScreen(true, () => {
249        Log.debug(TAG, 'setFullScreen true Succeeded');
250        if (AppStorage.get('deviceType') as string !== Constants.DEFAULT_DEVICE_TYPE) {
251          this.hideStatusBar();
252        } else {
253          this.setWindowBackgroundColorDefault(true);
254        }
255      });
256    } catch (err) {
257      Log.error(TAG, `setFullScreen err: ${err}`);
258    }
259  }
260
261  setWindowBackgroundColorDefault(defaultColor: boolean): void {
262    this.getMainWindow().setWindowBackgroundColor(defaultColor ? '#F1F3F5' : '#000000');
263  }
264
265  setSplitScreen(): void {
266    try {
267      this.statusBarHeight = 0;
268      this.naviBarHeight = 0;
269      this.leftBlank = [0, 0, 0, 0];
270      this.emit(ScreenManager.ON_LEFT_BLANK_CHANGED, [this.leftBlank]);
271    } catch (err) {
272      Log.error(TAG, `setSplitScreen err: ${err}`);
273    }
274  }
275
276  hideStatusBar(): void {
277    Log.debug(TAG, 'hideStatusBar start');
278    let topWindow: window.Window = this.getMainWindow();
279    Log.debug(TAG, 'getTopWindow start');
280    let names: Array<SystemBarKeys> = new Array<SystemBarKeys>('navigation');
281    Log.debug(TAG, `getTopWindow names: ${names} end`);
282    try {
283      topWindow.setSystemBarEnable(names, () => {
284        Log.debug(TAG, 'hideStatusBar Succeeded');
285        topWindow.getAvoidArea(0, async (err, data: window.AvoidArea) => {
286          Log.info(TAG, `Succeeded in obtaining the area. Data: ${JSON.stringify(data)}`);
287          this.onLeftBlankChanged(data);
288          let barColor: string = await UiUtil.getResourceString($r('app.color.transparent'));
289          if (!barColor) {
290            barColor = '#00000000';
291          }
292          topWindow.setSystemBarProperties({ navigationBarColor: barColor }, () => {
293            Log.info(TAG, 'setStatusBarColor done');
294          });
295        });
296      });
297    } catch (err) {
298      Log.error(TAG, `hideStatusBar err: ${err}`);
299    }
300  }
301
302  async setDefaultStatusBarProperties(): Promise<void> {
303    Log.debug(TAG, 'setStatusBarColor start');
304    let topWindow: window.Window = this.getMainWindow();
305    try {
306      topWindow.setSystemBarProperties(
307        { statusBarColor: Constants.STATUS_BAR_BACKGROUND_COLOR,
308          statusBarContentColor: Constants.STATUS_BAR_CONTENT_COLOR }, () => {
309        Log.info(TAG, 'setStatusBarColor done');
310      });
311    } catch (err) {
312      Log.error(TAG, `setStatusBarColor err: ${err}`);
313    }
314  }
315
316  setSystemUi(isShowBar: boolean): void {
317    let deviceTp: string = AppStorage.get('deviceType') as string;
318    Log.debug(TAG, `setSystemUi start, isShowBar=${isShowBar}, deviceType=${deviceTp}`);
319    let topWindow: window.Window = this.getMainWindow();
320    Log.debug(TAG, 'getTopWindow start');
321    let names: Array<SystemBarKeys> = new Array<SystemBarKeys>('status', 'navigation');
322    if (deviceTp === Constants.PC_DEVICE_TYPE || deviceTp === Constants.PAD_DEVICE_TYPE) {
323      names = new Array<SystemBarKeys>('navigation');
324    }
325    if (!isShowBar) {
326      names = [];
327    }
328    Log.debug(TAG, `getTopWindow names: ${names} end`);
329    try {
330      topWindow.setSystemBarEnable(names, () => {
331        Log.debug(TAG, `setSystemUi Succeeded: ${names}`);
332        if (isShowBar) {
333          topWindow.getAvoidArea(0, (err, data: window.AvoidArea) => {
334            Log.info(TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(data));
335            this.onLeftBlankChanged(data);
336          });
337        }
338      })
339    } catch (err) {
340      Log.error(TAG, `setSystemUi err: ${err}`);
341    }
342  }
343
344  isHorizontal(): boolean {
345    if (AppStorage.get(Constants.SCREEN_ORIENTATION_HORIZONTAL) == null) {
346      AppStorage.SetOrCreate(Constants.SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
347    }
348    return AppStorage.get(Constants.SCREEN_ORIENTATION_HORIZONTAL);
349  }
350
351  isSidebar(): boolean {
352    if (AppStorage.get(Constants.SCREEN_SIDEBAR) == null) {
353      AppStorage.SetOrCreate(Constants.SCREEN_SIDEBAR, this.sidebar);
354    }
355    return AppStorage.get(Constants.SCREEN_SIDEBAR);
356  }
357
358  getColumnsWidth(count: number): number {
359    let columnWidth: number = (this.winWidth - Constants.COLUMN_MARGIN) / this.columns;
360    columnWidth = parseInt((columnWidth * count - Constants.COLUMN_GUTTER) + '');
361    Log.info(TAG, `getColumnsWidth count is ${count} colunms is ${this.columns}, columnWidth is ${columnWidth} `);
362    return columnWidth;
363  }
364
365  getScreenColumns(): number {
366    return this.columns;
367  }
368
369  onRotationAngleChanged(isH: boolean): void {
370    Log.info(TAG, `onRotationAngleChanged horizontal: ${isH}`);
371    if (isH === null || isH === undefined) {
372      return;
373    }
374    this.horizontal = isH;
375    AppStorage.SetOrCreate(Constants.SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
376  }
377
378  private getMainWindow(): window.Window {
379    return AppStorage.get<window.Window>('mainWindow');
380  }
381
382  private emit(event: string, argument: unknown[]): void {
383    this.broadcast.emit(event, argument);
384  }
385
386  private isLeftBlankInitialized(): boolean {
387    return this.leftBlank[0] != 0 || this.leftBlank[1] != 0 || this.leftBlank[2] != 0 || this.leftBlank[3] != 0;
388  }
389
390  private onLeftBlankChanged(area: window.AvoidArea): void {
391    if (area === null || area === undefined || (area.bottomRect.height === 0 && area.topRect.height === 0)) {
392      return;
393    }
394    let leftBlankBefore = {
395      status: this.statusBarHeight,
396      navi: this.naviBarHeight
397    };
398    // Area data obtained through the system interface,
399    // There is a possibility that the area data is incorrect.
400    AppStorage.SetOrCreate<number>('statusBarHeight', area.topRect.height);
401    this.statusBarHeight = px2vp(area.topRect.height);
402    this.naviBarHeight = px2vp(area.bottomRect.height);
403    this.leftBlank = [this.leftBlank[0], this.leftBlank[1], this.leftBlank[2], px2vp(area.bottomRect.height)];
404    if (leftBlankBefore.status != this.statusBarHeight || leftBlankBefore.navi != this.naviBarHeight) {
405      Log.info(TAG, `leftBlank changed: ${JSON.stringify(leftBlankBefore)}-${JSON.stringify(this.leftBlank)}`);
406      this.emit(ScreenManager.ON_LEFT_BLANK_CHANGED, [this.leftBlank]);
407    }
408  }
409
410  private onWinSizeChanged(size: window.Size | window.Rect): void {
411    Log.info(TAG, `onWinSizeChanged ${JSON.stringify(size)}`);
412    if (size == null || size == undefined) {
413      return;
414    }
415    let isSplitModeBefore: boolean = this.isSplitMode();
416    this.checkWindowMode();
417    let sizeBefore = {
418      width: this.winWidth,
419      height: this.winHeight
420    };
421    this.winWidth = px2vp(size.width);
422    this.winHeight = px2vp(size.height);
423    Log.info(TAG, `onChanged winSize: ${size.width}*${size.height} px, ${this.winWidth}*${this.winHeight} vp`);
424    if (this.winWidth < ScreenWidth.WIDTH_MEDIUM) {
425      this.columns = ColumnSize.COLUMN_FOUR;
426    } else if (this.winWidth >= ScreenWidth.WIDTH_MEDIUM && this.winWidth < ScreenWidth.WIDTH_LARGE) {
427      this.columns = ColumnSize.COLUMN_EIGHT;
428    } else {
429      this.columns = ColumnSize.COLUMN_TWELVE;
430    }
431    let isSplitModeNow: boolean = this.isSplitMode();
432    if (isSplitModeBefore != isSplitModeNow) {
433      Log.info(TAG, `splitMode changed: ${isSplitModeBefore} -> ${isSplitModeNow}`);
434      this.emit(ScreenManager.ON_SPLIT_MODE_CHANGED, [isSplitModeNow]);
435    }
436    if (sizeBefore.width != this.winWidth || sizeBefore.height != this.winHeight) {
437      let newSize = {
438        width: this.winWidth,
439        height: this.winHeight
440      };
441      Log.info(TAG, `winSize changed: ${JSON.stringify(sizeBefore)} -> ${JSON.stringify(newSize)}`);
442      this.emit(ScreenManager.ON_WIN_SIZE_CHANGED, [size]);
443    }
444  }
445}
446