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 */
15
16import { Log } from '../utils/Log';
17import { SettingsModel } from '../model/SettingsModel';
18import { StyleConstants } from '../constants/StyleConstants';
19import { CommonConstants } from '../constants/CommonConstants';
20import { PresetStyleConstants } from '../constants/PresetStyleConstants';
21import { layoutConfigManager } from '../layoutconfig/LayoutConfigManager';
22import { LauncherLayoutStyleConfig } from '../layoutconfig/LauncherLayoutStyleConfig';
23
24const TAG = 'LayoutViewModel';
25
26/**
27 * layout viewmodel
28 */
29export class LayoutViewModel {
30  private mIsPad = true;
31  private mScreenHeight: number | undefined;
32  private mScreenWidth: number | undefined;
33  private mWorkSpaceHeight: number | undefined;
34  private mDockHeight: number | undefined;
35  private mSysUITopHeight: number | undefined;
36  private mSysUIBottomHeight: number | undefined;
37  private mIndicatorHeight: number | undefined;
38  private readonly mLauncherLayoutStyleConfig: LauncherLayoutStyleConfig;
39  private mNavigationBarStatus = false;
40  private mDesktopGap: number | undefined;
41  private mDesktopIconMarginLeft: number | undefined;
42  private mDesktopIconMarginTop: number | undefined;
43  private mDesktopNameHeight: number | undefined;
44  private mDesktopNameLines: number | undefined;
45  private mDesktopIconNameMargin: number | undefined;
46  private mDesktopIconSize: number | undefined;
47  private mDesktopItemSize: number | undefined;
48  private mDesktopNameSize: number | undefined;
49  private mDesktopFolderSize: number | undefined;
50  private mGridRealHeight: number | undefined;
51  private mCommonDialogWidth = '';
52
53  private constructor() {
54    this.mLauncherLayoutStyleConfig = layoutConfigManager.getStyleConfig(
55      LauncherLayoutStyleConfig.LAUNCHER_COMMON_STYLE_CONFIG, LauncherLayoutStyleConfig.LAUNCHER_PRODUCT_STYLE_CONFIG);
56  }
57
58  /**
59   * get the LayoutViewModel instance
60   *
61   * @return LayoutViewModel
62   */
63  static getInstance(): LayoutViewModel {
64    if (globalThis.LayoutViewModelInstance == null) {
65      globalThis.LayoutViewModelInstance = new LayoutViewModel();
66      globalThis.LayoutViewModelInstance.initScreen();
67    }
68    return globalThis.LayoutViewModelInstance;
69  }
70
71  /**
72   * init screen info
73   *
74   * @param navigationBarStatus
75   */
76  initScreen(navigationBarStatus?: string): void {
77    this.mScreenWidth = AppStorage.get('screenWidth');
78    this.mScreenHeight = AppStorage.get('screenHeight');
79    Log.showDebug(TAG, `initScreen screenWidth: ${this.mScreenWidth}, screenHeight: ${this.mScreenHeight}`);
80    this.mSysUITopHeight = this.mLauncherLayoutStyleConfig.mSysTopHeight;
81    this.mNavigationBarStatus = navigationBarStatus === '0' ? true : false;
82
83    if (!this.mNavigationBarStatus) {
84      if (this.mScreenWidth > this.mScreenHeight) {
85        this.mSysUIBottomHeight = this.mLauncherLayoutStyleConfig.mSysBottomHeight * this.mScreenWidth / 1280;
86      } else {
87        this.mSysUIBottomHeight = this.mLauncherLayoutStyleConfig.mSysBottomHeight * this.mScreenWidth / 360;
88      }
89    } else {
90      this.mSysUIBottomHeight = 0;
91    }
92    AppStorage.setOrCreate('sysUIBottomHeight', this.mSysUIBottomHeight);
93    this.mIndicatorHeight = this.mLauncherLayoutStyleConfig.mIndicatorHeight;
94    Log.showDebug(TAG, `initScreen SysUIBottomHeight: ${this.mSysUIBottomHeight},
95      IndicatorHeight: ${this.mIndicatorHeight}, SysUITopHeight: ${this.mSysUITopHeight},
96      SysUIBottomHeight: ${this.mSysUIBottomHeight}`);
97    this.mCommonDialogWidth = this.mLauncherLayoutStyleConfig.mCommonDialogWidth;
98  }
99
100  /**
101   * set device type
102   *
103   * @param deviceType: Device type
104   */
105  setDevice(deviceType: string): void {
106    this.mIsPad = deviceType === CommonConstants.PAD_DEVICE_TYPE;
107    AppStorage.setOrCreate('isPad', this.mIsPad);
108  }
109
110  getSysUITopHeight(): number {
111    return this.mSysUITopHeight;
112  }
113
114  getSysUIBottomHeight(): number {
115    return this.mSysUIBottomHeight;
116  }
117
118  /**
119   * get workSpaceHeight
120   */
121  getWorkSpaceHeight(): number {
122    return this.mWorkSpaceHeight;
123  }
124
125  /**
126   * get dockHeight
127   */
128  getDockHeight(): number {
129    return this.mDockHeight;
130  }
131
132  /**
133   * get UninstallDialogWidth
134   */
135  getCommonDialogWidth(): string {
136    return this.mCommonDialogWidth;
137  }
138
139  /**
140   * get indicatorHeight
141   */
142  getIndicator(): number {
143    return this.mIndicatorHeight;
144  }
145
146  /**
147   * calculate dock
148   */
149  calculateDock(): any {
150    Log.showInfo(TAG, 'calculateDock start');
151    let margin = this.mLauncherLayoutStyleConfig.mDockSaveMargin;
152    let dockGap = this.mLauncherLayoutStyleConfig.mDockGutter;
153    let iconSize = this.mLauncherLayoutStyleConfig.mDockIconSize;
154    let listItemGap = this.mLauncherLayoutStyleConfig.mDockItemGap;
155    let dockPadding = this.mLauncherLayoutStyleConfig.mDockPadding;
156    let marginBottom = this.mLauncherLayoutStyleConfig.mDockMarginBottomHideBar;
157    if (!this.mNavigationBarStatus) {
158      marginBottom = this.mLauncherLayoutStyleConfig.mDockMarginBottom;
159    }
160    let maxDockNum = 0;
161    let dockSpaceWidth = this.mScreenWidth - 2 * margin;
162    let maxRecentNum = 3;
163    if (this.mScreenWidth < PresetStyleConstants.DEFAULT_DOCK_RECENT_WIDTH || !this.mIsPad) {
164      maxRecentNum = 0;
165      maxDockNum = ~~((dockSpaceWidth - 2 * dockPadding + listItemGap) / (iconSize + listItemGap));
166    } else {
167      let maxNum = ~~((dockSpaceWidth - dockGap - 4 * dockPadding + 2 * listItemGap) / (iconSize + listItemGap));
168      maxDockNum = maxNum - maxRecentNum;
169    }
170    this.mDockHeight = iconSize + 2 * dockPadding + marginBottom;
171    this.mWorkSpaceHeight = this.mScreenHeight - this.mSysUIBottomHeight - this.mDockHeight;
172    let result = {
173      mDockGap: dockGap,
174      mIconSize: iconSize,
175      mListItemWidth: iconSize,
176      mListItemHeight: iconSize,
177      mListItemGap: listItemGap,
178      mDockPadding: dockPadding,
179      mMaxRecentNum: maxRecentNum,
180      mMaxDockNum: maxDockNum,
181      mDockHeight: iconSize + 2 * dockPadding,
182      mMarginBottom: marginBottom
183    };
184    return result;
185  }
186
187  /**
188   * calculate desktop
189   */
190  calculateDesktop(): any {
191    Log.showInfo(TAG, 'calculateDesktop start');
192    let margin = this.mLauncherLayoutStyleConfig.mMargin;
193    let realWidth = this.mScreenWidth - 2 * margin;
194    let realHeight = this.mWorkSpaceHeight - this.mIndicatorHeight - this.mSysUITopHeight;
195    if (this.mNavigationBarStatus) {
196      realHeight = realHeight - this.mLauncherLayoutStyleConfig.mSysBottomHeight;
197    }
198    let itemSize = this.mLauncherLayoutStyleConfig.mAppItemSize;
199    let minGutter = this.mLauncherLayoutStyleConfig.mGridGutter;
200    let column = ~~((realWidth + minGutter) / (itemSize + minGutter));
201    let userWidth = (realWidth + minGutter - (itemSize + minGutter) * column);
202    let gutter = (userWidth / (column - 1)) + minGutter;
203    let row = ~~((realHeight + gutter) / (itemSize + gutter));
204    let marginTop = ((realHeight + gutter - (itemSize + gutter) * row) / 2);
205    if (!this.mIsPad) {
206        if (row > 6) {
207            row = 6;
208        }
209
210        if (this.mNavigationBarStatus) {
211            realHeight = realHeight + this.mLauncherLayoutStyleConfig.mSysBottomHeight;
212        }
213        let remainHeight = (realHeight + gutter - (itemSize + gutter) * row)
214        realHeight -= remainHeight
215        marginTop = remainHeight / 2 + this.mSysUITopHeight
216    }
217    //set desktop icon
218    let ratio = this.mLauncherLayoutStyleConfig.mIconRatio;
219    let lines = this.mLauncherLayoutStyleConfig.mNameLines;
220    let appTextSize = this.mLauncherLayoutStyleConfig.mNameSize;
221    let nameHeight = this.mLauncherLayoutStyleConfig.mNameHeight;
222    let iconNameMargin = this.mLauncherLayoutStyleConfig.mIconNameGap;
223    let iconMarginVertical = ratio * itemSize;
224    let iconHeight = itemSize - 2 * iconMarginVertical - nameHeight - iconNameMargin;
225    let iconMarginHorizontal = (itemSize - iconHeight) / 2;
226    if (AppStorage.get('isPortrait') ) {
227      row = 11;
228      column = 5;
229    }
230    this.updateGrid(row, column);
231
232    this.mDesktopGap = gutter;
233    this.mDesktopIconMarginLeft = iconMarginHorizontal;
234    this.mDesktopIconMarginTop = iconMarginVertical;
235    this.mDesktopNameLines = lines;
236    this.mDesktopNameHeight = nameHeight;
237    this.mDesktopIconNameMargin = iconNameMargin;
238    this.mDesktopIconSize = iconHeight;
239    this.mDesktopItemSize = itemSize;
240    this.mDesktopNameSize = appTextSize;
241    this.mGridRealHeight = realHeight;
242    //set desktop config
243    let result = {
244      mMargin: margin,
245      mColumnsGap: gutter,
246      mRowsGap: gutter,
247      mColumns: column,
248      mRows: row,
249      mDesktopMarginTop: marginTop,
250      mGridWidth: realWidth,
251      mGridHeight: realHeight,
252      mAppItemSize: itemSize,
253      mNameSize: appTextSize,
254      mNameHeight: nameHeight,
255      mIconNameMargin: iconNameMargin,
256      mIconSize: iconHeight,
257      mNameLines: lines,
258      mIconMarginHorizontal: iconMarginHorizontal,
259      mIconMarginVertical: iconMarginVertical
260    };
261    return result;
262  }
263
264  /**
265   * calculate desktop folder
266   *
267   * @param layoutInfo folder layoutInfo
268   */
269  calculateFolder(layoutInfo: any): any {
270    Log.showInfo(TAG, 'calculateFolder start');
271    let itemSize = this.mLauncherLayoutStyleConfig.mAppItemSize;
272    let gap = this.mDesktopGap;
273    let iconMargin = this.mDesktopIconMarginLeft;
274    let gridColumn = SettingsModel.getInstance().getGridConfig().row;
275    let folderSize = this.mGridRealHeight / gridColumn + itemSize - iconMargin * 2;
276    let folderGutter = this.mLauncherLayoutStyleConfig.mFolderGutterRatio * folderSize;
277    let folderMargin = this.mLauncherLayoutStyleConfig.mFolderMarginRatio * folderSize;
278
279    let column = layoutInfo?.column;
280    let iconSize = (folderSize - folderGutter * 2 - folderMargin * 2) / column;
281    let nameHeight = this.mDesktopNameHeight;
282    let nameLines = this.mDesktopNameLines;
283    let iconNameMargin = this.mDesktopIconNameMargin;
284
285    this.mDesktopFolderSize = folderSize;
286    let result = {
287      mGridSize: folderSize,
288      mFolderAppSize: iconSize,
289      mFolderGridGap: folderGutter,
290      mGridMargin: folderMargin,
291      mNameHeight: nameHeight,
292      mNameLines: nameLines,
293      mIconNameMargin: iconNameMargin
294    };
295    return result;
296  }
297
298  /**
299   * calculate open folder
300   *
301   * @param openFolderConfig layoutInfo
302   */
303  calculateOpenFolder(openFolderConfig: any): any {
304    Log.showInfo(TAG, 'calculateOpenFolder start');
305    let row = openFolderConfig?.row;
306    let column = openFolderConfig?.column;
307    let gutter = this.mLauncherLayoutStyleConfig.mFolderOpenGutter;
308    let padding = this.mLauncherLayoutStyleConfig.mFolderOpenPADDING;
309    let margin = this.mLauncherLayoutStyleConfig.mFolderOpenMargin;
310    let title = this.mLauncherLayoutStyleConfig.mFolderOpenTitle;
311    let itemSize = this.mDesktopItemSize;
312    let layoutWidth = column * itemSize + (column - 1) * gutter + 2 * padding;
313    let layoutHeight = row * itemSize + (row - 1) * gutter + 2 * padding;
314    let layoutSwiperHeight = row * itemSize + (row - 1) * gutter + 2 * padding + itemSize;
315    let result = {
316      mOpenFolderGridWidth: layoutWidth,
317      mOpenFolderGridHeight: layoutHeight,
318      mOpenFolderSwiperHeight: layoutSwiperHeight,
319      mOpenFolderAddIconSize: this.mDesktopIconSize,
320      mOpenFolderIconSize: this.mDesktopIconSize,
321      mOpenFolderAppSize: this.mDesktopItemSize,
322      mOpenFolderAppNameSize: this.mDesktopNameSize,
323      mOpenFolderAppNameHeight: this.mDesktopNameHeight,
324      mOpenFolderGridRow: row,
325      mOpenFolderGridColumn: column,
326      mOpenFolderGridGap: gutter,
327      mOpenFolderGridPadding: padding,
328      mFolderOpenMargin: margin,
329      mFolderOpenTitle: title,
330      mOpenFolderGridIconTopPadding: this.mDesktopIconMarginTop
331    };
332    return result;
333  }
334
335  /**
336   * calculate add app
337   *
338   * @param addFolderConfig
339   */
340  calculateFolderAddList(addFolderConfig: any): any {
341    Log.showInfo(TAG, 'calculateFolderAddList start');
342    let column: number = addFolderConfig?.column;
343    let margin: number = this.mLauncherLayoutStyleConfig.mFolderAddGridMargin;
344    let saveMargin: number = PresetStyleConstants.DEFAULT_SCREEN_GRID_GAP_AND_MARGIN;
345    let screenGap: number = PresetStyleConstants.DEFAULT_SCREEN_GRID_GAP_AND_MARGIN;
346    let gap: number = this.mLauncherLayoutStyleConfig.mFolderAddGridGap;
347    let maxHeight: number = this.mLauncherLayoutStyleConfig.mFolderAddMaxHeight *
348    (this.mScreenHeight - this.mSysUITopHeight);
349    let toggleSize: number = this.mLauncherLayoutStyleConfig.mFolderToggleSize;
350    let screenColumns: number = PresetStyleConstants.DEFAULT_PHONE_GRID_APP_COLUMNS;
351    let textSize: number = this.mLauncherLayoutStyleConfig.mFolderAddTextSize;
352    let textLines: number = this.mLauncherLayoutStyleConfig.mFolderAddTextLines;
353    let titleSize: number = this.mLauncherLayoutStyleConfig.mFolderAddTitleSize;
354    let linesHeight = textSize * textLines;
355    let buttonSize: number = this.mLauncherLayoutStyleConfig.mFolderAddButtonSize;
356    let ratio: number = this.mLauncherLayoutStyleConfig.mFolderAddIconRatio;
357    if (this.mIsPad) {
358      screenColumns = PresetStyleConstants.DEFAULT_PAD_GRID_APP_COLUMNS;
359    }
360    let columnsWidth = this.mScreenWidth - 2 * saveMargin - (screenColumns - 1) * screenGap;
361    let columnWidth = columnsWidth / screenColumns;
362    let layoutWidth = columnWidth * column + (column - 1) * screenGap;
363    let gridSize = layoutWidth - 2 * margin;
364    let itemSize = (gridSize - (column - 1) * gap) / column;
365    let layoutHeight = layoutWidth + StyleConstants.DEFAULT_APP_ADD_TITLE_SIZE +
366    StyleConstants.DEFAULT_BUTTON_HEIGHT_NUMBER +
367    StyleConstants.DEFAULT_DIALOG_BOTTOM_MARGIN_NUMBER;
368    let iconSize = (1 - 2 * ratio) * itemSize - linesHeight - this.mDesktopIconNameMargin;
369
370    let result = {
371      mAddFolderGridWidth: gridSize,
372      mAddFolderDialogWidth: layoutWidth,
373      mAddFolderDialogHeight: layoutHeight,
374      mAddFolderGridGap: gap,
375      mAddFolderGridMargin: margin,
376      mAddFolderMaxHeight: maxHeight,
377      mFolderToggleSize: toggleSize,
378      mAddFolderTextSize: textSize,
379      mAddFolderTextLines: textLines,
380      mAddFolderLinesHeight: linesHeight,
381      mAddFolderItemSize: itemSize,
382      mAddFolderIconPaddingTop: itemSize * ratio,
383      mAddFolderIconMarginHorizontal: (itemSize - iconSize) / 2,
384      mAddFolderIconSize: iconSize,
385      mAddFolderTitleSize: titleSize,
386      mAddFolderButtonSize: buttonSize
387    };
388    return result;
389  }
390
391  /**
392   * calculate card form
393   */
394  calculateForm(): any {
395    Log.showInfo(TAG, 'calculateForm start');
396    let iconSize = this.mDesktopIconSize;
397    let folderSize = this.mDesktopFolderSize;
398    let itemSize = this.mDesktopItemSize;
399    let gap = this.mDesktopGap;
400    let iconMarginHorizontal = this.mDesktopIconMarginLeft;
401    let iconMarginVertical = this.mDesktopIconMarginTop;
402    let nameHeight = this.mDesktopNameHeight;
403    // 1x2
404    let widthDimension1: number = folderSize;
405    let heightDimension1: number = iconSize;
406    // 2x2
407    let widthDimension2 = folderSize;
408    let heightDimension2 = folderSize;
409    // 2x4
410    let widthDimension3 = (itemSize + gap) * 4 - gap - iconMarginHorizontal * 2;
411    let heightDimension3 = folderSize;
412    // 4x4
413    let widthDimension4 = widthDimension3;
414    let heightDimension4 = (itemSize + gap) * 4 - gap - 2 * iconMarginVertical -
415    nameHeight - this.mDesktopIconNameMargin;
416    // 2x1
417    let widthDimension5 = iconSize;
418    let heightDimension5 = folderSize;
419    // 1x1
420    let widthDimension6 = iconSize;
421    let heightDimension6 = iconSize;
422    // 6x4
423    let widthDimension7 = widthDimension3;
424    let heightDimension7 = (itemSize + gap) * 6 - gap - 2 * iconMarginVertical -
425      nameHeight - this.mDesktopIconNameMargin;
426
427    let result = {
428      widthDimension1: widthDimension1,
429      heightDimension1: heightDimension1,
430      widthDimension2: widthDimension2,
431      heightDimension2: heightDimension2,
432      widthDimension3: widthDimension3,
433      heightDimension3: heightDimension3,
434      widthDimension4: widthDimension4,
435      heightDimension4: heightDimension4,
436      widthDimension5: widthDimension5,
437      heightDimension5: heightDimension5,
438      widthDimension6: widthDimension6,
439      heightDimension6: heightDimension6,
440      widthDimension7: widthDimension7,
441      heightDimension7: heightDimension7,
442      mIconNameMargin: this.mDesktopIconNameMargin
443    };
444    return result;
445  }
446
447  /**
448   * calculate app center
449   */
450  calculateAppCenter(): any {
451    Log.showInfo(TAG, 'calculateAppCenter start');
452    let appCenterMarginLeft: number = this.mLauncherLayoutStyleConfig.mAppCenterMarginLeft;
453    let saveMargin: number = this.mLauncherLayoutStyleConfig.mAppCenterMargin;
454    let gutter: number = this.mLauncherLayoutStyleConfig.mAppCenterGutter;
455    let appItemSize = this.mLauncherLayoutStyleConfig.mAppCenterSize;
456    let width = this.mScreenWidth - 2 * appCenterMarginLeft;
457    let height = this.mWorkSpaceHeight;
458    let column = ~~((width + gutter) / (appItemSize + gutter));
459    let row = ~~((height + gutter) / (appItemSize + gutter));
460    let padding = (height - row * (appItemSize + gutter) + gutter) / 2;
461    let ratio = this.mLauncherLayoutStyleConfig.mAppCenterRatio;
462    let lines = this.mLauncherLayoutStyleConfig.mAppCenterNameLines;
463    let appTextSize = this.mLauncherLayoutStyleConfig.mAppCenterNameSize;
464    let nameHeight = lines * appTextSize;
465    let iconMarginVertical = ratio * appItemSize;
466    let iconHeight = appItemSize - 2 * iconMarginVertical - nameHeight - this.mDesktopIconNameMargin;
467    let result = {
468      mColumnsGap: gutter,
469      mRowsGap: gutter,
470      mColumns: column,
471      mRows: row,
472      mGridWidth: width,
473      mGridHeight: height - 2 * padding,
474      mPadding: padding,
475      mNameSize: appTextSize,
476      mNameHeight: nameHeight,
477      mIconSize: iconHeight,
478      mNameLines: lines,
479      mIconMarginVertical: iconMarginVertical,
480      mAppItemSize: appItemSize,
481      mAppCenterMarginLeft:appCenterMarginLeft
482    };
483    return result;
484  }
485
486  /**
487   * update gridConfig info
488   *
489   * @param {number} row row of grid
490   * @param {number} column column of grid
491   */
492  private updateGrid(row: number, column: number): void {
493    Log.showInfo(TAG, `updateGrid row ${row} column ${column}`);
494    let settingsModel = SettingsModel.getInstance();
495    let gridConfig = settingsModel.getGridConfig();
496    gridConfig.row = row;
497    gridConfig.column = column;
498    const layoutDimension = `${row}X${column}`;
499    gridConfig.layout = layoutDimension;
500    gridConfig.name = layoutDimension;
501  }
502}