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