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