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();