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