• 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    this.mainWindow && this.destroyMainWindow();
186  }
187
188  isSplitMode(): boolean {
189    return (WindowMode.PRIMARY === this.windowMode || WindowMode.SECONDARY === this.windowMode);
190  }
191
192  async checkWindowMode(): Promise<void> {
193    let before = this.windowMode;
194    let mode = await globalThis.photosWindowStage.getWindowMode();
195    Log.info(TAG, `photos application before/current window mode: ${before}/${mode}`);
196
197    if (before == mode) {
198      return;
199    }
200    this.windowMode = mode;
201    if (WindowMode.FULLSCREEN == this.windowMode) {
202      this.setFullScreen();
203    } else {
204      this.setSplitScreen();
205    }
206  }
207
208  setMainWindow(): void {
209    Log.debug(TAG, 'setMainWindow');
210    this.mainWindow.on('windowSizeChange', (data: window.Size) => {
211      Log.debug(TAG, `windowSizeChange ${JSON.stringify(data)}`);
212      try {
213        let properties: window.WindowProperties = this.mainWindow.getWindowProperties();
214        this.isFullScreen = properties.isFullScreen;
215      } catch (exception) {
216        Log.error(TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(exception));
217      }
218      this.onWinSizeChanged(data);
219    })
220    this.mainWindow.getProperties().then((prop: window.WindowProperties) => {
221      Log.info(TAG, `Window prop: ${JSON.stringify(prop)}`);
222      this.onWinSizeChanged(prop.windowRect);
223    });
224  }
225
226  destroyMainWindow(): void {
227    this.mainWindow.off('windowSizeChange');
228  }
229
230  getAvoidArea(): void {
231    let topWindow: window.Window = this.getMainWindow();
232    topWindow.getAvoidArea(0, (err, data: window.AvoidArea) => {
233      Log.info(TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(data));
234      this.onLeftBlankChanged(data);
235    });
236  }
237
238  setFullScreen(): void {
239    let topWindow: window.Window = this.getMainWindow();
240    Log.debug(TAG, 'getTopWindow start');
241    try {
242      topWindow.setLayoutFullScreen(true, () => {
243        Log.debug(TAG, 'setFullScreen true Succeeded');
244        if (AppStorage.Get('deviceType') as string !== Constants.DEFAULT_DEVICE_TYPE) {
245          this.hideStatusBar();
246        } else {
247          this.setWindowBackgroundColorDefault(true);
248        }
249      });
250    } catch (err) {
251      Log.error(TAG, `setFullScreen err: ${err}`);
252    }
253  }
254
255  setWindowBackgroundColorDefault(defaultColor: boolean): void {
256    this.getMainWindow().setWindowBackgroundColor(defaultColor ? '#F1F3F5' : '#000000');
257  }
258
259  setSplitScreen(): void {
260    try {
261      this.statusBarHeight = 0;
262      this.naviBarHeight = 0;
263      this.leftBlank = [0, 0, 0, 0];
264      this.emit(ScreenManager.ON_LEFT_BLANK_CHANGED, [this.leftBlank]);
265    } catch (err) {
266      Log.error(TAG, `setSplitScreen err: ${err}`);
267    }
268  }
269
270  hideStatusBar(): void {
271    Log.debug(TAG, 'hideStatusBar start');
272    let topWindow: window.Window = this.getMainWindow();
273    Log.debug(TAG, 'getTopWindow start');
274    let names: Array<SystemBarKeys> = new Array<SystemBarKeys>('navigation');
275    Log.debug(TAG, `getTopWindow names: ${names} end`);
276    try {
277      topWindow.setSystemBarEnable(names, () => {
278        Log.debug(TAG, 'hideStatusBar Succeeded');
279        topWindow.getAvoidArea(0, async (err, data: window.AvoidArea) => {
280          Log.info(TAG, `Succeeded in obtaining the area. Data: ${JSON.stringify(data)}`);
281          this.onLeftBlankChanged(data);
282          let barColor: string = await UiUtil.getResourceString($r('app.color.transparent'));
283          if (!barColor) {
284            barColor = '#00000000';
285          }
286          topWindow.setSystemBarProperties({ navigationBarColor: barColor }, () => {
287            Log.info(TAG, 'setStatusBarColor done');
288          });
289        });
290      });
291    } catch (err) {
292      Log.error(TAG, `hideStatusBar err: ${err}`);
293    }
294  }
295
296  async setDefaultStatusBarProperties(): Promise<void> {
297    Log.debug(TAG, 'setStatusBarColor start');
298    let topWindow: window.Window = this.getMainWindow();
299    try {
300      topWindow.setSystemBarProperties(
301        { statusBarColor: Constants.STATUS_BAR_BACKGROUND_COLOR,
302          statusBarContentColor: Constants.STATUS_BAR_CONTENT_COLOR }, () => {
303        Log.info(TAG, 'setStatusBarColor done');
304      });
305    } catch (err) {
306      Log.error(TAG, `setStatusBarColor err: ${err}`);
307    }
308  }
309
310  setSystemUi(isShowBar: boolean): void {
311    let deviceTp: string = AppStorage.Get('deviceType') as string;
312    Log.debug(TAG, `setSystemUi start, isShowBar=${isShowBar}, deviceType=${deviceTp}`);
313    let topWindow: window.Window = this.getMainWindow();
314    Log.debug(TAG, 'getTopWindow start');
315    let names: Array<SystemBarKeys> = new Array<SystemBarKeys>('status', 'navigation');
316    if (deviceTp === Constants.PC_DEVICE_TYPE || deviceTp === Constants.PAD_DEVICE_TYPE) {
317      names = new Array<SystemBarKeys>('navigation');
318    }
319    if (!isShowBar) {
320      names = [];
321    }
322    Log.debug(TAG, `getTopWindow names: ${names} end`);
323    try {
324      topWindow.setSystemBarEnable(names, () => {
325        Log.debug(TAG, `setSystemUi Succeeded: ${names}`);
326        if (isShowBar) {
327          topWindow.getAvoidArea(0, (err, data: window.AvoidArea) => {
328            Log.info(TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(data));
329            this.onLeftBlankChanged(data);
330          });
331        }
332      })
333    } catch (err) {
334      Log.error(TAG, `setSystemUi err: ${err}`);
335    }
336  }
337
338  isHorizontal(): boolean {
339    if (AppStorage.Get(Constants.SCREEN_ORIENTATION_HORIZONTAL) == null) {
340      AppStorage.SetOrCreate(Constants.SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
341    }
342    return AppStorage.Get(Constants.SCREEN_ORIENTATION_HORIZONTAL);
343  }
344
345  isSidebar(): boolean {
346    if (AppStorage.Get(Constants.SCREEN_SIDEBAR) == null) {
347      AppStorage.SetOrCreate(Constants.SCREEN_SIDEBAR, this.sidebar);
348    }
349    return AppStorage.Get(Constants.SCREEN_SIDEBAR);
350  }
351
352  getColumnsWidth(count: number): number {
353    let columnWidth: number = (this.winWidth - Constants.COLUMN_MARGIN) / this.columns;
354    columnWidth = parseInt((columnWidth * count - Constants.COLUMN_GUTTER) + '');
355    Log.info(TAG, `getColumnsWidth count is ${count} colunms is ${this.columns}, columnWidth is ${columnWidth} `);
356    return columnWidth;
357  }
358
359  getScreenColumns(): number {
360    return this.columns;
361  }
362
363  onRotationAngleChanged(isH: boolean): void {
364    Log.info(TAG, `onRotationAngleChanged horizontal: ${isH}`);
365    if (isH === null || isH === undefined) {
366      return;
367    }
368    this.horizontal = isH;
369    AppStorage.SetOrCreate(Constants.SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
370  }
371
372  private getMainWindow(): window.Window {
373    return AppStorage.Get<window.Window>('mainWindow');
374  }
375
376  private emit(event: string, argument: unknown[]): void {
377    this.broadcast.emit(event, argument);
378  }
379
380  private isLeftBlankInitialized(): boolean {
381    return this.leftBlank[0] != 0 || this.leftBlank[1] != 0 || this.leftBlank[2] != 0 || this.leftBlank[3] != 0;
382  }
383
384  private onLeftBlankChanged(area: window.AvoidArea): void {
385    if (area === null || area === undefined || (area.bottomRect.height === 0 && area.topRect.height === 0)) {
386      return;
387    }
388    let leftBlankBefore = {
389      status: this.statusBarHeight,
390      navi: this.naviBarHeight
391    };
392    // Area data obtained through the system interface,
393    // There is a possibility that the area data is incorrect.
394    AppStorage.SetOrCreate<number>('statusBarHeight', area.topRect.height);
395    this.statusBarHeight = px2vp(area.topRect.height);
396    this.naviBarHeight = px2vp(area.bottomRect.height);
397    this.leftBlank = [this.leftBlank[0], this.leftBlank[1], this.leftBlank[2], px2vp(area.bottomRect.height)];
398    if (leftBlankBefore.status != this.statusBarHeight || leftBlankBefore.navi != this.naviBarHeight) {
399      Log.info(TAG, `leftBlank changed: ${JSON.stringify(leftBlankBefore)}-${JSON.stringify(this.leftBlank)}`);
400      this.emit(ScreenManager.ON_LEFT_BLANK_CHANGED, [this.leftBlank]);
401    }
402  }
403
404  private onWinSizeChanged(size: window.Size | window.Rect): void {
405    Log.info(TAG, `onWinSizeChanged ${JSON.stringify(size)}`);
406    if (size == null || size == undefined) {
407      return;
408    }
409    let isSplitModeBefore: boolean = this.isSplitMode();
410    this.checkWindowMode();
411    let sizeBefore = {
412      width: this.winWidth,
413      height: this.winHeight
414    };
415    this.winWidth = px2vp(size.width);
416    this.winHeight = px2vp(size.height);
417    Log.info(TAG, `onChanged winSize: ${size.width}*${size.height} px, ${this.winWidth}*${this.winHeight} vp`);
418    if (this.winWidth < ScreenWidth.WIDTH_MEDIUM) {
419      this.columns = ColumnSize.COLUMN_FOUR;
420    } else if (this.winWidth >= ScreenWidth.WIDTH_MEDIUM && this.winWidth < ScreenWidth.WIDTH_LARGE) {
421      this.columns = ColumnSize.COLUMN_EIGHT;
422    } else {
423      this.columns = ColumnSize.COLUMN_TWELVE;
424    }
425    let isSplitModeNow: boolean = this.isSplitMode();
426    if (isSplitModeBefore != isSplitModeNow) {
427      Log.info(TAG, `splitMode changed: ${isSplitModeBefore} -> ${isSplitModeNow}`);
428      this.emit(ScreenManager.ON_SPLIT_MODE_CHANGED, [isSplitModeNow]);
429    }
430    if (sizeBefore.width != this.winWidth || sizeBefore.height != this.winHeight) {
431      let newSize = {
432        width: this.winWidth,
433        height: this.winHeight
434      };
435      Log.info(TAG, `winSize changed: ${JSON.stringify(sizeBefore)} -> ${JSON.stringify(newSize)}`);
436      this.emit(ScreenManager.ON_WIN_SIZE_CHANGED, [size]);
437    }
438  }
439}
440