• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2021-2022 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 window from '@ohos.window';
17import display from '@ohos.display';
18import commonEventMgr from '@ohos.commonEventManager';
19import common from '@ohos.app.ability.common';
20import { AsyncCallback, BusinessError } from '@ohos.base';
21import AbilityConstant from '@ohos.app.ability.AbilityConstant';
22import commonEventManager from './CommonEventManager'
23import { Log } from '../utils/Log';
24import { StyleConstants } from '../constants/StyleConstants';
25
26const TAG = 'WindowManager';
27
28/**
29 * Wrapper class for window interfaces.
30 */
31class WindowManager {
32  private mDisplayData: display.Display | null = null;
33
34  private static subscriber: commonEventMgr.CommonEventSubscriber;
35
36  private static eventCallback: AsyncCallback<commonEventMgr.CommonEventData>;
37
38  RECENT_WINDOW_NAME = 'RecentView';
39
40  DESKTOP_WINDOW_NAME = 'EntryView';
41
42  APP_CENTER_WINDOW_NAME = 'AppCenterView';
43
44  FORM_MANAGER_WINDOW_NAME = 'FormManagerView';
45
46  FORM_SERVICE_WINDOW_NAME = 'FormServiceView';
47
48  DESKTOP_RANK = window.WindowType.TYPE_DESKTOP;
49
50  RECENT_RANK = window.WindowType.TYPE_LAUNCHER_RECENT;
51
52  DOCK_RANK = window.WindowType.TYPE_LAUNCHER_DOCK;
53
54  recentMode?: number;
55
56  /**
57   * get WindowManager instance
58   *
59   * @return WindowManager singleton
60   */
61  static getInstance(): WindowManager {
62    if (globalThis.WindowManager == null) {
63      globalThis.WindowManager = new WindowManager();
64      this.eventCallback = this.winEventCallback.bind(this);
65      this.initSubscriber();
66    }
67    return globalThis.WindowManager;
68  }
69
70  /**
71   * get window width
72   *
73   * @return windowWidth
74   */
75  getWindowWidth(): number {
76    if (this.mDisplayData == null) {
77      this.mDisplayData = this.getWindowDisplayData();
78    }
79    return this.mDisplayData?.width as number;
80  }
81
82  /**
83   * get window height
84   *
85   * @return windowHeight
86   */
87  getWindowHeight(): number {
88    if (this.mDisplayData == null) {
89      this.mDisplayData = this.getWindowDisplayData();
90    }
91    return this.mDisplayData?.height as number;
92  }
93
94  private getWindowDisplayData(): display.Display | null {
95    let displayData: display.Display | null = null;
96    try {
97      displayData = display.getDefaultDisplaySync();
98    } catch(err) {
99      Log.showError(TAG, `display.getDefaultDisplaySync error: ${JSON.stringify(err)}`);
100    }
101    return displayData;
102  }
103
104  isSplitWindowMode(mode): boolean {
105    if ((mode === AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_PRIMARY) ||
106    (mode === AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_SECONDARY)) {
107      return true;
108    }
109    return false;
110  }
111
112  /**
113   * set window size
114   *
115   * @param width window width
116   * @param height window height
117   */
118  async setWindowSize(width: number, height: number): Promise<void> {
119    const abilityWindow = await window.getLastWindow(globalThis.desktopContext as common.BaseContext);
120    void abilityWindow.resize(width, height);
121  }
122
123  /**
124   * set window position
125   *
126   * @param x coordinate x
127   * @param y coordinate y
128   */
129  async setWindowPosition(x: number, y: number): Promise<void> {
130    const abilityWindow = await window.getLastWindow(globalThis.desktopContext as common.BaseContext);
131    void abilityWindow.moveWindowTo(x, y);
132  }
133
134  /**
135   * 隐藏状态栏
136   *
137   * @param name windowName
138   */
139  hideWindowStatusBar(name: string) {
140    let names: Array<'status'|'navigation'> = ['navigation'];
141    this.setWindowSystemBar(name, names);
142  }
143
144  /**
145   * 显示状态栏
146   *
147   * @param name
148   */
149  showWindowStatusBar(name: string) {
150    let names: Array<'status'|'navigation'> = ['navigation', 'status'];
151    this.setWindowSystemBar(name, names);
152  }
153
154  /**
155   * 设置状态栏与导航栏显隐
156   *
157   * @param windowName
158   * @param names 值为 'status'|'navigation' 枚举
159   */
160  private setWindowSystemBar(windowName: string, names: Array<'status'|'navigation'>) {
161    this.findWindow(windowName, win => {
162      win.setWindowSystemBarEnable(names).then(() => {
163        Log.showInfo(TAG, `set statusBar success`);
164      }).catch(err => {
165        Log.showInfo(TAG, `set statusBar failed, Cause: ${JSON.stringify(err)}`);
166      })
167    })
168  }
169
170  createWindow(context: common.ServiceExtensionContext, name: string, windowType: number, loadContent: string,
171               isShow: boolean, callback?: Function) {
172    let cfg: window.Configuration = {
173      name: name,
174      windowType: windowType,
175      ctx: context
176    };
177    try {
178      window.createWindow(cfg)
179        .then((win: window.Window) => {
180          win.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
181          win.setUIContent(loadContent)
182            .then(() => {
183              win.setWindowSystemBarProperties({
184                navigationBarColor: StyleConstants.DEFAULT_SYSTEM_UI_COLOR,
185                statusBarColor: StyleConstants.DEFAULT_SYSTEM_UI_COLOR
186              }).then(() => {
187                win.setWindowBackgroundColor(StyleConstants.DEFAULT_SYSTEM_UI_COLOR);
188                Log.showDebug(TAG, `then begin ${name} window loadContent in then!`);
189                if (name !== this.RECENT_WINDOW_NAME) {
190                  win.setWindowLayoutFullScreen(true).then(() => {
191                    Log.showDebug(TAG, `${name} setLayoutFullScreen`);
192                  });
193                }
194                if (callback) {
195                  callback(win);
196                }
197                // there is a low probability that white flashes when no delay because of setBackgroundColor is asynchronous
198                setTimeout(() => {
199                  isShow && this.showWindow(name);
200                }, StyleConstants.WINDOW_SHOW_DELAY)
201              });
202            }, (err: BusinessError) => {
203              Log.showError(TAG, `createWindow, setUIContent error: ${JSON.stringify(err)}`);
204            });
205        })
206        .catch((err: BusinessError) => {
207          Log.showError(TAG, `createWindow, createWindow error: ${JSON.stringify(err)}`);
208        })
209    } catch (err) {
210      let _err = err as BusinessError;
211      Log.showError(TAG, `createWindow, error: ${JSON.stringify(_err)}`);
212    }
213  }
214
215  createWindowIfAbsent(context: common.ServiceExtensionContext, name: string, windowType: number, loadContent: string): void {
216    Log.showDebug(TAG, `create, name ${name}`);
217    try {
218      let win: window.Window = window.findWindow(name);
219      win.showWindow().then(() => {
220        Log.showDebug(TAG, `show launcher ${name}`);
221      });
222    } catch (err) {
223      let _err = err as BusinessError;
224      Log.showError(TAG, `${name} ability is not created, because ${_err.message}`);
225      this.createWindow(context, name, windowType, loadContent, true);
226    }
227  }
228
229  resetSizeWindow(name: string, rect: {
230    width: number,
231    height: number
232  }, callback?: Function): void {
233    Log.showDebug(TAG, `resetSizeWindow, name ${name} rect: ${JSON.stringify(rect)}`);
234    this.findWindow(name, (win) => {
235      Log.showDebug(TAG, `resetSizeWindow, findWindow callback name: ${name}`);
236      win.resetSize(rect.width, rect.height).then(() => {
237        Log.showDebug(TAG, `resetSizeWindow, resetSize then name: ${name}`);
238        if (callback) {
239          callback(win);
240        }
241      });
242    });
243  }
244
245  showWindow(name: string, callback?: Function): void {
246    Log.showDebug(TAG, `showWindow, name ${name}`);
247    this.findWindow(name, (win) => {
248      Log.showDebug(TAG, `showWindow, findWindow callback name: ${name}`);
249      win.show().then(() => {
250        Log.showDebug(TAG, `showWindow, show then name: ${name}`);
251        if (callback) {
252          callback(win);
253        }
254      });
255    });
256  }
257
258  hideWindow(name: string, callback?: Function): void {
259    Log.showDebug(TAG, `hideWindow, name ${name}`);
260    this.findWindow(name, (win) => {
261      Log.showDebug(TAG, `hideWindow, findWindow callback name: ${name}`);
262      win.hide().then(() => {
263        Log.showDebug(TAG, `hideWindow, hide then name: ${name}`);
264        if (callback) {
265          callback(win);
266        }
267      });
268    });
269  }
270
271  minimizeAllApps(): void {
272    try {
273      let dis: display.Display = display.getDefaultDisplaySync();
274      window.minimizeAll(dis.id).then(() => {
275        Log.showDebug(TAG, 'Launcher minimizeAll');
276      });
277    } catch (err) {
278      let errCode = (err as BusinessError).code;
279      let errMsg = (err as BusinessError).message;
280      Log.showError(TAG, `minimizeAllApps errCode: ${errCode}, errMsg: ${errMsg}`);
281    }
282    this.destroyWindow(this.FORM_MANAGER_WINDOW_NAME);
283    this.destroyWindow(this.FORM_SERVICE_WINDOW_NAME);
284  }
285
286  destroyWindow(name: string, callback?: Function): void {
287    Log.showDebug(TAG, `destroyWindow, name ${name}`);
288    this.findWindow(name, (win) => {
289      Log.showDebug(TAG, `hideWindow, findWindow callback name: ${name}`);
290      win.destroy().then(() => {
291        Log.showDebug(TAG, `destroyWindow, destroy then name: ${name}`);
292        if (callback) {
293          callback(win);
294        }
295      });
296    });
297  }
298
299  findWindow(name: string, callback?: Function): void {
300    Log.showDebug(TAG, `findWindow, name ${name}`);
301    try {
302      let win: window.Window = window.findWindow(name);
303      Log.showDebug(TAG, `findWindow, find then name: ${name}`);
304      if (callback) {
305        callback(win);
306      }
307    } catch (err) {
308      let _err = err as BusinessError;
309      Log.showError(TAG, `findWindow errCode: ${_err.code}, errMsg: ${_err.message}`);
310    }
311  }
312
313  createRecentWindow(mode?: number) {
314    Log.showDebug(TAG, 'createRecentWindow Begin, mode=' + mode);
315    let setWinMode = (mode && this.isSplitWindowMode(mode)) ? (win) => {
316      windowManager.recentMode = mode;
317      win.setWindowMode(mode).then();
318    } : (win) => {
319      windowManager.recentMode = AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN;
320      win.setFullScreen(true).then(() => {
321        Log.showDebug(TAG, `${this.RECENT_WINDOW_NAME} setFullScreen`);
322      });
323    };
324    let registerWinEvent = (win: window.Window) => {
325      Log.showDebug(TAG, 'registerWinEvent Begin');
326      win.on('windowEvent', (stageEventType) => {
327        Log.showDebug(TAG,`Recent lifeCycleEvent callback stageEventType=${stageEventType}`);
328        if (stageEventType === window.WindowEventType.WINDOW_INACTIVE) {
329
330          Log.showDebug(TAG,'Recent MainAbility onWindowStageInactive');
331          try {
332            let wins: window.Window = window.findWindow(windowManager.RECENT_WINDOW_NAME);
333            Log.showDebug(TAG,'Hide recent on inactive');
334            wins.hide();
335          } catch (err) {
336            let _err = err as BusinessError;
337            Log.showError(TAG, `Recent lifeCycleEvent findWindow errCode: ${_err.code}, errMsg: ${_err.message}`);
338          }
339        }
340      })
341    };
342    try {
343      let win: window.Window = window.findWindow(windowManager.RECENT_WINDOW_NAME);
344      setWinMode(win);
345      win.showWindow()
346        .then(() => {
347          Log.showDebug(TAG, 'show launcher recent ability');
348        });
349    } catch (err) {
350      Log.showDebug(TAG, `recent window is not created, because ${JSON.stringify(err)}`);
351      let callback = (win) => {
352        Log.showDebug(TAG, 'Post recent window created');
353        registerWinEvent(win);
354        setWinMode(win);
355      }
356      this.createWindow(globalThis.desktopContext, windowManager.RECENT_WINDOW_NAME, windowManager.RECENT_RANK,
357        'pages/' + windowManager.RECENT_WINDOW_NAME, false, callback);
358    }
359  }
360
361  destroyRecentWindow() {
362    this.findWindow(windowManager.RECENT_WINDOW_NAME, win => {
363      win.off('windowEvent', (win) => {
364        win.destroy().then(() => {
365          Log.showDebug(TAG, 'destroyRecentWindow');
366        });
367      })
368    });
369  }
370
371  private static initSubscriber() {
372    if (WindowManager.subscriber != null) {
373      return;
374    }
375    const subscribeInfo: commonEventMgr.CommonEventSubscribeInfo = {
376      events: [commonEventManager.RECENT_FULL_SCREEN, commonEventManager.RECENT_SPLIT_SCREEN]
377    };
378    commonEventMgr.createSubscriber(subscribeInfo).then((commonEventSubscriber: commonEventMgr.CommonEventSubscriber) => {
379      Log.showDebug(TAG, "init SPLIT_SCREEN subscriber success");
380      WindowManager.subscriber = commonEventSubscriber;
381    }, (err) => {
382      Log.showError(TAG, `Failed to createSubscriber ${err}`)
383    })
384  }
385
386  /**
387   * Register window event listener.
388   */
389  public registerWindowEvent() {
390    commonEventManager.registerCommonEvent(WindowManager.subscriber, WindowManager.eventCallback);
391  }
392
393  /**
394   * Unregister window event listener.
395   */
396  public unregisterWindowEvent() {
397    commonEventManager.unregisterCommonEvent(WindowManager.subscriber, WindowManager.eventCallback);
398  }
399
400  /**
401   * Window event handler.
402   */
403  private static async winEventCallback(error: BusinessError, data: commonEventMgr.CommonEventData) {
404    Log.showDebug(TAG,`Launcher WindowManager winEventCallback receive data: ${JSON.stringify(data)}.`);
405    if (data.code !== 0) {
406      Log.showError(TAG, `get winEventCallback error: ${JSON.stringify(error)}`);
407      return;
408    }
409
410    switch (data.event) {
411      case commonEventManager.RECENT_FULL_SCREEN:
412        // full screen recent window
413        windowManager.createRecentWindow();
414        break;
415      case commonEventManager.RECENT_SPLIT_SCREEN:
416        // split window mode
417        const windowModeMap = {
418          'Primary': AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_PRIMARY,
419          'Secondary': AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_SECONDARY
420        };
421        if (data.parameters.windowMode !== 'Primary' && data.parameters.windowMode !== 'Secondary') {
422          break;
423        }
424        windowManager.createRecentWindow(windowModeMap[data.parameters.windowMode]);
425        globalThis.splitMissionId = data.parameters.missionId;
426        await WindowManager.subscriber.setCode(0);
427        await WindowManager.subscriber.finishCommonEvent();
428        break;
429      default:
430        break;
431    }
432  }
433
434  /**
435   * Screen rotation callback.
436   */
437  public async onPortrait(mediaQueryResult) {
438    if (mediaQueryResult.matches) {
439      Log.showInfo(TAG, 'screen change to landscape');
440      AppStorage.setOrCreate('isPortrait', false);
441    } else {
442      Log.showInfo(TAG, 'screen change to portrait');
443      AppStorage.setOrCreate('isPortrait', true);
444    }
445    try {
446      let dis: display.Display = display.getDefaultDisplaySync();
447      Log.showInfo(TAG, `change to display: ${JSON.stringify(dis)}`);
448      AppStorage.setOrCreate('screenWidth', px2vp(dis.width));
449      AppStorage.setOrCreate('screenHeight', px2vp(dis.height));
450      Log.showDebug(TAG, `screenWidth and screenHeight: ${AppStorage.get('screenWidth')},${AppStorage.get('screenHeight')}`);
451    } catch (err) {
452      Log.showError(TAG, `onPortrait error: ${JSON.stringify(err)}`);
453    }
454  }
455
456  createWindowWithName = ((windowName: string, windowRank: number): void => {
457    Log.showInfo(TAG, `createWindowWithName begin windowName: ${windowName}`);
458    if (windowName === windowManager.RECENT_WINDOW_NAME) {
459      windowManager.createRecentWindow();
460    } else {
461      windowManager.createWindowIfAbsent(globalThis.desktopContext, windowName, windowRank, 'pages/' + windowName);
462    }
463  })
464
465}
466
467export const windowManager = WindowManager.getInstance();