• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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 deviceInfo from '@ohos.deviceInfo';
17import window from '@ohos.window';
18import { stashOrGetObject } from '../utils/SingleInstanceUtils';
19import { Log } from '../utils/Log';
20import { BroadcastConstants } from '../constants/BroadcastConstants';
21import { Constants } from '../constants/Constants';
22import { getResourceString } from '../utils/ResourceUtils';
23import { GlobalContext } from './GlobalContext';
24
25export enum ColumnSize {
26  COLUMN_TWO = 2,
27  COLUMN_FOUR = 4,
28  COLUMN_SIX = 6,
29  COLUMN_EIGHT = 8,
30  COLUMN_TWELVE = 12
31}
32
33enum ScreenWidth {
34  WIDTH_MEDIUM = 520,
35  WIDTH_LARGE = 840
36}
37
38enum WindowMode {
39  UNDEFINED = 1,
40  FULL_SCREEN,
41  PRIMARY,
42  SECONDARY,
43  FLOATING
44}
45
46interface Size {
47  width: number;
48  height: number;
49}
50
51interface StatusNaviHeight {
52  status: number;
53  navi: number;
54}
55
56interface GetWindowMode {
57  getWindowMode(): Promise<WindowMode>;
58}
59
60const TAG = 'ScreenManager';
61
62const APP_KEY_SCREEN_MANAGER = 'app_key_screen_manager';
63
64const SCREEN_ORIENTATION_HORIZONTAL: string = 'isHorizontal';
65const SCREEN_SIDEBAR: string = 'isSidebar';
66
67const COLUMN_MARGIN: number = 12;
68const COLUMN_GUTTER: number = 12;
69
70type CallbackType = Function;
71
72class ScreenManager {
73  readonly ON_WIN_SIZE_CHANGED = 'on_win_size_changed';
74  readonly ON_LEFT_BLANK_CHANGED = 'on_left_blank_changed';
75  readonly DEFAULT_WIDTH: number = 1920;
76  readonly DEFAULT_HEIGHT: number = 1080;
77  readonly SPLIT_THRESHOLD = 1.7;
78  private winWidth = 0.0;
79  private winHeight = 0.0;
80  private statusBarHeight = 0;
81  private naviBarHeight = 0;
82  private leftBlank: number[] = [0, 0, 0, 0];
83  private events: Map<string, CallbackType[]> = new Map<string, CallbackType[]>();
84  private mainWindow: window.Window = undefined;
85  private globalThis = GlobalContext.getContext();
86
87  // Default orientation
88  private horizontal = deviceInfo.deviceType === 'phone' || deviceInfo.deviceType === 'default' ? false : true;
89
90  // Default sidebar
91  private sidebar = deviceInfo.deviceType === 'phone' || deviceInfo.deviceType === 'default' ? false : true;
92  private windowMode = WindowMode.UNDEFINED;
93
94  constructor() {
95    Log.info(TAG, 'constructor');
96  }
97
98  async initializationSize(win): Promise<void> {
99    this.mainWindow = win;
100    this.setMainWindow(win);
101    await this.checkWindowMode();
102    this.getWindowProperties(win);
103  }
104
105  /**
106   * Add Listeners
107   *
108   * @param event
109   * @param fn
110   */
111  on(event, fn): void {
112    if (Array.isArray(event)) {
113      for (let i = 0, l = event.length; i < l; i++) {
114        this.on(event[i], fn);
115      }
116    } else {
117      if (this.events.get(event) === null || this.events.get(event) === undefined) {
118        this.events.set(event, []);
119      }
120      this.events.get(event).push(fn);
121    }
122  }
123
124  /**
125   * Delete Listeners
126   *
127   * @param event
128   * @param fn
129   */
130  off(event, fn): void {
131    if (event == null || event == undefined) {
132      return;
133    }
134    if (Array.isArray(event)) {
135      for (let i = 0, l = event.length; i < l; i++) {
136        this.off(event[i], fn);
137      }
138    }
139    const cbs: CallbackType[] = this.events.get(event);
140    if (!new Boolean(cbs).valueOf()) {
141      return;
142    }
143    if (fn == null || fn == undefined) {
144      return;
145    }
146    let cb;
147    let i = cbs.length;
148    while (i-- > 0) {
149      cb = cbs[i];
150      if (cb === fn || cb.fn === fn) {
151        cbs.splice(i, 1);
152        break;
153      }
154    }
155  }
156
157  private emit(event, argument: Object[]): void {
158    let _self = this;
159    if (!new Boolean(this.events.get(event)).valueOf()) {
160      return;
161    }
162
163    let cbs: CallbackType[] = [];
164    for (let i = 0; i < this.events.get(event).length; i++) {
165      cbs.push(this.events.get(event)[i]);
166    }
167
168    if (cbs.length > 0) {
169      for (let i = 0, l = cbs.length; i < l; i++) {
170        let ref = cbs[i];
171        if (ref != null) {
172          try {
173            ref.apply(_self, argument);
174          } catch (e) {
175            new Error(e);
176          }
177        }
178      }
179    }
180  }
181
182  private isLeftBlankInitialized(): boolean {
183    return this.leftBlank[0] !== 0 || this.leftBlank[1] !== 0 || this.leftBlank[2] !== 0 || this.leftBlank[3] !== 0;
184  }
185
186  // Unit:vp
187  getWinWidth(): number {
188    return px2vp(this.winWidth);
189  }
190
191  setWinWidth(width: number): void {
192    this.winWidth = width;
193  }
194
195  // Unit:vp
196  getWinHeight(): number {
197    return px2vp(this.winHeight);
198  }
199
200  getStatusBarHeight(): number {
201    return px2vp(this.statusBarHeight);
202  }
203
204  getNaviBarHeight(): number {
205    return px2vp(this.naviBarHeight);
206  }
207
208  async initWindowMode(): Promise<void> {
209    Log.debug(TAG, 'start to initialize photos application window mode: ' + this.windowMode);
210  }
211
212  isSplitMode(): boolean {
213    return (WindowMode.PRIMARY === this.windowMode || WindowMode.SECONDARY === this.windowMode)
214  }
215
216  async checkWindowMode(): Promise<void> {
217    let before = this.windowMode;
218    let photosWindowStage = this.globalThis.getObject('photosWindowStage') as GetWindowMode;
219    let mode = await photosWindowStage.getWindowMode();
220    Log.info(TAG, 'photos application before/current window mode: ' + before + '/' + mode);
221
222    if (before === mode) {
223      return;
224    }
225    this.windowMode = mode;
226
227    if (WindowMode.FULL_SCREEN == this.windowMode) {
228      this.setFullScreen();
229    } else {
230      this.setSplitScreen();
231    }
232  }
233
234  private setMainWindow(win: window.Window): void {
235        Log.debug(TAG, 'setMainWindow');
236        win.on('windowSizeChange', (data: window.Size): void => {
237            Log.debug(TAG, 'windowSizeChange ' + JSON.stringify(data));
238            this.checkWindowMode();
239            this.onWinSizeChanged(data);
240        })
241    }
242
243  private getWindowProperties(win: window.Window): void {
244    Log.debug(TAG, 'getWindowProperties');
245    try {
246      let properties = win.getWindowProperties();
247      if (properties.windowRect.width !== 0 && properties.windowRect.height !== 0) {
248        this.winWidth = properties.windowRect.width;
249        this.winHeight = properties.windowRect.height;
250      }
251      Log.debug(TAG, 'this.winWidth = ' + this.winWidth + ' this.winHeight = ' + this.winHeight);
252    } catch (exception) {
253      console.error('Failed to obtain the window properties. Cause: ' + JSON.stringify(exception));
254    }
255  }
256
257  private async setFullScreen(): Promise<void> {
258    let topWindow: window.Window = AppStorage.Get<window.Window>(Constants.MAIN_WINDOW);
259    Log.debug(TAG, 'getTopWindow start');
260    try {
261      await topWindow.setWindowLayoutFullScreen(true);
262      Log.debug(TAG, 'setFullScreen true Succeeded');
263      await this.hideStatusBar(topWindow);
264    } catch (err) {
265      Log.error(TAG, 'setFullScreen err: ' + err);
266    }
267  }
268
269  setSplitScreen(): void {
270    try {
271      this.statusBarHeight = 0;
272      this.naviBarHeight = 0;
273      this.leftBlank = [0, 0, 0, 0];
274      AppStorage.SetOrCreate<number[]>(BroadcastConstants.LEFT_BLANK, this.leftBlank);
275    } catch (err) {
276      Log.error(TAG, 'setSplitScreen err: ' + err);
277    }
278  }
279
280  private async hideStatusBar(topWindow: window.Window): Promise<void> {
281    Log.debug(TAG, 'hideStatusBar start');
282    let names: string[] = ['navigation'];
283    Log.debug(TAG, 'getTopWindow names: ' + names + ' end');
284    try {
285      // @ts-ignore
286      await topWindow.setWindowSystemBarEnable(names);
287      Log.debug(TAG, 'hideStatusBar Succeeded');
288      let data = await topWindow.getWindowAvoidArea(0)
289      Log.debug(TAG, 'Succeeded in obtaining the area. Data: ' + JSON.stringify(data));
290      this.onLeftBlankChanged(data);
291      let barColor: string = await getResourceString($r('app.color.default_background_color'));
292      let barContentColor: string = await getResourceString($r('app.color.default_bar_content_color'));
293      if (!barColor) {
294        barColor = '#FFF1F3F5';
295      }
296      if (!barContentColor) {
297        barContentColor = '#FF000000';
298      }
299      let systemBarProperties: window.SystemBarProperties = {
300        navigationBarColor: barColor,
301        navigationBarContentColor: barContentColor
302      };
303      await topWindow.setWindowSystemBarProperties(systemBarProperties);
304      Log.info(TAG, 'setStatusBarColor done');
305    } catch (err) {
306      Log.error(TAG, 'hideStatusBar err: ' + err);
307    }
308  }
309
310  async setNavigationBarColor(barColor: string, barContentColor: string): Promise<void> {
311    Log.debug(TAG, 'setNavigationBarColor start');
312    let topWindow: window.Window = AppStorage.Get<window.Window>(Constants.MAIN_WINDOW);
313    try {
314      let systemBarProperties: window.SystemBarProperties = {
315        navigationBarColor: barColor,
316        navigationBarContentColor: barContentColor
317      };
318      topWindow.setWindowSystemBarProperties(
319        systemBarProperties,
320        (): void  => Log.info(TAG, 'setStatusBarColor done')
321      );
322      } catch (err) {
323      Log.error(TAG, 'setNavigationBarColor err: ' + err);
324    }
325  }
326
327  setSystemUi(isShowBar: boolean): void {
328    Log.debug(TAG, 'setSystemUi start');
329    let topWindow: window.Window = AppStorage.Get<window.Window>(Constants.MAIN_WINDOW);
330    Log.debug(TAG, 'getTopWindow start');
331    let names: string[] = ['navigation'];
332    if (!isShowBar) {
333      names = [];
334    }
335    Log.debug(TAG, 'getTopWindow names: ' + names + ' end');
336    try {
337            // @ts-ignore
338            topWindow.setWindowSystemBarEnable(names, async (): Promise<void> => {
339                Log.debug(TAG, 'setFullScreen Succeeded');
340                if (isShowBar) {
341                    let data = await topWindow.getWindowAvoidArea(0);
342                    this.onLeftBlankChanged(data);
343                }
344            })
345        } catch (err) {
346      Log.error(TAG, 'setSystemUi err: ' + err);
347    }
348  }
349
350  private onLeftBlankChanged(area: window.AvoidArea): void {
351    if (area == null || area == undefined || area.bottomRect.height === 0) {
352      return;
353    }
354    let leftBlankBefore: StatusNaviHeight = {
355      status: this.statusBarHeight,
356      navi: this.naviBarHeight
357    };
358    this.statusBarHeight = 0;
359    this.naviBarHeight = area.bottomRect.height;
360    this.leftBlank = [this.leftBlank[0], this.leftBlank[1], this.leftBlank[2], area.bottomRect.height];
361    if (leftBlankBefore.status !== this.statusBarHeight || leftBlankBefore.navi !== this.naviBarHeight) {
362      Log.info(TAG, 'leftBlank changed: ' + JSON.stringify(leftBlankBefore) + '-' + JSON.stringify(this.leftBlank))
363      AppStorage.SetOrCreate<number[]>(BroadcastConstants.LEFT_BLANK, this.leftBlank);
364    }
365  }
366
367  private onWinSizeChanged(size): void {
368    Log.info(TAG, 'onWinSizeChanged ' + JSON.stringify(size));
369    if (size == null || size == undefined) {
370      return;
371    }
372    let sizeBefore: Size = {
373      width: this.winWidth,
374      height: this.winHeight
375    };
376    this.winWidth = size.width;
377    this.winHeight = size.height;
378
379
380    if (sizeBefore.width !== this.winWidth || sizeBefore.height !== this.winHeight) {
381      Log.info(TAG, 'winSize changed: ' + JSON.stringify(sizeBefore) + ' -> ' + JSON.stringify(size));
382      this.emit(screenManager.ON_WIN_SIZE_CHANGED, [size]);
383    }
384  }
385
386  private onRotationAngleChanged(angle): void {
387    if (angle == null || angle == undefined) {
388      return;
389    }
390
391    if (angle === 0) {
392      this.horizontal = false;
393    } else {
394      this.horizontal = true;
395    }
396    AppStorage.SetOrCreate<boolean>(SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
397  }
398
399  isHorizontal(): boolean {
400    if (AppStorage.Get<boolean>(SCREEN_ORIENTATION_HORIZONTAL) == null) {
401      AppStorage.SetOrCreate<boolean>(SCREEN_ORIENTATION_HORIZONTAL, this.horizontal);
402    }
403    return AppStorage.Get<boolean>(SCREEN_ORIENTATION_HORIZONTAL);
404  }
405
406  isSidebar(): boolean {
407    if (AppStorage.Get<boolean>(SCREEN_SIDEBAR) == null) {
408      AppStorage.SetOrCreate<boolean>(SCREEN_SIDEBAR, this.sidebar);
409    }
410    return AppStorage.Get<boolean>(SCREEN_SIDEBAR);
411  }
412
413  getColumnsWidth(count: number): number {
414    let columns = this.getScreenColumns();
415    Log.info(TAG, 'getColumnsWidth count is ' + count + ' columns: ' + columns);
416    let columnWidth = (px2vp(this.winWidth) - COLUMN_MARGIN) / columns;
417    return columnWidth * count - COLUMN_GUTTER;
418  }
419
420  getScreenColumns(): number {
421    let width = px2vp(this.winWidth);
422    if (width < ScreenWidth.WIDTH_MEDIUM) {
423      return ColumnSize.COLUMN_FOUR;
424    } else if (width >= ScreenWidth.WIDTH_MEDIUM && width < ScreenWidth.WIDTH_LARGE) {
425      return ColumnSize.COLUMN_EIGHT;
426    } else {
427      return ColumnSize.COLUMN_TWELVE;
428    }
429  }
430
431  setKeepScreenOn(): void {
432    Log.info(TAG, 'setKeepScreenOn start');
433    let topWindow: window.Window = AppStorage.Get<window.Window>('mainWindow');
434    try {
435          topWindow.setWindowKeepScreenOn(true, (): void => Log.info(TAG, 'setKeepScreenOn Succeeded'))
436      } catch (err) {
437      Log.error(TAG, 'setKeepScreenOn err: ' + err);
438    }
439  }
440
441  setKeepScreenOff(): void {
442    Log.info(TAG, 'setKeepScreenOff start');
443    let topWindow: window.Window = AppStorage.Get<window.Window>('mainWindow');
444    try {
445          topWindow.setWindowKeepScreenOn(false, (): void => Log.info(TAG, 'setKeepScreenOff Succeeded'))
446      } catch (err) {
447      Log.error(TAG, 'setKeepScreenOff err: ' + err);
448    }
449  }
450}
451
452export let screenManager: ScreenManager = stashOrGetObject<ScreenManager>(new ScreenManager(), TAG);
453