1/* 2 * Copyright (c) 2022-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 { Log } from '../../../utils/Log'; 17import { MediaDataSource } from './MediaDataSource'; 18import type { LoadingListener } from '../LoadingListener'; 19import { BroadCast } from '../../../utils/BroadCast'; 20import { Constants as PhotoConstants } from './Constants'; 21import { MediaItem } from './MediaItem'; 22import { Constants } from '../../common/Constants'; 23import { BrowserDataFactory } from '../../../interface/BrowserDataFactory'; 24import { AlbumDefine } from '../AlbumDefine'; 25import { BroadCastConstants } from '../../common/BroadCastConstants'; 26import { BroadCastManager } from '../../common/BroadCastManager'; 27import { ImageUtil } from '../../../utils/ImageUtil'; 28import { ScreenManager } from '../../common/ScreenManager'; 29 30import display from '@ohos.display'; 31import { UserFileManagerAccess } from '../../../access/UserFileManagerAccess'; 32 33const TAG: string = 'common_PhotoDataSource'; 34 35// DataSource 36export class PhotoDataSource implements IDataSource, LoadingListener { 37 static readonly MEIDA_URL_PREFIX_STR = 'datashare:///media/'; 38 static readonly ALBUM_URL_PREFIX_STR = 'datashare:///media/album/'; 39 static readonly IMAGE_URL_PREFIX_STR = 'datashare:///media/image/'; 40 static readonly VIDEO_URL_PREFIX_STR = 'datashare:///media/video/'; 41 static readonly IMAGE_URL_PREFIX_STR_V10 = 'file://media/image/'; 42 static readonly VIDEO_URL_PREFIX_STR_V10 = 'file://media/video/'; 43 static readonly MEIDA_URL_PREFIX_STR_V10 = 'file://media/'; 44 static readonly ALBUM_URL_PREFIX_STR_V10 = 'file://media/PhotoAlbum/'; 45 static readonly IMAGE_VIDEO_URL_PREFIX_STR_V10 = 'file://media/Photo/'; 46 47 static readonly IMAGE_TYPE_PREFIX_STR = 'image/'; 48 static readonly VIDEO_TYPE_PREFIX_STR = 'video/'; 49 // Data change listener 50 albumUri: string = undefined; 51 protected photoDataImpl; 52 private lastTotalCount = -1; 53 private albumDataSource: MediaDataSource; 54 private broadCast: BroadCast; 55 private currentIndex = 0; 56 private deviceId: string = '' 57 private enableGetDataFlag: boolean = true; 58 59 constructor(albumUri?: string) { 60 Log.debug(TAG, 'bind onMessage'); 61 if (albumUri) { 62 this.albumUri = albumUri; 63 } 64 65 this.photoDataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO); 66 } 67 68 totalCount(): number { 69 let newTotalCount = 0; 70 if (this.albumDataSource) { 71 newTotalCount = this.albumDataSource.mediaCount; 72 if (this.lastTotalCount != newTotalCount) { 73 Log.info(TAG, `totalCount: ${newTotalCount}`); 74 this.lastTotalCount = newTotalCount; 75 } 76 if (newTotalCount > 0 && !this.albumDataSource.getFirstRawData()) { 77 return 0; 78 } 79 } 80 return newTotalCount || 0; 81 } 82 83 realTotalCount(): number { 84 let totalCount = 0; 85 if (this.albumDataSource) { 86 totalCount = this.albumDataSource.realTotalCount(); 87 Log.debug(TAG, `realTotalCount: ${totalCount}`); 88 return totalCount; 89 } 90 return 0; 91 } 92 93 // get DataIndex with item 94 getDataIndex(item: MediaItem): number { 95 return this.albumDataSource.getDataIndex(item); 96 } 97 98 resetAlbumUri(albumUri: string) { 99 this.albumUri = albumUri; 100 if (this.albumDataSource) { 101 this.albumDataSource.setAlbumUri(this.albumUri); 102 this.albumDataSource.resetActiveWindow(); 103 this.albumDataSource.initData(); 104 this.albumDataSource.forceUpdate(); 105 } 106 } 107 108 setAlbumUri(albumUri: string) { 109 this.albumUri = albumUri; 110 } 111 112 getAlbumUri(): string { 113 return this.albumUri; 114 } 115 116 setDeviceId(deviceId: string) { 117 this.deviceId = deviceId; 118 } 119 120 releaseCache(key: string): void { 121 } 122 123 getItemIndexByUri(uri: string, indexNotifyCallback: Function): void { 124 Log.info(TAG, `getItemIndexByUri, ${uri}`); 125 this.albumDataSource.getItemIndexByUri(uri, indexNotifyCallback); 126 } 127 128 initData() { 129 let dataSource: MediaDataSource = new MediaDataSource(Constants.DEFAULT_SLIDING_WIN_SIZE); 130 dataSource.setAlbumUri(this.albumUri); 131 dataSource.initData(); 132 this.setAlbumDataSource(dataSource); 133 } 134 135 enableGetData(flag: boolean): void { 136 this.enableGetDataFlag = flag; 137 if (this.albumDataSource) { 138 this.albumDataSource.enableGetData(flag); 139 } 140 } 141 142 // LazyForEach call 143 getData(index: number): any { 144 if (!this.enableGetDataFlag) { 145 return undefined; 146 } 147 Log.info(TAG, `getData index ${index}`); 148 this.albumDataSource.updateSlidingWindow(index); 149 let mediaItem: MediaItem = this.albumDataSource.getRawData(index); 150 return this.packData(index, mediaItem); 151 } 152 153 packData(index: number, mediaItem: MediaItem) { 154 if (!mediaItem) { 155 Log.error(TAG, `Get item undefined, index: ${index}`); 156 return undefined; 157 } 158 if (mediaItem.height == 0 || mediaItem.width == 0) { 159 this.getDataByUri(mediaItem.uri).then((result) => { 160 mediaItem = new MediaItem(result); 161 if (mediaItem.height == 0 || mediaItem.width == 0) { 162 return; 163 } 164 let index = this.albumDataSource.getIndexByMediaItem(mediaItem); 165 if (index != -1) { 166 this.albumDataSource.onDataChanged(index); 167 } 168 this.onDataChanged(index); 169 }) 170 } 171 let imgWidth = mediaItem.width; 172 let imgHeight = mediaItem.height; 173 let scale = this.convertDecodeSize(mediaItem.width, mediaItem.height); 174 Log.debug(TAG, `packData imgWidth: ${imgWidth} imgHeight: ${imgHeight} scale: ${scale}`); 175 if (scale != 0) { 176 const NEAR_PIX: number = 0.01; 177 mediaItem.imgWidth = Math.floor(mediaItem.width * scale); 178 mediaItem.imgHeight = Math.floor(mediaItem.height * scale); 179 imgWidth = Math.floor(imgWidth * scale + NEAR_PIX); 180 imgHeight = Math.floor(imgHeight * scale + NEAR_PIX); 181 } 182 Log.debug(TAG, `packData imgWidth: ${imgWidth} imgHeight: ${imgHeight}}`); 183 184 return { 185 data: mediaItem, 186 pos: index, 187 thumbnail: this.photoDataImpl.getThumbnail(mediaItem.uri, mediaItem.path, { height: imgHeight, width: imgWidth }) 188 }; 189 } 190 191 updatePixMapDataSource(index: number): void { 192 this.currentIndex = index; 193 // cache load. 194 } 195 196 getRawData(index: number): any { 197 if (this.albumDataSource) { 198 return { 199 data: this.albumDataSource.getRawData(index), 200 pos: index 201 }; 202 } 203 204 Log.warn(TAG, `albumDataSource is undefined for index:${index}`); 205 206 return { 207 data: null, 208 pos: index 209 }; 210 } 211 212 registerDataChangeListener(listener: DataChangeListener): void { 213 Log.info(TAG, 'registerDataChangeListener'); 214 if (this.albumDataSource) { 215 this.albumDataSource.registerObserver(); 216 if (this.albumDataSource.listeners.indexOf(listener) < 0) { 217 this.albumDataSource.listeners.push(listener); 218 } 219 Log.debug(TAG, `listener size: ${this.albumDataSource.listeners.length}`); 220 } 221 222 } 223 224 unregisterDataChangeListener(listener: DataChangeListener): void { 225 Log.info(TAG, 'unregisterDataChangeListener'); 226 if (this.albumDataSource) { 227 const pos = this.albumDataSource.listeners.indexOf(listener); 228 if (pos >= 0) { 229 this.albumDataSource.listeners.splice(pos, 1); 230 } 231 Log.debug(TAG, `unregisterDataChangeListener listener size: ${this.albumDataSource.listeners.length}`); 232 } 233 } 234 235 setAlbumDataSource(albumDataSource: MediaDataSource): void { 236 Log.debug(TAG, 'setAlbumDataSource'); 237 this.albumDataSource = albumDataSource; 238 this.albumDataSource.addLoadingListener(this); 239 } 240 241 setBroadCast(broadCastParam: BroadCast): void { 242 this.broadCast = broadCastParam; 243 } 244 245 setBroadCastToAlbum(broadCastParam: BroadCast) { 246 if (this.albumDataSource) { 247 this.albumDataSource.setPhotoBroadCast(broadCastParam); 248 } 249 } 250 251 onDataReloaded() { 252 Log.info(TAG, `onDataReloaded start`); 253 if (this.albumDataSource) { 254 this.albumDataSource.onDataReloaded(); 255 } 256 } 257 258 onSizeLoadingFinished(size: number): void { 259 Log.info(TAG, `onSizeLoadingFinished, current size: ${size}`); 260 this.broadCast && this.broadCast.emit(PhotoConstants.DATA_SIZE_CHANGED, [size]); 261 } 262 263 onDataLoadingFinished(): void { 264 // after the mediaLib updates the data, it directly calls onDataReloaded. 265 // swiper updates only the five mounted items 266 Log.debug(TAG, `onDataLoadingFinished listeners size: ${this.albumDataSource.listeners.length}\ 267 totalCount: ${this.totalCount()}`); 268 this.broadCast && this.broadCast.emit(PhotoConstants.DATA_CONTENT_CHANGED, []); 269 } 270 271 onDataChanged(dataIndex: number): void { 272 if (this.albumDataSource) { 273 this.albumDataSource.listeners.forEach(listener => { 274 listener.onDataChanged(dataIndex); 275 }) 276 } 277 } 278 279 deleteData(index: number) { 280 if (this.albumDataSource) { 281 this.albumDataSource.listeners.forEach(listener => { 282 Log.debug(TAG, 'onDataDeleted'); 283 listener.onDataDeleted(index); 284 }) 285 } 286 } 287 288 async getDataByUri(uri) { 289 return await this.photoDataImpl.getDataByUri(uri); 290 } 291 292 getItemByUri(uri: string): any { 293 if (this.albumDataSource) { 294 return this.albumDataSource.getItemByUri(uri); 295 } 296 return null; 297 } 298 299 getDataIndexByUri(uri: string): number { 300 if (this.albumDataSource) { 301 return this.albumDataSource.getDataIndexByUri(uri); 302 } 303 return Constants.NOT_FOUND; 304 } 305 // Search for the index of first valid item in MediaDataSource 306 getValidStartIndex(): number { 307 if (this.albumDataSource) { 308 return this.albumDataSource.getValidStartIndex(); 309 } 310 return Constants.NOT_FOUND; 311 } 312 // Search for the index of last valid item in MediaDataSource 313 getValidEndIndex(): number { 314 if (this.albumDataSource) { 315 return this.albumDataSource.getValidEndIndex(); 316 } 317 return Constants.NOT_FOUND; 318 } 319 320 public release(): void { 321 if (this.albumDataSource) { 322 this.albumDataSource.releasePhotoBroadCast(); 323 this.albumDataSource.removeLoadingListener(this); 324 // cancel the mediaLibrary listening of albumDataSource when the large image is destroyed. 325 // cannot cancel it in unregisterDataChangeListener 326 this.albumDataSource && this.albumDataSource.unregisterObserver(); 327 } 328 329 this.broadCast = null; 330 } 331 332 getPositionByIndex(index: number): number { 333 if (this.albumDataSource) { 334 return this.albumDataSource.getPositionByIndex(index); 335 } 336 } 337 338 onChange(changeType) { 339 if (this.albumDataSource) { 340 this.albumDataSource.onActive(); 341 this.albumDataSource.onChange(changeType); 342 } 343 } 344 345 switchRefreshOn() { 346 if (this.albumDataSource) { 347 this.albumDataSource.switchRefreshOn(); 348 } 349 }; 350 351 private convertDecodeSize(imageWidth: number, imageHeight: number): number { 352 const HOLD_SCALE: number = 1.0; 353 if (imageWidth <= 0 || imageHeight <= 0) { 354 return HOLD_SCALE; 355 } 356 let displayClass: display.Display = display.getDefaultDisplaySync(); 357 let screenWidth: number = displayClass.width; 358 let screenHeight: number = displayClass.height; 359 if (screenWidth <= 0 || screenHeight <= 0) { 360 return HOLD_SCALE; 361 } 362 let scale = HOLD_SCALE; 363 let desiredScale = screenHeight / screenWidth; 364 let sourceScale = imageHeight / imageWidth; 365 366 if (sourceScale > desiredScale) { 367 scale = screenHeight / imageHeight; 368 } else { 369 scale = screenWidth / imageWidth; 370 } 371 return scale < HOLD_SCALE ? scale : HOLD_SCALE; 372 } 373 374 private generateSampleSize(imageWidth: number, imageHeight: number, index: number): number { 375 let width = vp2px(ScreenManager.getInstance().getWinWidth()); 376 let height = vp2px(ScreenManager.getInstance().getWinHeight()); 377 width = width == 0 ? ScreenManager.DEFAULT_WIDTH : width; 378 height = height == 0 ? ScreenManager.DEFAULT_HEIGHT : height; 379 let maxNumOfPixels 380 if (this.currentIndex == index) { 381 maxNumOfPixels = 2 * width * height; 382 } else { 383 maxNumOfPixels = width * height; 384 } 385 let minSide = Math.min(width, height); 386 return ImageUtil.computeSampleSize(imageWidth, imageHeight, minSide, maxNumOfPixels); 387 } 388}