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 { ThumbnailSize } 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: string): 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: number, 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: FileAssetModel) { 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 156 let suffix = formatSuffix(this.fileName); 157 if (suffix !== undefined) { 158 this.suffix = suffix; 159 } 160 this.icon = this.mimeTypeObj.getResID(); 161 this.gridIcon = this.mimeTypeObj.getGridResID(); 162 this.localGridIcon = this.mimeTypeObj.getLocalGridResID(); 163 this.lastModifiedDate = DateTimeUtil.getDateStringForCategory(this.dateModified); 164 this.sortLabel = file.sortLabel; 165 if (this.mimeTypeObj.isMedia()) { 166 this.thumbUri = `${this.uri}/thumbnail/${ThumbnailSize.WIDTH}/${ThumbnailSize.HEIGHT}`; 167 } 168 } 169 170 setFileName(fileName: string): void { 171 this.fileName = fileName; 172 this.mimeTypeObj = FileMimeTypeUtil.getFileMimeType(this.fileName); 173 this.fullPath = getFullPath(this); 174 this.icon = this.mimeTypeObj.getResID(); 175 this.gridIcon = this.mimeTypeObj.getGridResID(); 176 this.localGridIcon = this.mimeTypeObj.getLocalGridResID(); 177 if (this.mimeTypeObj.isMedia()) { 178 this.thumbUri = `${this.uri}/thumbnail/${ThumbnailSize.WIDTH}/${ThumbnailSize.HEIGHT}`; 179 } 180 } 181 182 pickFile(startModeOptions: StartModeOptions): void { 183 AbilityCommonUtil.terminateFilePicker([this.uri], ResultCodePicker.SUCCESS, startModeOptions); 184 } 185} 186 187/** 188 * 对媒体文件进行排序 189 * @param dataList 待排序数组 190 * @param order 排序规则 191 * @param isDesc 是否倒序 192 * @return 排序后的数组 193 */ 194function sortFileAssetList(dataList: FileAssetModel[]): FileAssetModel[] { 195 const language = LanguageUtil.getSystemLanguage(); 196 return dataList.sort((a: FileAssetModel, b: FileAssetModel) => { 197 if (b.dateModified !== a.dateModified) { 198 return b.dateModified - a.dateModified; 199 } else { 200 return b.displayName.localeCompare(a.displayName, language); 201 } 202 }) 203} 204 205/** 206 * 媒体库查询条件类 207 */ 208export class MediaFetchOptions { 209 public selections: string = photoAccessHelper.PhotoKeys.PHOTO_TYPE + '=?'; 210 public selectionArgs: string[] = []; 211 public order: string = photoAccessHelper.PhotoKeys.DATE_MODIFIED + ' DESC'; 212 public uri: string = ''; 213 public networkId: string = ''; 214 public extendArgs: string = ''; 215 216 constructor(mediaTypeArg: string = '') { 217 if (!mediaTypeArg) { 218 this.selections = ''; 219 } else { 220 this.selectionArgs.push(mediaTypeArg); 221 } 222 } 223 224 /** 225 * 设置要查询文件的uri 226 */ 227 setUri(uri: string): void { 228 this.uri = uri; 229 } 230 231 /** 232 * 追加其他查询条件 233 * @param selection 要查询的关键字 234 * @param selectionArg 要查询的值 235 */ 236 addSelection(selection: photoAccessHelper.PhotoKeys, selectionArg: string) { 237 if (this.selections.length) { 238 this.selections += ` AND ${selection} = ? `; 239 } else { 240 this.selections = `${selection} = ?`; 241 } 242 this.selectionArgs.push(selectionArg); 243 } 244} 245 246/** 247 * 查询媒体库内指定类型的文件 248 * @param mediaFetchOptions 媒体库查询条件 249 * @return 文件列表 250 */ 251export async function getMediaFileDuration(mediaFetchOptions: MediaFetchOptions): Promise<number> { 252 const photoManageHelper: photoAccessHelper.PhotoAccessHelper = AbilityCommonUtil.getPhotoManageHelper(); 253 let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates(); 254 let fetchOptions: photoAccessHelper.FetchOptions = { 255 fetchColumns: mediaFetchOptions.selectionArgs, 256 predicates: predicates 257 }; 258 if (!photoManageHelper) { 259 return 0; 260 } 261 262 let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = 263 await photoManageHelper.getAssets(fetchOptions); 264 let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject(); 265 let duration: photoAccessHelper.MemberType = photoAsset.get(photoAccessHelper.PhotoKeys.DURATION); 266 return Number(duration) ?? 0; 267} 268 269export function getDurationByUri(mediaType: photoAccessHelper.PhotoType, uri: string): Promise<number> { 270 const option = new MediaFetchOptions(mediaType.toString()); 271 option.setUri(uri); 272 return getMediaFileDuration(option).then((res: number) => { 273 return res; 274 }).catch(() => { 275 return 0; 276 }) 277} 278 279/** 280 * 根据文件名(后缀)判断媒体类型 281 * @param fileName 文件名 282 * @return 媒体类型photoAccessHelper.PhotoType 283 */ 284export function getMediaType(fileName: string): photoAccessHelper.PhotoType { 285 const mimeType = FileMimeTypeUtil.getFileMimeType(fileName); 286 if (mimeType.isImage()) { 287 return photoAccessHelper.PhotoType.IMAGE; 288 } else if (mimeType.isVideo()) { 289 return photoAccessHelper.PhotoType.VIDEO; 290 } else { 291 return 0; 292 } 293} 294 295/** 296 * 获取文件的完整路径 297 * @param file 文件信息 298 * @return 完整路径 299 */ 300export function getFullPath(file: FileAssetModel): string { 301 return getResourceString($r('app.string.myPhone')) + '/' + file.relativePath + file.fileName; 302} 303 304/** 305 * 设置文件列表排序后需要显示的label 306 * @param fileAssetList 文件列表 307 * @param order 排序规则 308 * @return 设置了label的文件数组 309 */ 310export function addSortLabel(fileAssetList: FileAssetModel[]): FileAssetModel[] { 311 fileAssetList.forEach((fileAsset: FileAssetModel) => { 312 fileAsset.sortLabel = DateTimeUtil.getDateStringForCategory(fileAsset.dateModified); 313 }); 314 return fileAssetList; 315} 316 317 318