1/*
2 * Copyright (c) 2021-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 { MILLISECOND } from '../../base/constants/Constant';
17import { formatSuffix, getResourceString } from '../../base/utils/Tools';
18import DateTimeUtil from '../../base/utils/DateTimeUtil';
19import { FileMimeTypeUtil } from '../../base/utils/FileMimeTypeUtil';
20import LanguageUtil from '../../base/utils/LanguageUtil';
21import { MimeType } from './MimeType';
22import { THUMBNAIL_SIZE } from '../../base/constants/UiConstant';
23import { BasicDataSource } from './BasicDataSource';
24import AbilityCommonUtil, { ResultCodePicker } from '../../base/utils/AbilityCommonUtil';
25import { StartModeOptions } from '../../base/model/StartModeOptions';
26import { photoAccessHelper } from '@kit.MediaLibraryKit';
27import { dataSharePredicates } from '@kit.ArkData';
28
29const TAG = 'FileAssetModel';
30
31export class FileAssetLazyModel extends BasicDataSource {
32  private dataArray: FileAssetModel[] = [];
33  public dataCount: number = 0;
34
35  public totalCount(): number {
36    return this.dataArray.length;
37  }
38
39  public getDataArray(): FileAssetModel[] {
40    return this.dataArray;
41  }
42
43  public setData(data: FileAssetModel[]): void {
44    this.dataArray = [...data];
45    this.dataCount = this.dataArray.length;
46    this.notifyDataReload();
47  }
48
49  public getData(index: number): FileAssetModel {
50    return this.dataArray[index];
51  }
52
53  public selectAll(isSelected: boolean): void {
54    this.dataArray.forEach(item => {
55      item.isChecked = isSelected;
56    });
57  }
58
59  public getIndex(uri): number {
60    return this.dataArray.findIndex(item => item.uri === uri);
61  }
62
63  public getSelectedFileList(): FileAssetModel[] {
64    return this.dataArray.filter(item => item.isChecked);
65  }
66
67  public replaceData(index, data: FileAssetModel): void {
68    this.dataArray.splice(index, 1, data);
69    this.notifyDataChange(index);
70  }
71
72  public addData(index: number, data: FileAssetModel): void {
73    this.dataArray.splice(index, 0, data);
74    this.dataCount = this.dataArray.length;
75    this.notifyDataAdd(index);
76  }
77
78  public pushData(data: FileAssetModel): void {
79    this.dataArray.push(data);
80    this.dataCount = this.dataArray.length;
81    this.notifyDataAdd(this.dataArray.length - 1);
82  }
83
84  public deleteData(index: number): void {
85    this.dataArray.splice(index, 1);
86    this.dataCount = this.dataArray.length;
87    this.notifyDataDelete(index);
88  }
89}
90
91/**
92 * 媒体文件信息类
93 */
94export class FileAssetModel {
95  public id: number;
96  public uri: string;
97  public mimeType: string;
98  public mediaType: number;
99  public displayName: string;
100  public title: string;
101  public relativePath: string;
102  public parent: number;
103  public size: number;
104  public dateAdded: number;
105  public dateModified: number;
106  public dateTaken: number;
107  public artist: string;
108  public audioAlbum: string;
109  public width: number;
110  public height: number;
111  public orientation: number;
112  public duration: number;
113  public albumId: number;
114  public albumUri: string;
115  public albumName: string;
116  // MediaLibrary.FileAsset对象外的属性
117  public fileName: string;
118  public fullPath: string;
119  public isChecked: boolean = false;
120  public suffix: string;
121  public icon: Resource | PixelMap;
122  public gridIcon: Resource | PixelMap;
123  public localGridIcon: Resource | PixelMap;
124  public lastModifiedDate: string | Resource;
125  public thumbUri: string;
126  public sortLabel: string = '';
127  public mimeTypeObj: MimeType;
128
129  constructor(file) {
130    this.id = file.id;
131    this.uri = file.uri;
132    this.mimeType = file.mimeType;
133    this.mediaType = file.mediaType;
134    this.displayName = file.displayName;
135    this.title = file.title;
136    this.relativePath = file.relativePath;
137    this.parent = file.parent;
138    this.size = file.size;
139    this.dateAdded = file.dateAdded;
140    this.dateModified = file.dateModified * MILLISECOND.ONE_SECOND;
141    this.dateTaken = file.dateTaken;
142    this.artist = file.artist;
143    this.audioAlbum = file.audioAlbum;
144    this.width = file.width;
145    this.height = file.height;
146    this.orientation = file.orientation;
147    this.duration = file.duration;
148    this.albumId = file.albumId;
149    this.albumUri = file.albumUri;
150    this.albumName = file.albumName;
151
152    this.fileName = file.displayName;
153    this.mimeTypeObj = FileMimeTypeUtil.getFileMimeType(this.fileName);
154    this.fullPath = getFullPath(this);
155    this.suffix = formatSuffix(file.fileName);
156    this.icon = this.mimeTypeObj.getResID();
157    this.gridIcon = this.mimeTypeObj.getGridResID();
158    this.localGridIcon = this.mimeTypeObj.getLocalGridResID();
159    this.lastModifiedDate = DateTimeUtil.getDateStringForCategory(this.dateModified);
160    this.sortLabel = file.sortLabel;
161    if (this.mimeTypeObj.isMedia()) {
162      this.thumbUri = `${this.uri}/thumbnail/${THUMBNAIL_SIZE.WIDTH}/${THUMBNAIL_SIZE.HEIGHT}`;
163    }
164  }
165
166  setFileName(fileName: string): void {
167    this.fileName = fileName;
168    this.mimeTypeObj = FileMimeTypeUtil.getFileMimeType(this.fileName);
169    this.fullPath = getFullPath(this);
170    this.icon = this.mimeTypeObj.getResID();
171    this.gridIcon = this.mimeTypeObj.getGridResID();
172    this.localGridIcon = this.mimeTypeObj.getLocalGridResID();
173    if (this.mimeTypeObj.isMedia()) {
174      this.thumbUri = `${this.uri}/thumbnail/${THUMBNAIL_SIZE.WIDTH}/${THUMBNAIL_SIZE.HEIGHT}`;
175    }
176  }
177
178  pickFile(startModeOptions: StartModeOptions): void {
179    AbilityCommonUtil.terminateFilePicker([this.uri], ResultCodePicker.SUCCESS, startModeOptions);
180  }
181}
182
183/**
184 * 对媒体文件进行排序
185 * @param dataList 待排序数组
186 * @param order 排序规则
187 * @param isDesc 是否倒序
188 * @return 排序后的数组
189 */
190function sortFileAssetList(dataList) {
191  const language = LanguageUtil.getSystemLanguage();
192  return dataList.sort((a, b) => {
193    if (b.dateModified !== a.dateModified) {
194      return b.dateModified - a.dateModified;
195    } else {
196      return b.displayName.localeCompare(a.displayName, language);
197    }
198  })
199}
200
201/**
202 * 媒体库查询条件类
203 */
204export class MediaFetchOptions {
205  public selections: string = photoAccessHelper.PhotoKeys.PHOTO_TYPE + '=?';
206  public selectionArgs: string[] = [];
207  public order: string = photoAccessHelper.PhotoKeys.DATE_MODIFIED + ' DESC';
208  public uri: string = '';
209  public networkId: string = '';
210  public extendArgs: string = '';
211
212  constructor(mediaTypeArg: string = '') {
213    if (!mediaTypeArg) {
214      this.selections = '';
215    } else {
216      this.selectionArgs.push(mediaTypeArg);
217    }
218  }
219
220  /**
221   * 设置要查询文件的uri
222   */
223  setUri(uri: string): void {
224    this.uri = uri;
225  }
226
227  /**
228   * 追加其他查询条件
229   * @param selection 要查询的关键字
230   * @param selectionArg 要查询的值
231   */
232  addSelection(selection: photoAccessHelper.PhotoKeys, selectionArg: string) {
233    if (this.selections.length) {
234      this.selections += ` AND ${selection} = ? `;
235    } else {
236      this.selections = `${selection} = ?`;
237    }
238    this.selectionArgs.push(selectionArg);
239  }
240}
241
242/**
243 * 查询媒体库内指定类型的文件
244 * @param mediaFetchOptions 媒体库查询条件
245 * @return 文件列表
246 */
247export async function getMediaFileDuration(mediaFetchOptions: MediaFetchOptions): Promise<number> {
248  const photoManageHelper: photoAccessHelper.PhotoAccessHelper = AbilityCommonUtil.getPhotoManageHelper();
249  let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
250  let fetchOptions: photoAccessHelper.FetchOptions = {
251    fetchColumns: mediaFetchOptions.selectionArgs,
252    predicates: predicates
253  };
254  if (!photoManageHelper) {
255    return 0;
256  }
257
258  let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
259    await photoManageHelper.getAssets(fetchOptions);
260  let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
261  let duration: photoAccessHelper.MemberType = photoAsset.get(photoAccessHelper.PhotoKeys.DURATION);
262  return Number(duration) ?? 0;
263}
264
265export function getDurationByUri(mediaType: photoAccessHelper.PhotoType, uri: string): Promise<number> {
266  const option = new MediaFetchOptions(mediaType.toString());
267  option.setUri(uri);
268  return getMediaFileDuration(option).then((res: number) => {
269    return res;
270  }).catch(() => {
271    return 0;
272  })
273}
274
275/**
276 * 根据文件名(后缀)判断媒体类型
277 * @param fileName 文件名
278 * @return 媒体类型photoAccessHelper.PhotoType
279 */
280export function getMediaType(fileName: string): photoAccessHelper.PhotoType {
281  const mimeType = FileMimeTypeUtil.getFileMimeType(fileName);
282  if (mimeType.isImage()) {
283    return photoAccessHelper.PhotoType.IMAGE;
284  } else if (mimeType.isVideo()) {
285    return photoAccessHelper.PhotoType.VIDEO;
286  } else {
287    return 0;
288  }
289}
290
291/**
292 * 获取文件的完整路径
293 * @param file 文件信息
294 * @return 完整路径
295 */
296export function getFullPath(file: FileAssetModel): string {
297  return getResourceString($r('app.string.myPhone')) + '/' + file.relativePath + file.fileName;
298}
299
300/**
301 * 设置文件列表排序后需要显示的label
302 * @param fileAssetList 文件列表
303 * @param order 排序规则
304 * @return 设置了label的文件数组
305 */
306export function addSortLabel(fileAssetList): FileAssetModel[] {
307  fileAssetList.forEach((fileAsset: FileAssetModel) => {
308    fileAsset.sortLabel = DateTimeUtil.getDateStringForCategory(fileAsset.dateModified);
309  });
310  return fileAssetList;
311}
312
313
314