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