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.minimizeAllApps(); 320 windowManager.recentMode = AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN; 321 win.setFullScreen(true).then(() => { 322 Log.showDebug(TAG, `${this.RECENT_WINDOW_NAME} setFullScreen`); 323 }); 324 }; 325 let registerWinEvent = (win: window.Window) => { 326 Log.showDebug(TAG, 'registerWinEvent Begin'); 327 win.on('windowEvent', (stageEventType) => { 328 Log.showDebug(TAG,`Recent lifeCycleEvent callback stageEventType=${stageEventType}`); 329 if (stageEventType === window.WindowEventType.WINDOW_INACTIVE) { 330 331 Log.showDebug(TAG,'Recent MainAbility onWindowStageInactive'); 332 try { 333 let wins: window.Window = window.findWindow(windowManager.RECENT_WINDOW_NAME); 334 Log.showDebug(TAG,'Hide recent on inactive'); 335 wins.hide(); 336 } catch (err) { 337 let _err = err as BusinessError; 338 Log.showError(TAG, `Recent lifeCycleEvent findWindow errCode: ${_err.code}, errMsg: ${_err.message}`); 339 } 340 } 341 }) 342 }; 343 try { 344 let win: window.Window = window.findWindow(windowManager.RECENT_WINDOW_NAME); 345 setWinMode(win); 346 win.showWindow() 347 .then(() => { 348 Log.showDebug(TAG, 'show launcher recent ability'); 349 }); 350 } catch (err) { 351 Log.showDebug(TAG, `recent window is not created, because ${JSON.stringify(err)}`); 352 let callback = (win) => { 353 Log.showDebug(TAG, 'Post recent window created'); 354 registerWinEvent(win); 355 setWinMode(win); 356 } 357 this.createWindow(globalThis.desktopContext, windowManager.RECENT_WINDOW_NAME, windowManager.RECENT_RANK, 358 'pages/' + windowManager.RECENT_WINDOW_NAME, false, callback); 359 } 360 } 361 362 destroyRecentWindow() { 363 this.findWindow(windowManager.RECENT_WINDOW_NAME, win => { 364 win.off('windowEvent', (win) => { 365 win.destroy().then(() => { 366 Log.showDebug(TAG, 'destroyRecentWindow'); 367 }); 368 }) 369 }); 370 } 371 372 private static initSubscriber() { 373 if (WindowManager.subscriber != null) { 374 return; 375 } 376 const subscribeInfo: commonEventMgr.CommonEventSubscribeInfo = { 377 events: [commonEventManager.RECENT_FULL_SCREEN, commonEventManager.RECENT_SPLIT_SCREEN] 378 }; 379 commonEventMgr.createSubscriber(subscribeInfo).then((commonEventSubscriber: commonEventMgr.CommonEventSubscriber) => { 380 Log.showDebug(TAG, "init SPLIT_SCREEN subscriber success"); 381 WindowManager.subscriber = commonEventSubscriber; 382 }, (err) => { 383 Log.showError(TAG, `Failed to createSubscriber ${err}`) 384 }) 385 } 386 387 /** 388 * Register window event listener. 389 */ 390 public registerWindowEvent() { 391 commonEventManager.registerCommonEvent(WindowManager.subscriber, WindowManager.eventCallback); 392 } 393 394 /** 395 * Unregister window event listener. 396 */ 397 public unregisterWindowEvent() { 398 commonEventManager.unregisterCommonEvent(WindowManager.subscriber, WindowManager.eventCallback); 399 } 400 401 /** 402 * Window event handler. 403 */ 404 private static async winEventCallback(error: BusinessError, data: commonEventMgr.CommonEventData) { 405 Log.showDebug(TAG,`Launcher WindowManager winEventCallback receive data: ${JSON.stringify(data)}.`); 406 if (data.code !== 0) { 407 Log.showError(TAG, `get winEventCallback error: ${JSON.stringify(error)}`); 408 return; 409 } 410 411 switch (data.event) { 412 case commonEventManager.RECENT_FULL_SCREEN: 413 // full screen recent window 414 windowManager.createRecentWindow(); 415 break; 416 case commonEventManager.RECENT_SPLIT_SCREEN: 417 // split window mode 418 const windowModeMap = { 419 'Primary': AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_PRIMARY, 420 'Secondary': AbilityConstant.WindowMode.WINDOW_MODE_SPLIT_SECONDARY 421 }; 422 if (data.parameters.windowMode !== 'Primary' && data.parameters.windowMode !== 'Secondary') { 423 break; 424 } 425 windowManager.createRecentWindow(windowModeMap[data.parameters.windowMode]); 426 globalThis.splitMissionId = data.parameters.missionId; 427 await WindowManager.subscriber.setCode(0); 428 await WindowManager.subscriber.finishCommonEvent(); 429 break; 430 default: 431 break; 432 } 433 } 434 435 /** 436 * Screen rotation callback. 437 */ 438 public async onPortrait(mediaQueryResult) { 439 if (mediaQueryResult.matches) { 440 Log.showInfo(TAG, 'screen change to landscape'); 441 AppStorage.setOrCreate('isPortrait', false); 442 } else { 443 Log.showInfo(TAG, 'screen change to portrait'); 444 AppStorage.setOrCreate('isPortrait', true); 445 } 446 try { 447 let dis: display.Display = display.getDefaultDisplaySync(); 448 Log.showInfo(TAG, `change to display: ${JSON.stringify(dis)}`); 449 AppStorage.setOrCreate('screenWidth', px2vp(dis.width)); 450 AppStorage.setOrCreate('screenHeight', px2vp(dis.height)); 451 Log.showDebug(TAG, `screenWidth and screenHeight: ${AppStorage.get('screenWidth')},${AppStorage.get('screenHeight')}`); 452 } catch (err) { 453 Log.showError(TAG, `onPortrait error: ${JSON.stringify(err)}`); 454 } 455 } 456 457 createWindowWithName = ((windowName: string, windowRank: number): void => { 458 Log.showInfo(TAG, `createWindowWithName begin windowName: ${windowName}`); 459 if (windowName === windowManager.RECENT_WINDOW_NAME) { 460 windowManager.createRecentWindow(); 461 } else { 462 windowManager.createWindowIfAbsent(globalThis.desktopContext, windowName, windowRank, 'pages/' + windowName); 463 } 464 }) 465 466} 467 468export const windowManager = WindowManager.getInstance();