• 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 CommonEvent from '@ohos.commonEvent';
19import featureAbility from '@ohos.ability.featureAbility';
20import ServiceExtensionContext from 'application/ServiceExtensionContext';
21import { AsyncCallback, BusinessError} from 'basic';
22import { CommonEventData } from 'commonEvent/commonEventData';
23import { CommonEventSubscriber } from 'commonEvent/commonEventSubscriber';
24import { CommonEventSubscribeInfo } from 'commonEvent/commonEventSubscribeInfo';
25
26import commonEventManager from './CommonEventManager'
27import { Log } from '../utils/Log';
28import { StyleConstants } from '../constants/StyleConstants';
29
30const TAG = 'WindowManager';
31
32/**
33 * Wrapper class for window interfaces.
34 */
35class WindowManager {
36  private mDisplayData = null;
37
38  private static subscriber: CommonEventSubscriber;
39
40  private static eventCallback: AsyncCallback<CommonEventData>;
41
42  RECENT_WINDOW_NAME = 'RecentView';
43
44  DESKTOP_WINDOW_NAME = 'EntryView';
45
46  APP_CENTER_WINDOW_NAME = 'AppCenterView';
47
48  FORM_MANAGER_WINDOW_NAME = 'FormManagerView';
49
50  DESKTOP_RANK = Window.WindowType.TYPE_DESKTOP;
51
52  RECENT_RANK = Window.WindowType.TYPE_LAUNCHER_RECENT;
53
54  DOCK_RANK = Window.WindowType.TYPE_LAUNCHER_DOCK;
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  async getWindowWidth() {
76    if (this.mDisplayData == null) {
77      this.mDisplayData = await this.getWindowDisplayData();
78    }
79    return px2vp(this.mDisplayData.width);
80  }
81
82  /**
83   * get window height
84   *
85   * @return windowHeight
86   */
87  async getWindowHeight() {
88    if (this.mDisplayData == null) {
89      this.mDisplayData = await this.getWindowDisplayData();
90    }
91    return px2vp(this.mDisplayData.height);
92  }
93
94  private async getWindowDisplayData() {
95    let displayData = null;
96    await display.getDefaultDisplay()
97      .then((res)=>{
98        displayData = res;
99      }).catch((err)=>{
100        Log.showError(TAG, 'getWindowDisplayData error:' + err);
101      });
102    return displayData;
103  }
104
105  isSplitWindowMode(mode): boolean {
106    if ((mode == featureAbility.AbilityWindowConfiguration.WINDOW_MODE_SPLIT_PRIMARY) ||
107    (mode == featureAbility.AbilityWindowConfiguration.WINDOW_MODE_SPLIT_SECONDARY)) {
108      return true;
109    }
110    return false;
111  }
112
113  /**
114   * set window size
115   *
116   * @param width window width
117   * @param height window height
118   */
119  async setWindowSize(width: number, height: number): Promise<void> {
120    const abilityWindow = await Window.getTopWindow();
121    void abilityWindow.resetSize(width, height);
122  }
123
124  /**
125   * set window position
126   *
127   * @param x coordinate x
128   * @param y coordinate y
129   */
130  async setWindowPosition(x: number, y: number): Promise<void> {
131    const abilityWindow = await Window.getTopWindow();
132    void abilityWindow.moveTo(x, y);
133  }
134
135  createWindow(context: ServiceExtensionContext, name: string, windowType: number, loadContent: string,
136               isShow: boolean, callback?: Function) {
137    Window.create(context, name, windowType).then((win) => {
138      void win.setPreferredOrientation(Window.Orientation.AUTO_ROTATION_RESTRICTED);
139      void win.loadContent(loadContent).then(() => {
140        void win.setSystemBarProperties({
141          navigationBarColor: StyleConstants.DEFAULT_SYSTEM_UI_COLOR,
142          statusBarColor: StyleConstants.DEFAULT_SYSTEM_UI_COLOR
143        }).then(() => {
144          win.setBackgroundColor(StyleConstants.DEFAULT_SYSTEM_UI_COLOR, () => {
145            Log.showDebug(TAG, `then begin ${name} window loadContent in then!`);
146            if (name !== this.RECENT_WINDOW_NAME) {
147              void win.setLayoutFullScreen(true).then(() => {
148                Log.showDebug(TAG, `${name} setLayoutFullScreen`);
149              });
150            }
151            if (callback) {
152              callback(win);
153            }
154            // there is a low probability that white flashes when no delay because of setBackgroundColor is asynchronous
155            setTimeout(() => {
156              isShow && this.showWindow(name);
157            }, StyleConstants.WINDOW_SHOW_DELAY)
158          })
159        });
160      }, (error) => {
161        Log.showError(TAG, `createWindow, create error: ${JSON.stringify(error)}`);
162      });
163    });
164  }
165
166  createWindowIfAbsent(context: ServiceExtensionContext, name: string, windowType: number, loadContent: string): void {
167    Log.showDebug(TAG, `create, name ${name}`);
168    Window.find(name).then(win => {
169      void win.show().then(() => {
170        Log.showDebug(TAG, `show launcher ${name}`);
171      });
172    }).catch(error => {
173      Log.showError(TAG, `${name} ability is not created, because ${error}`);
174      this.createWindow(context, name, windowType, loadContent, true);
175    });
176  }
177
178  resetSizeWindow(name: string, rect: {
179    width: number,
180    height: number
181  }, callback?: Function): void {
182    Log.showDebug(TAG, `resetSizeWindow, name ${name} rect: ${JSON.stringify(rect)}`);
183    this.findWindow(name, (win) => {
184      Log.showDebug(TAG, `resetSizeWindow, findWindow callback name: ${name}`);
185      win.resetSize(rect.width, rect.height).then(() => {
186        Log.showDebug(TAG, `resetSizeWindow, resetSize then name: ${name}`);
187        if (callback) {
188          callback(win);
189        }
190      });
191    });
192  }
193
194  showWindow(name: string, callback?: Function): void {
195    Log.showDebug(TAG, `showWindow, name ${name}`);
196    this.findWindow(name, (win) => {
197      Log.showDebug(TAG, `showWindow, findWindow callback name: ${name}`);
198      win.show().then(() => {
199        Log.showDebug(TAG, `showWindow, show then name: ${name}`);
200        if (callback) {
201          callback(win);
202        }
203      });
204    });
205  }
206
207  hideWindow(name: string, callback?: Function): void {
208    Log.showDebug(TAG, `hideWindow, name ${name}`);
209    this.findWindow(name, (win) => {
210      Log.showDebug(TAG, `hideWindow, findWindow callback name: ${name}`);
211      win.hide().then(() => {
212        Log.showDebug(TAG, `hideWindow, hide then name: ${name}`);
213        if (callback) {
214          callback(win);
215        }
216      });
217    });
218  }
219
220  minimizeAllApps(): void {
221    display.getDefaultDisplay().then(dis => {
222      Window.minimizeAll(dis.id).then(() => {
223        Log.showDebug(TAG, 'Launcher minimizeAll');
224      });
225    });
226    this.destroyWindow(this.FORM_MANAGER_WINDOW_NAME);
227  }
228
229  destroyWindow(name: string, callback?: Function): void {
230    Log.showDebug(TAG, `destroyWindow, name ${name}`);
231    this.findWindow(name, (win) => {
232      Log.showDebug(TAG, `hideWindow, findWindow callback name: ${name}`);
233      win.destroy().then(() => {
234        Log.showDebug(TAG, `destroyWindow, destroy then name: ${name}`);
235        if (callback) {
236          callback(win);
237        }
238      });
239    });
240  }
241
242  findWindow(name: string, callback?: Function): void {
243    Log.showDebug(TAG, `findWindow, name ${name}`);
244    void Window.find(name)
245      .then((win) => {
246        Log.showDebug(TAG, `findWindow, find then name: ${name}`);
247        if (callback) {
248          callback(win);
249        }
250      });
251  }
252
253  createRecentWindow(mode?: number) {
254    Log.showDebug(TAG, 'createRecentWindow Begin, mode=' + mode);
255    let setWinMode = (mode && this.isSplitWindowMode(mode)) ? (win) => {
256      globalThis.recentMode = mode;
257      win.setWindowMode(mode).then();
258    } : (win) => {
259      globalThis.recentMode = featureAbility.AbilityWindowConfiguration.WINDOW_MODE_FULLSCREEN;
260      win.setFullScreen(true).then(() => {
261        Log.showDebug(TAG, `${this.RECENT_WINDOW_NAME} setFullScreen`);
262      });
263    };
264    let registerWinEvent = (win) => {
265      Log.showDebug(TAG, 'registerWinEvent Begin');
266      win.on('lifeCycleEvent', (stageEventType) => {
267        Log.showDebug(TAG,`Recent lifeCycleEvent callback stageEventType=${stageEventType}`);
268        if (stageEventType == Window.WindowStageEventType.INACTIVE) {
269          Log.showDebug(TAG,'Recent MainAbility onWindowStageInactive');
270          Window.find(windowManager.RECENT_WINDOW_NAME).then((win) => {
271            Log.showDebug(TAG,'Hide recent on inactive');
272            win.hide();
273          })
274        }
275      })
276    };
277    Window.find(windowManager.RECENT_WINDOW_NAME).then(win => {
278      setWinMode(win);
279      void win.show().then(() => {
280        Log.showDebug(TAG, 'show launcher recent ability');
281      });
282    }).catch(error => {
283      Log.showDebug(TAG, `recent window is not created, because ${error}`);
284      let callback = (win) => {
285        Log.showDebug(TAG, 'Post recent window created');
286        registerWinEvent(win);
287        setWinMode(win);
288      }
289      this.createWindow(globalThis.desktopContext, windowManager.RECENT_WINDOW_NAME, windowManager.RECENT_RANK,
290        'pages/' + windowManager.RECENT_WINDOW_NAME, false, callback);
291    });
292  }
293
294  destroyRecentWindow() {
295    this.findWindow(windowManager.RECENT_WINDOW_NAME, win => {
296      win.off('lifeCycleEvent', (win) => {
297        win.destroy().then(() => {
298          Log.showDebug(TAG, 'destroyRecentWindow');
299        });
300      })
301    });
302  }
303
304  private static initSubscriber() {
305    if (WindowManager.subscriber != null) {
306      return;
307    }
308    const subscribeInfo: CommonEventSubscribeInfo = {
309      events: [commonEventManager.RECENT_FULL_SCREEN, commonEventManager.RECENT_SPLIT_SCREEN]
310    };
311    CommonEvent.createSubscriber(subscribeInfo).then((commonEventSubscriber: CommonEventSubscriber) => {
312      Log.showDebug(TAG, "init SPLIT_SCREEN subscriber success");
313      WindowManager.subscriber = commonEventSubscriber;
314    }, (err) => {
315      Log.showError(TAG, `Failed to createSubscriber ${err}`)
316    })
317  }
318
319  /**
320   * Register window event listener.
321   */
322  public registerWindowEvent() {
323    commonEventManager.registerCommonEvent(WindowManager.subscriber, WindowManager.eventCallback);
324  }
325
326  /**
327   * Unregister window event listener.
328   */
329  public unregisterWindowEvent() {
330    commonEventManager.unregisterCommonEvent(WindowManager.subscriber, WindowManager.eventCallback);
331  }
332
333  /**
334   * Window event handler.
335   */
336  private static async winEventCallback(error: BusinessError, data: CommonEventData) {
337    Log.showDebug(TAG,`Launcher WindowManager winEventCallback receive data: ${JSON.stringify(data)}.`);
338    if (error.code != 0) {
339      Log.showError(TAG, `get winEventCallback error: ${JSON.stringify(error)}`);
340      return;
341    }
342
343    switch (data.event) {
344      case commonEventManager.RECENT_FULL_SCREEN:
345        // full screen recent window
346        windowManager.createRecentWindow();
347        break;
348      case commonEventManager.RECENT_SPLIT_SCREEN:
349        // split window mode
350        const windowModeMap = {
351          'Primary': featureAbility.AbilityWindowConfiguration.WINDOW_MODE_SPLIT_PRIMARY,
352          'Secondary': featureAbility.AbilityWindowConfiguration.WINDOW_MODE_SPLIT_SECONDARY
353        };
354        if (data.parameters.windowMode != 'Primary' && data.parameters.windowMode != 'Secondary') {
355          break;
356        }
357        windowManager.createRecentWindow(windowModeMap[data.parameters.windowMode]);
358        globalThis.splitMissionId = data.parameters.missionId;
359        await WindowManager.subscriber.setCode(0)
360        await WindowManager.subscriber.finishCommonEvent();
361        break;
362      default:
363        break;
364    }
365  }
366
367  /**
368   * Screen rotation callback.
369   */
370  public async onPortrait(mediaQueryResult) {
371    if (mediaQueryResult.matches) {
372      Log.showInfo(TAG, 'screen change to landscape');
373      AppStorage.SetOrCreate('isPortrait', false);
374    } else {
375      Log.showInfo(TAG, 'screen change to portrait');
376      AppStorage.SetOrCreate('isPortrait', true);
377    }
378    display.getDefaultDisplay()
379      .then((dis: {
380        id: number,
381        width: number,
382        height: number,
383        refreshRate: number
384      }) => {
385        Log.showInfo(TAG, `change to display: ${JSON.stringify(dis)}`);
386        AppStorage.SetOrCreate('screenWidth', px2vp(dis.width));
387        AppStorage.SetOrCreate('screenHeight', px2vp(dis.height));
388        Log.showDebug(TAG, `screenWidth and screenHeight: ${AppStorage.Get('screenWidth')},${AppStorage.Get('screenHeight')}`);
389      });
390  }
391
392}
393
394export const windowManager = WindowManager.getInstance();