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 MediaLibrary from '@ohos.multimedia.mediaLibrary' 17import { MILLISECOND } from '../../base/constants/Constant' 18import { formatSuffix, getResourceString } from '../../base/utils/Tools' 19import DateTimeUtil from '../../base/utils/DateTimeUtil' 20import { FileMimeTypeUtil } from '../../base/utils/FileMimeTypeUtil' 21import LanguageUtil from '../../base/utils/LanguageUtil' 22import ErrorCodeConst from '../../base/constants/ErrorCodeConst' 23import { MimeType } from './MimeType' 24import { THUMBNAIL_SIZE } from '../../base/constants/UiConstant' 25import { BasicDataSource } from './BasicDataSource' 26import Logger from '../../base/log/Logger' 27import AbilityCommonUtil from '../../base/utils/AbilityCommonUtil' 28 29const TAG = 'FileAssetModel' 30 31export class FileAssetLazyModel extends BasicDataSource { 32 private dataArray: FileAssetModel[] = [] 33 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 id: number 96 uri: string 97 mimeType: string 98 mediaType: number 99 displayName: string 100 title: string 101 relativePath: string 102 parent: number 103 size: number 104 dateAdded: number 105 dateModified: number 106 dateTaken: number 107 artist: string 108 audioAlbum: string 109 width: number 110 height: number 111 orientation: number 112 duration: number 113 albumId: number 114 albumUri: string 115 albumName: string 116 // MediaLibrary.FileAsset对象外的属性 117 fileName: string 118 fullPath: string 119 isChecked: boolean = false 120 suffix: string 121 icon: Resource | PixelMap 122 gridIcon: Resource | PixelMap 123 localGridIcon: Resource | PixelMap 124 lastModifiedDate: string | Resource 125 thumbUri: string 126 sortLabel: string = '' 127 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(): void { 179 AbilityCommonUtil.terminateFilePicker([this.uri], [this.fileName]) 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 selections: string = MediaLibrary.FileKey.MEDIA_TYPE + '=?' 206 selectionArgs: Array<string> = [] 207 order: string = MediaLibrary.FileKey.DATE_MODIFIED + " DESC" 208 uri: string 209 networkId: string 210 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: MediaLibrary.FileKey, 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 function getMediaFileAssets(mediaFetchOptions: MediaFetchOptions): Promise<FileAssetModel[]> { 248 const mediaLibrary = AbilityCommonUtil.getMediaLibrary() 249 if (!mediaLibrary) { 250 return Promise.resolve([]) 251 } 252 return mediaLibrary.getFileAssets(mediaFetchOptions).then((fetchFileResult: MediaLibrary.FetchFileResult) => { 253 return fetchFileResult.getAllObject().then((fileAssetList: Array<MediaLibrary.FileAsset>) => { 254 let newFileAssetList = [] 255 fileAssetList.forEach(fileAsset => { 256 newFileAssetList.push(new FileAssetModel(fileAsset)) 257 }) 258 newFileAssetList = addSortLabel(newFileAssetList) 259 newFileAssetList = sortFileAssetList(newFileAssetList) 260 return newFileAssetList 261 }).catch((err) => { 262 // 媒体库查询为空code返回3 263 if (err.code === ErrorCodeConst.FILE_ACCESS.GET_MEDIAFILE_NULL) { 264 Logger.e(TAG, 'no media file') 265 return [] 266 } 267 return Promise.reject(err) 268 }).finally(() => { 269 fetchFileResult.close() 270 }) 271 }).catch((err) => { 272 return Promise.reject(err) 273 }) 274} 275 276/** 277 * 根据文件名(后缀)判断媒体类型 278 * @param fileName 文件名 279 * @return 媒体类型MediaLibrary.MediaType 280 */ 281export function getMediaType(fileName: string): MediaLibrary.MediaType { 282 const mimeType = FileMimeTypeUtil.getFileMimeType(fileName) 283 if (mimeType.isImage()) { 284 return MediaLibrary.MediaType.IMAGE 285 } else if (mimeType.isVideo()) { 286 return MediaLibrary.MediaType.VIDEO 287 } else if (mimeType.isAudio()) { 288 return MediaLibrary.MediaType.AUDIO 289 } else { 290 return MediaLibrary.MediaType.FILE 291 } 292} 293 294export function getDurationByUri(mediaType: MediaLibrary.MediaType, uri: string): Promise<number> { 295 const option = new MediaFetchOptions(mediaType.toString()) 296 option.setUri(uri) 297 return getMediaFileAssets(option).then((res) => { 298 if (res && res.length) { 299 return res[0].duration 300 } else { 301 return 0 302 } 303 }).catch(() => { 304 return 0 305 }) 306} 307 308/** 309 * 获取文件的完整路径 310 * @param file 文件信息 311 * @return 完整路径 312 */ 313export function getFullPath(file: FileAssetModel): string { 314 return getResourceString($r('app.string.myPhone')) + '/' + file.relativePath + file.fileName 315} 316 317/** 318 * 设置文件列表排序后需要显示的label 319 * @param fileAssetList 文件列表 320 * @param order 排序规则 321 * @return 设置了label的文件数组 322 */ 323export function addSortLabel(fileAssetList): FileAssetModel[] { 324 fileAssetList.forEach((fileAsset: FileAssetModel) => { 325 fileAsset.sortLabel = DateTimeUtil.getDateStringForCategory(fileAsset.dateModified) 326 }) 327 return fileAssetList 328} 329 330 331