• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { FileAsset, 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  getAlbumDataSource(): MediaDataSource | undefined {
242    if (this.albumDataSource) {
243      return this.albumDataSource;
244    }
245    return undefined;
246  }
247
248  setBroadCast(broadCastParam: BroadCast): void {
249    this.broadCast = broadCastParam;
250  }
251
252  setBroadCastToAlbum(broadCastParam: BroadCast) {
253    if (this.albumDataSource) {
254      this.albumDataSource.setPhotoBroadCast(broadCastParam);
255    }
256  }
257
258  onDataReloaded() {
259    Log.info(TAG, `onDataReloaded start`);
260    if (this.albumDataSource) {
261      this.albumDataSource.onDataReloaded();
262    }
263  }
264
265  onSizeLoadingFinished(size: number): void {
266    Log.info(TAG, `onSizeLoadingFinished, current size: ${size}`);
267    this.broadCast && this.broadCast.emit(PhotoConstants.DATA_SIZE_CHANGED, [size]);
268  }
269
270  onDataLoadingFinished(): void {
271    // after the mediaLib updates the data, it directly calls onDataReloaded.
272    // swiper updates only the five mounted items
273    Log.debug(TAG, `onDataLoadingFinished listeners size: ${this.albumDataSource.listeners.length}\
274            totalCount: ${this.totalCount()}`);
275    this.broadCast && this.broadCast.emit(PhotoConstants.DATA_CONTENT_CHANGED, []);
276  }
277
278  onDataChanged(dataIndex: number): void {
279    if (this.albumDataSource) {
280      this.albumDataSource.listeners.forEach(listener => {
281        listener.onDataChanged(dataIndex);
282      })
283    }
284  }
285
286  deleteData(index: number) {
287    if (this.albumDataSource) {
288      this.albumDataSource.listeners.forEach(listener => {
289        Log.debug(TAG, 'onDataDeleted');
290        listener.onDataDeleted(index);
291      })
292    }
293  }
294
295  async getDataByUri(uri: string): Promise<FileAsset> {
296    let tmp: FileAsset = await this.photoDataImpl.getDataByUri(uri) as FileAsset;
297    return tmp;
298  }
299
300  getItemByUri(uri: string): any {
301    if (this.albumDataSource) {
302      return this.albumDataSource.getItemByUri(uri);
303    }
304    return null;
305  }
306
307  getDataIndexByUri(uri: string): number {
308    if (this.albumDataSource) {
309      return this.albumDataSource.getDataIndexByUri(uri);
310    }
311    return Constants.NOT_FOUND;
312  }
313  // Search for the index of first valid item in MediaDataSource
314  getValidStartIndex(): number {
315    if (this.albumDataSource) {
316      return this.albumDataSource.getValidStartIndex();
317    }
318    return Constants.NOT_FOUND;
319  }
320  // Search for the index of last valid item in MediaDataSource
321  getValidEndIndex(): number {
322    if (this.albumDataSource) {
323      return this.albumDataSource.getValidEndIndex();
324    }
325    return Constants.NOT_FOUND;
326  }
327
328  public release(): void {
329    if (this.albumDataSource) {
330      this.albumDataSource.releasePhotoBroadCast();
331      this.albumDataSource.removeLoadingListener(this);
332      // cancel the mediaLibrary listening of albumDataSource when the large image is destroyed.
333      // cannot cancel it in unregisterDataChangeListener
334      this.albumDataSource && this.albumDataSource.unregisterObserver();
335    }
336
337    this.broadCast = null;
338  }
339
340  getPositionByIndex(index: number): number {
341    if (this.albumDataSource) {
342      return this.albumDataSource.getPositionByIndex(index);
343    }
344  }
345
346  onChange(changeType) {
347    if (this.albumDataSource) {
348      this.albumDataSource.onActive();
349      this.albumDataSource.onChange(changeType);
350    }
351  }
352
353  switchRefreshOn() {
354    if (this.albumDataSource) {
355      this.albumDataSource.switchRefreshOn();
356    }
357  };
358
359  private convertDecodeSize(imageWidth: number, imageHeight: number): number {
360    const HOLD_SCALE: number = 1.0;
361    if (imageWidth <= 0 || imageHeight <= 0) {
362      return HOLD_SCALE;
363    }
364    let displayClass: display.Display = display.getDefaultDisplaySync();
365    let screenWidth: number = displayClass.width;
366    let screenHeight: number = displayClass.height;
367    if (screenWidth <= 0 || screenHeight <= 0) {
368      return HOLD_SCALE;
369    }
370    let scale = HOLD_SCALE;
371    let desiredScale = screenHeight / screenWidth;
372    let sourceScale = imageHeight / imageWidth;
373
374    if (sourceScale > desiredScale) {
375      scale = screenHeight / imageHeight;
376    } else {
377      scale = screenWidth / imageWidth;
378    }
379    return scale < HOLD_SCALE ? scale : HOLD_SCALE;
380  }
381
382  private generateSampleSize(imageWidth: number, imageHeight: number, index: number): number {
383    let width = vp2px(ScreenManager.getInstance().getWinWidth());
384    let height = vp2px(ScreenManager.getInstance().getWinHeight());
385    width = width == 0 ? ScreenManager.DEFAULT_WIDTH : width;
386    height = height == 0 ? ScreenManager.DEFAULT_HEIGHT : height;
387    let maxNumOfPixels
388    if (this.currentIndex == index) {
389      maxNumOfPixels = 2 * width * height;
390    } else {
391      maxNumOfPixels = width * height;
392    }
393    let minSide = Math.min(width, height);
394    return ImageUtil.computeSampleSize(imageWidth, imageHeight, minSide, maxNumOfPixels);
395  }
396}