• 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 { 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}