1/* 2 * Copyright (c) 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 */ 15import deviceInfo from '@ohos.deviceInfo'; 16import window from '@ohos.window'; 17import createOrGet from '../utils/SingleInstanceUtils'; 18import { Log } from '../utils/Log'; 19import { BroadcastConstants } from '../constants/BroadcastConstants'; 20import { WindowConstants } from '../constants/WindowConstants'; 21import { getResourceString } from '../utils/ResourceUtils'; 22 23export enum ColumnSize { 24 COLUMN_TWO = 2, 25 COLUMN_FOUR = 4, 26 COLUMN_SIX = 6, 27 COLUMN_EIGHT = 8, 28 COLUMN_TWELVE = 12 29} 30 31enum ScreenWidth { 32 WIDTH_MEDIUM = 520, 33 WIDTH_LARGE = 840 34} 35 36enum WindowMode { 37 UNDEFINED = 1, 38 FULL_SCREEN, 39 PRIMARY, 40 SECONDARY, 41 FLOATING 42} 43 44const TAG = "ScreenManager" 45 46const APP_KEY_SCREEN_MANAGER = 'app_key_screen_manager'; 47 48const SCREEN_ORIENTATION_HORIZONTAL: string = 'isHorizontal'; 49const SCREEN_SIDEBAR: string = 'isSidebar'; 50 51const COLUMN_MARGIN: number = 12; 52const COLUMN_GUTTER: number = 12; 53 54class ScreenManager { 55 readonly ON_WIN_SIZE_CHANGED = 'on_win_size_changed'; 56 readonly ON_LEFT_BLANK_CHANGED = 'on_left_blank_changed'; 57 readonly DEFAULT_WIDTH: number = 1920; 58 readonly DEFAULT_HEIGHT: number = 1080; 59 readonly SPLIT_THRESHOLD = 1.7; 60 private winWidth = 0.0; 61 private winHeight = 0.0; 62 private statusBarHeight = 0; 63 private naviBarHeight = 0; 64 private leftBlank: [number, number, number, number] = [0, 0, 0, 0]; 65 private events = []; 66 private mainWindow: window.Window = undefined; 67 68 // Default orientation 69 private horizontal = deviceInfo.deviceType == 'phone' || deviceInfo.deviceType == 'default' ? false : true; 70 71 // Default sidebar 72 private sidebar = deviceInfo.deviceType == 'phone' || deviceInfo.deviceType == 'default' ? false : true; 73 private windowMode = WindowMode.UNDEFINED; 74 75 constructor() { 76 Log.info(TAG, 'constructor'); 77 } 78 79 async initializationSize(win): Promise<void> { 80 this.mainWindow = win; 81 this.setMainWindow(win) 82 await this.checkWindowMode(); 83 this.getWindowProperties(win); 84 } 85 86 /** 87 * Add Listeners 88 * 89 * @param event 90 * @param fn 91 */ 92 on(event, fn) { 93 if (Array.isArray(event)) { 94 for (let i = 0, l = event.length; i < l; i++) { 95 this.on(event[i], fn); 96 } 97 } else { 98 (this.events[event] || (this.events[event] = [])).push(fn); 99 } 100 } 101 102 /** 103 * Delete Listeners 104 * 105 * @param event 106 * @param fn 107 */ 108 off(event, fn) { 109 if (event == null || event == undefined) { 110 return; 111 } 112 if (Array.isArray(event)) { 113 for (let i = 0, l = event.length; i < l; i++) { 114 this.off(event[i], fn); 115 } 116 } 117 const cbs = this.events[event]; 118 if (!cbs) { 119 return; 120 } 121 if (fn == null || fn == undefined) { 122 return; 123 } 124 let cb; 125 let i = cbs.length; 126 while (i--) { 127 cb = cbs[i]; 128 if (cb === fn || cb.fn === fn) { 129 cbs.splice(i, 1); 130 break; 131 } 132 } 133 } 134 135 private emit(event, argument: any[]) { 136 let _self = this; 137 if (!this.events[event]) { 138 return; 139 } 140 let cbs = [...this.events[event]]; 141 if (cbs) { 142 for (let i = 0, l = cbs.length; i < l; i++) { 143 let ref = cbs[i]; 144 if (ref) { 145 try { 146 ref.apply(_self, argument); 147 } catch (e) { 148 new Error(e); 149 } 150 } 151 } 152 } 153 } 154 155 private isLeftBlankInitialized(): boolean { 156 return this.leftBlank[0] != 0 || this.leftBlank[1] != 0 || this.leftBlank[2] != 0 || this.leftBlank[3] != 0; 157 } 158 159 // Unit:vp 160 getWinWidth(): number { 161 return px2vp(this.winWidth); 162 } 163 164 // Unit:vp 165 getWinHeight(): number { 166 return px2vp(this.winHeight); 167 } 168 169 getStatusBarHeight(): number { 170 return px2vp(this.statusBarHeight); 171 } 172 173 getNaviBarHeight(): number { 174 return px2vp(this.naviBarHeight); 175 } 176 177 async initWindowMode() { 178 Log.debug(TAG, `start to initialize photos application window mode: ${this.windowMode}`); 179 } 180 181 isSplitMode(): boolean { 182 return (WindowMode.PRIMARY == this.windowMode || WindowMode.SECONDARY == this.windowMode) 183 } 184 185 async checkWindowMode() { 186 let before = this.windowMode; 187 let mode = await globalThis.photosWindowStage.getWindowMode(); 188 Log.info(TAG, `photos application before/current window mode: ${before}/${mode}`); 189 190 if (before == mode) { 191 return; 192 } 193 this.windowMode = mode; 194 if (WindowMode.FULL_SCREEN == this.windowMode) { 195 await this.setFullScreen(); 196 } else { 197 this.setSplitScreen(); 198 } 199 } 200 201 private setMainWindow(win: window.Window) { 202 Log.debug(TAG, 'setMainWindow'); 203 win.on('windowSizeChange', (data) => { 204 Log.debug(TAG, `windowSizeChange ${JSON.stringify(data)}`); 205 this.checkWindowMode() 206 this.onWinSizeChanged(data); 207 }) 208 } 209 210 private getWindowProperties(win: window.Window) { 211 Log.debug(TAG, 'getWindowProperties'); 212 try { 213 let properties = win.getWindowProperties(); 214 if(properties.windowRect.width !== 0 && properties.windowRect.height !== 0){ 215 this.winWidth = properties.windowRect.width; 216 this.winHeight = properties.windowRect.height; 217 } 218 Log.debug(TAG, `this.winWidth = ${this.winWidth} this.winHeight = ${this.winHeight}`); 219 } catch (exception) { 220 console.error('Failed to obtain the window properties. Cause: ' + JSON.stringify(exception)); 221 } 222 } 223 224 private async setFullScreen() { 225 let topWindow: any = AppStorage.Get(WindowConstants.MAIN_WINDOW); 226 Log.debug(TAG, 'getTopWindow start'); 227 try { 228 await topWindow.setLayoutFullScreen(true) 229 Log.debug(TAG, 'setFullScreen true Succeeded'); 230 await this.hideStatusBar(topWindow); 231 } catch (err) { 232 Log.error(TAG, `setFullScreen err: ${err}`); 233 } 234 } 235 236 setSplitScreen() { 237 try { 238 this.statusBarHeight = 0; 239 this.naviBarHeight = 0; 240 this.leftBlank = [0, 0, 0, 0]; 241 AppStorage.SetOrCreate(BroadcastConstants.LEFT_BLANK, this.leftBlank); 242 } catch (err) { 243 Log.error(TAG, `setSplitScreen err: ${err}`); 244 } 245 } 246 247 private async hideStatusBar(topWindow: any) { 248 Log.debug(TAG, 'hideStatusBar start'); 249 let names = ['navigation']; 250 Log.debug(TAG, `getTopWindow names: ${names} end`); 251 try { 252 await topWindow.setSystemBarEnable(names) 253 Log.debug(TAG, 'hideStatusBar Succeeded'); 254 let data = await topWindow.getAvoidArea(0) 255 Log.debug(TAG, `Succeeded in obtaining the area. Data: ${JSON.stringify(data)}`); 256 this.onLeftBlankChanged(data); 257 let barColor = await getResourceString($r('app.color.default_background_color')); 258 let barContentColor = await getResourceString($r('app.color.default_bar_content_color')); 259 if (!barColor) { 260 barColor = '#00FFFFFF'; 261 } 262 if (!barContentColor) { 263 barContentColor = '#FF000000'; 264 } 265 await topWindow.setSystemBarProperties({ 266 navigationBarColor: barColor, 267 navigationBarContentColor: barContentColor 268 }); 269 Log.info(TAG, 'setStatusBarColor done'); 270 } catch (err) { 271 Log.error(TAG, `hideStatusBar err: ${err}`); 272 } 273 } 274 275 async setNavigationBarColor(barColor: string, barContentColor: string) { 276 Log.debug(TAG, 'setNavigationBarColor start'); 277 let topWindow: any = AppStorage.Get(WindowConstants.MAIN_WINDOW); 278 try { 279 topWindow.setSystemBarProperties( 280 { 281 navigationBarColor: barColor, 282 navigationBarContentColor: barContentColor 283 }, 284 () => Log.info(TAG, 'setStatusBarColor done') 285 ); 286 } catch (err) { 287 Log.error(TAG, `setNavigationBarColor err: ${err}`); 288 } 289 } 290 291 setSystemUi(isShowBar: boolean): void { 292 Log.debug(TAG, 'setSystemUi start'); 293 let topWindow: any = AppStorage.Get(WindowConstants.MAIN_WINDOW); 294 Log.debug(TAG, 'getTopWindow start'); 295 let names = ["navigation"]; 296 if (!isShowBar) { 297 names = []; 298 } 299 Log.debug(TAG, `getTopWindow names: ${names} end`); 300 try { 301 topWindow.setSystemBarEnable(names, () => { 302 Log.debug(TAG, 'setFullScreen Succeeded'); 303 if (isShowBar) { 304 topWindow.getAvoidArea(0, (err, data) => { 305 Log.info(TAG, 'Succeeded in obtaining the area. Data:' + JSON.stringify(data)); 306 this.onLeftBlankChanged(data); 307 }); 308 } 309 }) 310 } catch (err) { 311 Log.error(TAG, `setSystemUi err: ${err}`); 312 } 313 } 314 315 private onLeftBlankChanged(area) { 316 if (area == null || area == undefined || area.bottomRect.height == 0) { 317 return; 318 } 319 let leftBlankBefore = { 320 status: this.statusBarHeight, 321 navi: this.naviBarHeight 322 }; 323 this.statusBarHeight = 0; 324 this.naviBarHeight = area.bottomRect.height; 325 this.leftBlank = [this.leftBlank[0], this.leftBlank[1], this.leftBlank[2], area.bottomRect.height]; 326 if (leftBlankBefore.status != this.statusBarHeight || leftBlankBefore.navi != this.naviBarHeight) { 327 Log.info(TAG, `leftBlank changed: ${JSON.stringify(leftBlankBefore)}-${JSON.stringify(this.leftBlank)}`) 328 AppStorage.SetOrCreate(BroadcastConstants.LEFT_BLANK, this.leftBlank); 329 } 330 } 331 332 private onWinSizeChanged(size) { 333 Log.info(TAG, `onWinSizeChanged ${JSON.stringify(size)}`); 334 if (size == null || size == undefined) { 335 return; 336 } 337 let sizeBefore = { 338 width: this.winWidth, 339 height: this.winHeight 340 }; 341 this.winWidth = size.width; 342 this.winHeight = size.height; 343 344 345 if (sizeBefore.width != this.winWidth || sizeBefore.height != this.winHeight) { 346 Log.info(TAG, `winSize changed: ${JSON.stringify(sizeBefore)} -> ${JSON.stringify(size)}`); 347 this.emit(screenManager.ON_WIN_SIZE_CHANGED, [size]); 348 } 349 } 350 351 private onRotationAngleChanged(angle) { 352 if (angle == null || angle == undefined) { 353 return; 354 } 355 356 if (angle == 0) { 357 this.horizontal = false; 358 } else { 359 this.horizontal = true; 360 } 361 AppStorage.SetOrCreate(SCREEN_ORIENTATION_HORIZONTAL, this.horizontal); 362 } 363 364 isHorizontal(): boolean { 365 if (AppStorage.Get(SCREEN_ORIENTATION_HORIZONTAL) == null) { 366 AppStorage.SetOrCreate(SCREEN_ORIENTATION_HORIZONTAL, this.horizontal); 367 } 368 return AppStorage.Get(SCREEN_ORIENTATION_HORIZONTAL); 369 } 370 371 isSidebar(): boolean { 372 if (AppStorage.Get(SCREEN_SIDEBAR) == null) { 373 AppStorage.SetOrCreate(SCREEN_SIDEBAR, this.sidebar); 374 } 375 return AppStorage.Get(SCREEN_SIDEBAR); 376 } 377 378 getColumnsWidth(count: number): number { 379 let columns = this.getScreenColumns() 380 Log.info(TAG, `getColumnsWidth count is ${count} colunms is ${columns}`); 381 let columnWidth = (px2vp(this.winWidth) - COLUMN_MARGIN) / columns; 382 return columnWidth * count - COLUMN_GUTTER; 383 } 384 385 getScreenColumns(): number { 386 let width = px2vp(this.winWidth) 387 if (width < ScreenWidth.WIDTH_MEDIUM) { 388 return ColumnSize.COLUMN_FOUR; 389 } else if (width >= ScreenWidth.WIDTH_MEDIUM && width < ScreenWidth.WIDTH_LARGE) { 390 return ColumnSize.COLUMN_EIGHT; 391 } else { 392 return ColumnSize.COLUMN_TWELVE; 393 } 394 } 395 396 setKeepScreenOn() { 397 Log.info(TAG, 'setKeepScreenOn start'); 398 let topWindow: any = AppStorage.Get('mainWindow'); 399 try { 400 topWindow.setKeepScreenOn(true, () => Log.info(TAG, 'setKeepScreenOn Succeeded')) 401 } catch (err) { 402 Log.error(TAG, `setKeepScreenOn err: ${err}`); 403 } 404 } 405 406 setKeepScreenOff() { 407 Log.info(TAG, 'setKeepScreenOff start'); 408 let topWindow: any = AppStorage.Get('mainWindow'); 409 try { 410 topWindow.setKeepScreenOn(false, () => Log.info(TAG, 'setKeepScreenOff Succeeded')) 411 } catch (err) { 412 Log.error(TAG, `setKeepScreenOff err: ${err}`); 413 } 414 } 415} 416 417let screenManager = createOrGet(ScreenManager, TAG); 418 419export default screenManager as ScreenManager; 420