1/* 2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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 { Log } from '../utils/Log'; 17import { ViewType } from '../models/ViewType'; 18import { userFileModel } from './UserFileModel'; 19import { MediaConstants } from '../constants/MediaConstants'; 20import { selectManager } from '../common/SelectManager'; 21import photoAccessHelper from '@ohos.file.photoAccessHelper'; 22import { screenManager } from '../common/ScreenManager'; 23 24const TAG = 'UserFileDataItem'; 25const STATUS_UNDEFINED = -1; 26const STATUS_FALSE = 0; 27const STATUS_TRUE = 1; 28 29export interface DateAdded { 30 dateAdded: number; 31 viewType: ViewType; 32} 33 34export class UserFileDataItem implements DateAdded { 35 viewType: ViewType = ViewType.ITEM; 36 readonly hashIndex: number; 37 index: number; 38 dateAdded: number = 0; 39 dateModified: number; 40 dateTaken: number; 41 status: number = MediaConstants.UNDEFINED; 42 isSelect: boolean = false; 43 uri: string; 44 orientation: number; 45 duration: number; 46 size: number; 47 width: number; // width changed by orientation 48 height: number; // height changed by orientation 49 imgWidth: number; // may be smaller than width, as width is too large 50 imgHeight: number; // may be smaller than height, as height is too large 51 path: string = ''; 52 title: string; 53 displayName: string; 54 mediaType: photoAccessHelper.PhotoType; 55 favouriteStatus: number = STATUS_UNDEFINED; 56 canRotate: number = STATUS_UNDEFINED; 57 selections: string = ''; 58 selectionArgs: string[] = []; 59 deviceId: string = ''; 60 fileAsset: photoAccessHelper.PhotoAsset = undefined; 61 defaultThumbnail: PixelMap = undefined; 62 thumbnailArray: Map<string, PixelMap> = new Map<string, PixelMap>(); 63 64 constructor(selections: string, selectionArgs: string[], deviceId: string, index: number) { 65 this.selections = selections; 66 this.selectionArgs = selectionArgs; 67 this.deviceId = deviceId; 68 this.hashIndex = index; 69 this.index = index; 70 } 71 72 getHashCode(): string { 73 // 时间线界面角度,收藏状态变更,都需要刷新界面;大图浏览界面角度变更,需要刷新界面 74 return this.status === MediaConstants.UNDEFINED ? 75 '' + this.hashIndex : 76 this.uri + this.favouriteStatus + ' ' + this.orientation + ' ' + this.isSelect; 77 } 78 79 async loadFileAsset(): Promise<photoAccessHelper.PhotoAsset> { 80 return await userFileModel.getMediaItemByUri(this.uri); 81 } 82 83 isLoad(): boolean { 84 if (this.status > MediaConstants.UNDEFINED) { 85 return true; 86 } 87 return false; 88 } 89 90 async load(isForce: boolean): Promise<void> { 91 Log.info(TAG, 'load ' + this.status); 92 if (this.status > (isForce ? MediaConstants.PART_LOADED : MediaConstants.UNDEFINED)) { 93 return; 94 } 95 let fileAsset = await this.loadFileAsset(); 96 if (fileAsset != null) { 97 this.update(fileAsset); 98 } 99 return; 100 } 101 102 async update(fileAsset: photoAccessHelper.PhotoAsset): Promise<void> { 103 this.fileAsset = fileAsset; 104 this.uri = fileAsset.uri; 105 this.displayName = fileAsset.displayName; 106 this.mediaType = fileAsset.photoType; 107 this.width = screenManager.getWinWidth(); 108 this.height = screenManager.getWinHeight(); 109 this.orientation = MediaConstants.ROTATE_NONE; 110 try { 111 this.orientation = fileAsset.get(photoAccessHelper.PhotoKeys.ORIENTATION.toString()) as number; 112 Log.info(TAG, 'orientation ' + this.orientation); 113 } catch (err) { 114 Log.error(TAG, 'get orientation ' + JSON.stringify(err)); 115 } 116 try { 117 this.duration = fileAsset.get(photoAccessHelper.PhotoKeys.DURATION.toString()) as number; 118 Log.info(TAG, 'duration ' + this.duration); 119 } catch (err) { 120 Log.error(TAG, 'get duration ' + JSON.stringify(err)); 121 } 122 try { 123 if (this.orientation === MediaConstants.ROTATE_ONCE || this.orientation === MediaConstants.ROTATE_THIRD) { 124 this.width = fileAsset.get(photoAccessHelper.PhotoKeys.HEIGHT.toString()) as number; 125 this.height = fileAsset.get(photoAccessHelper.PhotoKeys.WIDTH.toString()) as number; 126 } else { 127 this.width = fileAsset.get(photoAccessHelper.PhotoKeys.WIDTH.toString()) as number; 128 this.height = fileAsset.get(photoAccessHelper.PhotoKeys.HEIGHT.toString()) as number; 129 } 130 Log.info(TAG, 'width ' + this.width); 131 Log.info(TAG, 'height ' + this.height); 132 } catch (err) { 133 Log.error(TAG, 'get width height ' + JSON.stringify(err)); 134 } 135 try { 136 this.title = fileAsset.get(photoAccessHelper.PhotoKeys.TITLE.toString()) as string; 137 Log.info(TAG, 'title ' + this.title); 138 } catch (err) { 139 Log.error(TAG, 'get title ' + JSON.stringify(err)); 140 } 141 try { 142 this.dateAdded = fileAsset.get(photoAccessHelper.PhotoKeys.DATE_ADDED.toString()) as number * 1000; 143 this.dateModified = fileAsset.get(photoAccessHelper.PhotoKeys.DATE_MODIFIED.toString()) as number * 1000; 144 this.dateTaken = fileAsset.get(photoAccessHelper.PhotoKeys.DATE_TAKEN.toString()) as number * 1000; 145 Log.info(TAG, 'dateAdded ' + this.dateAdded); 146 } catch (err) { 147 Log.error(TAG, 'get date ' + JSON.stringify(err)); 148 } 149 try { 150 this.favouriteStatus = fileAsset.get(photoAccessHelper.PhotoKeys.FAVORITE.toString()) as boolean ? STATUS_TRUE : STATUS_FALSE 151 Log.info(TAG, 'favouriteStatus ' + this.favouriteStatus); 152 } catch (err) { 153 Log.error(TAG, 'get favouriteStatus ' + JSON.stringify(err)); 154 } 155 try { 156 this.size = fileAsset.get(photoAccessHelper.PhotoKeys.SIZE.toString()) as number; 157 Log.info(TAG, 'size ' + this.size); 158 } catch (err) { 159 Log.error(TAG, 'get favouriteStatus ' + JSON.stringify(err)); 160 } 161 let size = { width: MediaConstants.DEFAULT_SIZE, height: MediaConstants.DEFAULT_SIZE }; 162 if (fileAsset != null && this.defaultThumbnail == undefined) { 163 try { 164 this.defaultThumbnail = await this.fileAsset.getThumbnail(size); 165 } catch (err) { 166 Log.error(TAG, 'getThumbnail error: ' + JSON.stringify(err)); 167 } 168 } 169 this.isSelect = selectManager.isSelect(this.uri, this.isSelect); 170 this.imgWidth = this.width; 171 this.imgHeight = this.height; 172 if (this.width > 0 && this.height > 0) { 173 this.status = MediaConstants.LOADED; 174 } else { 175 this.status = MediaConstants.PART_LOADED; 176 } 177 } 178 179 async getThumbnail(width: number, height: number): Promise<PixelMap> { 180 Log.debug(TAG, 'getThumbnail ' + this.status); 181 if (this.status !== MediaConstants.LOADED && this.status !== MediaConstants.PART_LOADED) { 182 Log.warn(TAG, 'getThumbnail fail as status: ' + this.status); 183 return undefined; 184 } 185 if (width === MediaConstants.DEFAULT_SIZE && height === MediaConstants.DEFAULT_SIZE) { 186 return this.defaultThumbnail; 187 } 188 let newThumbnail: PixelMap = undefined; 189 let size = { width: width, height: height }; 190 let cacheThumbnail = this.thumbnailArray.get(width.toString() + height.toString()); 191 if (cacheThumbnail != null) { 192 return cacheThumbnail; 193 } 194 if (this.fileAsset != undefined) { 195 try { 196 newThumbnail = await this.fileAsset.getThumbnail(size); 197 this.thumbnailArray.set(width.toString() + height.toString(), newThumbnail); 198 } catch (err) { 199 Log.error(TAG, 'getThumbnail error: ' + JSON.stringify(err)); 200 } 201 } 202 return newThumbnail; 203 } 204 205 getAlt(): Resource { 206 if (this.mediaType === photoAccessHelper.PhotoType.VIDEO) { 207 return $r('app.media.alt_video_placeholder'); 208 } else { 209 return $r('app.media.alt_placeholder'); 210 } 211 } 212 213 setSelect(isSelect: boolean): void { 214 this.isSelect = isSelect; 215 selectManager.setSelect(this.uri, this.isSelect); 216 } 217 218 async onDelete(): Promise<boolean> { 219 try { 220 await userFileModel.deleteOne(this.uri); 221 selectManager.deleteSelect(this.uri); 222 this.status = MediaConstants.TRASHED; 223 return true; 224 } catch (err) { 225 Log.error(TAG, 'onDelete ' + this.index + ' error: ' + JSON.stringify(err)); 226 return false; 227 } 228 } 229 230 async addToAlbum(albumUri:string): Promise<boolean> { 231 try { 232 await userFileModel.addPhotoToAlbumByUserFileMgr(albumUri, this.uri); 233 selectManager.deleteSelect(this.uri); 234 this.status = MediaConstants.TRASHED; 235 return true; 236 } catch (err) { 237 Log.error(TAG, 'addToAlbum ' + this.index + ' error: ' + JSON.stringify(err)); 238 return false; 239 } 240 } 241 242 isDeleted(): boolean { 243 return this.status === MediaConstants.TRASHED; 244 } 245 246 async isFavor(): Promise<boolean> { 247 if (this.favouriteStatus === STATUS_UNDEFINED) { 248 let fileAsset = await this.loadFileAsset(); 249 try { 250 this.favouriteStatus = (fileAsset.get(photoAccessHelper.PhotoKeys.FAVORITE.toString()) as boolean) ? STATUS_TRUE : STATUS_FALSE; 251 } catch (err) { 252 Log.error(TAG, 'isFavor error: ' + JSON.stringify(err)); 253 } 254 } 255 return this.favouriteStatus === STATUS_TRUE; 256 } 257 258 async setFavor(): Promise<boolean> { 259 let status = !(await this.isFavor()); 260 try { 261 let fileAsset = await this.loadFileAsset(); 262 await fileAsset.setFavorite(status); 263 this.favouriteStatus = status ? STATUS_TRUE : STATUS_FALSE; 264 return true; 265 } catch (err) { 266 return false; 267 } 268 } 269 270 async setName(name: string): Promise<void> { 271 let fileAsset = await this.loadFileAsset(); 272 let displayName = fileAsset.displayName; 273 let index = displayName.lastIndexOf('.'); 274 displayName = name + displayName.slice(index); 275 this.displayName = displayName; 276 this.title = name; 277 try { 278 fileAsset.set(photoAccessHelper.PhotoKeys.TITLE.toString(), name); 279 await fileAsset.commitModify(); 280 } catch (err) { 281 Log.error(TAG, 'setName error: ' + JSON.stringify(err)); 282 } 283 } 284} 285