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