• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 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 { MathUtil } from './MathUtil';
17import { Constants } from '../model/common/Constants';
18import { Log } from './Log';
19import { ScreenManager } from '../model/common/ScreenManager';
20import prompt from '@system.prompt';
21import type window from '@ohos.window';
22import type { Action } from '../view/browserOperation/Action';
23import { AlbumInfo } from '../model/browser/album/AlbumInfo';
24import common from '@ohos.app.ability.common';
25import resourceManager from '@ohos.resourceManager';
26import { UserFileManagerAccess } from '../access/UserFileManagerAccess';
27import { MediaItem } from '../model/browser/photo/MediaItem';
28
29const TAG: string = 'common_UiUtil';
30
31type Window = window.Window;
32
33export class UiUtil {
34
35  /**
36   * Status bar height
37   */
38  static readonly STATUS_BAR_HEIGHT = 45;
39
40  /**
41   * Navigation bar height
42   */
43  static readonly NAVI_BAR_HEIGHT = 45;
44
45  /**
46   * Hexadecimal Radix
47   */
48  static readonly HEX_BASE = 16;
49
50  /**
51   * Maximum order of color
52   */
53  static readonly MAX_COLOR_ORDER = 255;
54  public static readonly TOAST_DURATION = 3000;
55  /**
56   * 3-bit length hex color
57   */
58  private static readonly HEX_COLOR_LENGTH_THREE = 3;
59  /**
60   * 8-bit length hex color
61   */
62  private static readonly HEX_COLOR_LENGTH_EIGHT = 8;
63  /**
64   * Opacity length of hex color
65   */
66  private static readonly HEX_COLOR_OPACITY_LENGTH = 2;
67  /**
68   * Hexadecimal array
69   */
70  private static readonly HEX_ARRAY = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
71
72  /**
73   * Set status bar color
74   *
75   * @param statusBarColor statusBarColor
76   */
77  static setStatusBarColor(topWindow: Window): void {
78    topWindow.setSystemBarProperties({ navigationBarColor: '#FFF1F3F5', navigationBarContentColor: '#FF000000' },
79      () => {
80        Log.info(TAG, 'setStatusBarColor done');
81      });
82  }
83
84  /**
85   * Gets the hexadecimal color with opacity
86   *
87   * @param color Original hex color
88   * @param opacity Opacity
89   * @returns Hex color with opacity
90   */
91  static getHexOpacityColor(paramColor: string, paramOpacity: number): string {
92    let colorReg = /^\#([0-9a-fA-f]{3}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/;
93    // The input must be # the first 3-bit / 6-bit / 8-bit hex color
94    if (paramColor.match(colorReg) == null) {
95      Log.error(TAG, `getHexOpacityColor failed because of invalid input, color:  ${paramColor}`);
96      return paramColor;
97    }
98    let color = paramColor.replace(/\#/g, '').toUpperCase();
99    let opacity = MathUtil.clamp(0, 1, paramOpacity);
100    // If it is an abbreviated 3-digit color, complete the 3-digit characters to 6-digit characters
101    if (color.length === UiUtil.HEX_COLOR_LENGTH_THREE) {
102      let arr = color.split('');
103      color = '';
104      for (let i = 0; i < arr.length; i++) {
105        color += (arr[i] + arr[i]);
106      }
107    }
108    // If it is an 8-bit color, the original opacity will be removed
109    if (color.length === UiUtil.HEX_COLOR_LENGTH_EIGHT) {
110      color = color.slice(UiUtil.HEX_COLOR_OPACITY_LENGTH, color.length);
111    }
112    let opacityNum = Math.round(UiUtil.MAX_COLOR_ORDER * opacity); // rounding
113    let opacityStr = '';
114    while (opacityNum > 0) {
115      let mod = opacityNum % UiUtil.HEX_BASE;
116      opacityNum = (opacityNum - mod) / UiUtil.HEX_BASE;
117      opacityStr = UiUtil.HEX_ARRAY[mod] + opacityStr;
118    }
119    if (opacityStr.length == 1) {
120      opacityStr = `0${opacityStr}`;
121    }
122    if (opacityStr.length == 0) {
123      opacityStr = '00';
124    }
125    return `#${opacityStr + color}`;
126  }
127
128  /**
129   * Get the content of the resource reference
130   *
131   * @param resource resource reference
132   * @returns resource Corresponding content string
133   */
134  static async getResourceString(resource: Resource): Promise<string> {
135    try {
136      Log.info(TAG, `getResourceString: ${JSON.stringify(resource)}`);
137      if (AppStorage.get('photosAbilityContext') === null || AppStorage.get('photosAbilityContext') === 'undefined') {
138        Log.error(TAG, 'getResourceString error: context is null');
139        return null;
140      }
141      let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext');
142      let mgr: string = await context.resourceManager.getString(resource.id);
143      if (mgr) {
144        return mgr;
145      } else {
146        Log.error(TAG, `getResourceManager instance is none`);
147        return null;
148      }
149    } catch (error) {
150      Log.error(TAG, `getResourceString error: ${error}`);
151      return null;
152    }
153  }
154
155  private static getResourceManager(src: string = 'photosAbilityContext'): resourceManager.ResourceManager | undefined {
156    const resourceManager: resourceManager.ResourceManager | undefined =
157      AppStorage.get<common.UIAbilityContext>(src)?.resourceManager;
158    if (resourceManager === undefined) {
159      Log.error(TAG, JSON.stringify({ context: AppStorage.get<common.UIAbilityContext>(src), resourceManager }));
160    }
161    return resourceManager;
162  }
163
164  static async getResourceNumber(resource: Resource): Promise<number> {
165    try {
166      Log.info(TAG, `getResourceNumber: ${JSON.stringify(resource)}`);
167      if (AppStorage.get('photosAbilityContext') === null || AppStorage.get('photosAbilityContext') === 'undefined') {
168        Log.error(TAG, 'getResourceNumber error: context is null');
169        return null;
170      }
171      let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext');
172      let mgr: number = context.resourceManager.getNumber(resource.id);
173      if (mgr) {
174        return mgr;
175      } else {
176        Log.error(TAG, `getResourceNumber instance is none`);
177        return null;
178      }
179    } catch (error) {
180      Log.error(TAG, `getResourceNumber error: ${error}`);
181      return null;
182    }
183  }
184
185  /**
186   * 用于检查该文件是否可以编辑,用于控制编辑菜单是否灰化
187   *
188   * @param mediaItem 被检测的媒体
189   *
190   * @returns true 可以编辑,false 不可以编辑
191   */
192  static isEditedEnable(mediaItem: MediaItem): boolean {
193    if (mediaItem.mediaType !== UserFileManagerAccess.MEDIA_TYPE_IMAGE && mediaItem.mediaType !== UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
194      Log.warn(TAG, 'the mediaType is invalid, the type is ' + mediaItem.mediaType);
195      return false;
196    }
197    if (mediaItem.size === undefined || mediaItem.size === 0) {
198      Log.warn(TAG, 'the size is invalid, the size is ' + mediaItem.size);
199      return false;
200    }
201    if (mediaItem.width === undefined || mediaItem.width === 0) {
202      Log.warn(TAG, 'the width is invalid, the width is ' + mediaItem.width);
203      return false;
204    }
205    if (mediaItem.height === undefined || mediaItem.height === 0) {
206      Log.warn(TAG, 'the height is invalid, the height is ' + mediaItem.height);
207      return false;
208    }
209    if (mediaItem.mediaType === UserFileManagerAccess.MEDIA_TYPE_VIDEO && (mediaItem.duration === undefined || mediaItem.duration === 0)) {
210      Log.warn(TAG, 'the duration is invalid, the duration is ' + mediaItem.duration);
211      return false;
212    }
213    return true;
214  }
215
216  /**
217   * Get the content of the resource reference
218   *
219   * @param resource resource reference
220   * @returns resource Corresponding content number
221   */
222  static getResourceNumberSync(resource: Resource): number {
223    Log.info(TAG, `getResourceNumber: ${JSON.stringify(resource)}`);
224    return this.getResourceManager()?.getNumber(resource.id) ?? Number.NaN;
225  }
226
227  /**
228   * Get the content of the resource reference
229   *
230   * @param resource resource reference
231   * @returns resource Corresponding content Color
232   */
233  static getResourceColorSync(resource: Resource): number {
234    try {
235      Log.info(TAG, `getResourceString: ${JSON.stringify(resource)}`);
236      if (globalThis.photosAbilityContext == null || globalThis.photosAbilityContext === 'undefined') {
237        Log.error(TAG, 'getResourceString error: context is null');
238        return null;
239      }
240      let mgr = globalThis.photosAbilityContext.resourceManager.getColorSync(resource.id);
241      if (mgr) {
242        return mgr;
243      } else {
244        Log.error(TAG, `getResourceManager instance is none`);
245        return null;
246      }
247    } catch (error) {
248      Log.error(TAG, `getResourceString error: ${error}`);
249      return null;
250    }
251  }
252
253  static async showToast(resource: Resource): Promise<void> {
254    let message = await UiUtil.getResourceString(resource);
255    Log.debug(TAG, `showToast: ${message}`);
256    prompt.showToast({
257      message: message,
258      duration: UiUtil.TOAST_DURATION,
259      bottom: '64vp'
260    });
261  }
262
263  /**
264   * Get the content of the resource reference
265   *
266   * @param resource resource reference
267   * @returns resource Corresponding content string
268   */
269  static getResourceStringSync(resource: Resource): string {
270    Log.info(TAG, `getResourceString: ${JSON.stringify(resource)}`);
271    if (resource.moduleName === 'browserlibrary') {
272      return this.getResourceManager('photoLibraryContext')?.getStringSync(resource.id) ?? '';
273    }
274    return this.getResourceManager()?.getStringSync(resource.id) ?? '';
275  }
276
277  /**
278   * 获取相册封面宫格布局列的个数
279   *
280   * @param isSidebar 是否包含侧边栏
281   */
282  static getAlbumGridCount(isSidebar: boolean): number {
283    Log.info(TAG, `get screen width is : ${ScreenManager.getInstance().getWinWidth()}`);
284    let sideBarWidth = isSidebar ? Constants.TAB_BAR_WIDTH : 0;
285    let contentWidth = ScreenManager.getInstance().getWinWidth() - sideBarWidth;
286    let maxCardWidth = Constants.ALBUM_SET_COVER_SIZE * Constants.GRID_MAX_SIZE_RATIO;
287    let gridColumnsCount = Math.max(Constants.DEFAULT_ALBUM_GRID_COLUMN_MIN_COUNT,
288      Math.ceil((contentWidth - Constants.ALBUM_SET_MARGIN * Constants.NUMBER_2 + Constants.ALBUM_SET_GUTTER) /
289        (maxCardWidth + Constants.ALBUM_SET_GUTTER)));
290    Log.info(TAG, `[getAlbumGridCount] contentWidth: ${contentWidth}, gridColumnsCount: ${gridColumnsCount}`);
291    return gridColumnsCount;
292  }
293
294  static resetGeometryTransitionParams(): void {
295    AppStorage.setOrCreate<number>('geometryScale', 1);
296    AppStorage.setOrCreate<number>('geometryOffsetX', 0);
297    AppStorage.setOrCreate<number>('geometryOffsetY', 0);
298    AppStorage.setOrCreate<number>('geometryOpacity', 1);
299  }
300
301  static updateGeometryTapInfo(geometryTapIndex: number, geometryTransitionString: string): void {
302    AppStorage.setOrCreate<number>('placeholderIndex', geometryTapIndex);
303    AppStorage.setOrCreate<string>('geometryTransitionBrowserId',
304      geometryTransitionString);
305    Log.debug(TAG, 'this.geometryTransitionId = ' + geometryTransitionString +
306      ', placeholderIndex = ' + geometryTapIndex);
307  }
308
309  static getRouterParams(params: Object): Object {
310    let fakeRouterParam: Object = AppStorage.Get('fakeRouterParam');
311    AppStorage.delete('fakeRouterParam');
312    if (fakeRouterParam) {
313      Log.debug(TAG, 'fakeRouterParam = ' + JSON.stringify(fakeRouterParam));
314    }
315    if (params) {
316      Log.debug(TAG, 'params = ' + JSON.stringify(params));
317    }
318    return params ? params : fakeRouterParam;
319  }
320
321  static isActionArrayEqual(firstList: Array<Action>, secondList: Array<Action>): boolean {
322    if (firstList === secondList) {
323      return true;
324    }
325    if (firstList.length !== secondList.length) {
326      return false;
327    }
328    for (let index = 0; index < firstList.length; index++) {
329      if (!firstList[index].equals(secondList[index])) {
330        return false;
331      }
332    }
333    return true;
334  }
335
336  static getDisplayNameResourceByAlbumInfo(albumInfo: AlbumInfo): Resource {
337    let res: Resource = null;
338    if (albumInfo.isPhotoAlbum) {
339      return $r('app.string.album_photos');
340    } else if (albumInfo.isFavorAlbum) {
341      return $r('app.string.album_favor');
342    } else if (albumInfo.isVideoAlbum) {
343      return $r('app.string.album_video');
344    } else if (albumInfo.isTrashAlbum) {
345      return $r('app.string.album_recycle');
346    } else if (albumInfo.isScreenShotAlbum) {
347      return $r('app.string.album_screen_shot');
348    } else {
349      return res;
350    }
351  }
352}