• 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  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: string): Promise<FileAsset> {
289    let tmp: FileAsset = await this.photoDataImpl.getDataByUri(uri) as FileAsset;
290    return tmp;
291  }
292
293  getItemByUri(uri: string): any {
294    if (this.albumDataSource) {
295      return this.albumDataSource.getItemByUri(uri);
296    }
297    return null;
298  }
299
300  getDataIndexByUri(uri: string): number {
301    if (this.albumDataSource) {
302      return this.albumDataSource.getDataIndexByUri(uri);
303    }
304    return Constants.NOT_FOUND;
305  }
306  // Search for the index of first valid item in MediaDataSource
307  getValidStartIndex(): number {
308    if (this.albumDataSource) {
309      return this.albumDataSource.getValidStartIndex();
310    }
311    return Constants.NOT_FOUND;
312  }
313  // Search for the index of last valid item in MediaDataSource
314  getValidEndIndex(): number {
315    if (this.albumDataSource) {
316      return this.albumDataSource.getValidEndIndex();
317    }
318    return Constants.NOT_FOUND;
319  }
320
321  public release(): void {
322    if (this.albumDataSource) {
323      this.albumDataSource.releasePhotoBroadCast();
324      this.albumDataSource.removeLoadingListener(this);
325      // cancel the mediaLibrary listening of albumDataSource when the large image is destroyed.
326      // cannot cancel it in unregisterDataChangeListener
327      this.albumDataSource && this.albumDataSource.unregisterObserver();
328    }
329
330    this.broadCast = null;
331  }
332
333  getPositionByIndex(index: number): number {
334    if (this.albumDataSource) {
335      return this.albumDataSource.getPositionByIndex(index);
336    }
337  }
338
339  onChange(changeType) {
340    if (this.albumDataSource) {
341      this.albumDataSource.onActive();
342      this.albumDataSource.onChange(changeType);
343    }
344  }
345
346  switchRefreshOn() {
347    if (this.albumDataSource) {
348      this.albumDataSource.switchRefreshOn();
349    }
350  };
351
352  private convertDecodeSize(imageWidth: number, imageHeight: number): number {
353    const HOLD_SCALE: number = 1.0;
354    if (imageWidth <= 0 || imageHeight <= 0) {
355      return HOLD_SCALE;
356    }
357    let displayClass: display.Display = display.getDefaultDisplaySync();
358    let screenWidth: number = displayClass.width;
359    let screenHeight: number = displayClass.height;
360    if (screenWidth <= 0 || screenHeight <= 0) {
361      return HOLD_SCALE;
362    }
363    let scale = HOLD_SCALE;
364    let desiredScale = screenHeight / screenWidth;
365    let sourceScale = imageHeight / imageWidth;
366
367    if (sourceScale > desiredScale) {
368      scale = screenHeight / imageHeight;
369    } else {
370      scale = screenWidth / imageWidth;
371    }
372    return scale < HOLD_SCALE ? scale : HOLD_SCALE;
373  }
374
375  private generateSampleSize(imageWidth: number, imageHeight: number, index: number): number {
376    let width = vp2px(ScreenManager.getInstance().getWinWidth());
377    let height = vp2px(ScreenManager.getInstance().getWinHeight());
378    width = width == 0 ? ScreenManager.DEFAULT_WIDTH : width;
379    height = height == 0 ? ScreenManager.DEFAULT_HEIGHT : height;
380    let maxNumOfPixels
381    if (this.currentIndex == index) {
382      maxNumOfPixels = 2 * width * height;
383    } else {
384      maxNumOfPixels = width * height;
385    }
386    let minSide = Math.min(width, height);
387    return ImageUtil.computeSampleSize(imageWidth, imageHeight, minSide, maxNumOfPixels);
388  }
389}