• 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 { MediaItem } from './MediaItem';
17import { ViewType } from './ViewType';
18import { AbsDataSource } from '../AbsDataSource';
19import { GetItemsCallback } from './GetItemsCallback';
20import { GetMediaCountCallback } from './GetMediaCountCallback';
21import { Log } from '../../../utils/Log';
22import { BroadCastConstants } from '../../common/BroadCastConstants';
23import { BroadCast } from '../../../utils/BroadCast';
24import { BroadCastManager } from '../../common/BroadCastManager';
25import { Constants } from '../../common/Constants';
26import { PendingTask } from './PendingTask';
27import type { PendingCondition } from './PendingCondition';
28import { TraceControllerUtils } from '../../../utils/TraceControllerUtils';
29import { AlbumDefine } from '../AlbumDefine';
30import { BrowserDataFactory } from '../../../interface/BrowserDataFactory';
31import type { BrowserDataInterface } from '../../../interface/BrowserDataInterface';
32import type { QueryParam } from '../BrowserDataImpl';
33import type { ViewData } from './ViewData';
34import { GetItemIndexCallback } from './GetItemIndexCallback';
35import { FileAsset } from '../../../access/UserFileManagerAccess';
36import { GetItemCallback } from './GetItemCallback';
37
38const TAG: string = 'common_MediaDataSource';
39
40export class MediaDataSource extends AbsDataSource {
41  // Number of first pictures loaded during initialization
42  private static INIT_FIRST_PATCH_LOAD_COUNT = 50;
43  initDataTraceName: string = 'PhotoGridPageInitData';
44
45  // Album list, all photos, etc. may involve album aggregation display, so multiple albums are required
46  photoDataImpl: BrowserDataInterface;
47
48  // Number of elements in layout
49  size: number = 0;
50  realSize: number = 0;
51  getItemCountFinish: boolean = false;
52
53  // Number of media in the dataset
54  mediaCount: number = 0;
55
56  // Is the quantity changed
57  isCountChanged: boolean = false;
58
59  // Is the quantity changed
60  isCountReduced: boolean = false;
61
62  addedCount: number = Constants.NUMBER_0;
63
64  // Save the amount of data of a window size
65  items: MediaItem[] = [];
66
67  // window Size
68  windowSize: number = 0;
69
70  // start point
71  activeStart: number = 0;
72
73  // end point
74  activeEnd: number = 0;
75
76  // layoutIndex to dataIndex
77  dataIndexes: number[] = [];
78
79  // dataIndex to layoutIndex
80  layoutIndexes: number[] = [];
81  broadCast: BroadCast;
82  photosBroadCast: BroadCast;
83
84  // The BroadCast of the application process. Event registration and destruction should be paired
85  appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
86
87  // Whether to delay updating data
88  isPendingUpdateData: boolean = true;
89
90  // During initialization, the task to data updates before the delay when count returns
91  pendingUpdateData: PendingTask;
92
93  // Request time of the first batch of data
94  firstPatchDataRequestTime: number;
95
96  // 删除ID,通知统一使用uri,准确查询整机相册中的资源,图库再通过uri识别系统相册。
97  albumUri: string = undefined;
98  filterMediaType: string = undefined;
99  isRefresh: boolean = false;
100  isEditSaveReload: boolean = false;
101  validEndIndex: number;
102  private browserActiveCallback: Function = this.onPhotoBrowserActive.bind(this);
103  private enableGetDataFlag: boolean = true;
104
105  constructor(windowSize: number) {
106    super();
107    this.photoDataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO);
108    this.windowSize = windowSize;
109    this.activeEnd = windowSize;
110    this.items = new Array(windowSize);
111    this.appBroadCast.on(BroadCastConstants.PHOTO_BROWSER_ACTIVE, this.browserActiveCallback);
112  }
113
114  releaseBroadCast(): void {
115    Log.debug(TAG, 'release all appBroadCast');
116    this.appBroadCast.off(BroadCastConstants.PHOTO_BROWSER_ACTIVE, this.browserActiveCallback);
117  }
118
119  totalCount(): number {
120    if (this.lastTotalCount != this.size) {
121      Log.info(TAG, `totalCount: ${this.size}`);
122      this.lastTotalCount = this.size;
123    }
124    return this.size;
125  }
126
127  realTotalCount(): number {
128    Log.info(TAG, `realTotalCount: ${this.size}, ${this.getItemCountFinish}`);
129    if (this.getItemCountFinish == true) {
130      return this.size;
131    }
132    return -1;
133  }
134
135  getData(index: number): ViewData {
136    if (!this.enableGetDataFlag) {
137      return undefined;
138    }
139    this.updateSlidingWindow(this.dataIndexes[index]);
140    let result = this.getWrappedData(index);
141    if (result == null) {
142      return undefined;
143    }
144    return result;
145  }
146
147  // get raw MediaItem data
148  getRawData(dataIndex: number): MediaItem {
149    if (dataIndex < this.activeStart || dataIndex >= this.activeEnd) {
150      Log.warn(TAG, `dataIndex is invalid: ${dataIndex}, ${this.activeStart} ~ ${this.activeEnd}`);
151      return undefined;
152    }
153    return this.items[dataIndex - this.activeStart];
154  }
155
156  getFirstRawData(): MediaItem {
157    if (this.items.length <= 0) {
158      return undefined;
159    }
160    return this.items[0];
161  }
162
163  // get DataIndex with item
164  getDataIndex(item: MediaItem): number {
165    for (let i = 0; i < this.items.length; i++) {
166      if (this.items[i] != undefined && this.items[i].uri === item.uri) {
167        return i + this.activeStart;
168      }
169    }
170    return Constants.NOT_FOUND;
171  }
172
173  getItemByUri(uri: string): MediaItem {
174    for (let i = 0; i < this.items.length; i++) {
175      if (this.items[i] != undefined && this.items[i].uri == uri) {
176        return this.items[i];
177      }
178    }
179    return null;
180  }
181
182  getDataIndexByUri(uri: string): number {
183    for (let i = 0; i < this.items.length; i++) {
184      if (this.items[i] != undefined && this.items[i].uri === uri) {
185        return i + this.activeStart;
186      }
187    }
188    return Constants.NOT_FOUND;
189  }
190  // Search for the index of first valid item in 500 items
191  getValidStartIndex(): number {
192    for (let i = 0; i < this.items.length; i++) {
193      if (this.items[i]) {
194        return i + this.activeStart;
195      }
196    }
197    return Constants.NOT_FOUND;
198  }
199  // Search for the index of last valid item in 500 items
200  getValidEndIndex(): number {
201    return this.validEndIndex;
202  }
203
204  getMediaItemByUri(uri: string): MediaItem {
205    if (this.items.length <= 0) {
206      return undefined;
207    }
208    for (let item of this.items) {
209      if (item != undefined && item.uri === uri) {
210        return item;
211      }
212    }
213    return undefined;
214  }
215
216  getMediaItemByUriFromAll(uri: string, itemNotifyCallback: Function): MediaItem {
217    Log.info(TAG, `getMediaItemByUriFromAll uri ${uri}`);
218    if (this.items.length <= 0) {
219      return undefined;
220    }
221
222    for (let item of this.items) {
223      if (item?.uri === uri) {
224        return item;
225      }
226    }
227
228    // 若当前数据不存在于当前列表的处理
229    Log.info(TAG, `getMediaItemByUriFromAll, ${uri}`);
230    let itemIndexCallback: GetItemCallback = new GetItemCallback(this, itemNotifyCallback);
231    this.photoDataImpl.getMediaItemByUri(itemIndexCallback, uri);
232
233    return undefined;
234  }
235
236  getMediaCount(): number {
237    return this.mediaCount;
238  }
239
240  resetActiveWindow(): void {
241    this.activeStart = 0;
242    this.activeEnd = this.windowSize;
243    this.items = new Array<MediaItem>(this.windowSize);
244  }
245
246  // Initialize the first batch of data
247  initData(): void {
248    TraceControllerUtils.startTrace(this.initDataTraceName);
249    this.getItemCountFinish = false;
250    Log.info(TAG, `initData, ${this.getItemCountFinish}`);
251    this.pendingUpdateData = new PendingTask(<PendingCondition> {
252      shouldPending: () => {
253        return this.isPendingUpdateData;
254      }
255    });
256    let start = 0;
257    let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT;
258    this.firstPatchDataRequestTime = Date.now();
259    this.lastUpdateTime = this.firstPatchDataRequestTime;
260    let firstPatchDataCallback = {
261      callback: (assets: MediaItem[]): void => {
262        Log.info(TAG, `took  ${(Date.now() - this.firstPatchDataRequestTime)}\
263          milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`);
264        if (assets.length > 0) {
265          this.updateMediaData(this.firstPatchDataRequestTime, start, assets);
266          let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, limit);
267          let secondParam: QueryParam = {
268            albumUri: this.albumUri,
269            start: limit,
270            count: this.windowSize - limit,
271          };
272          if (this.filterMediaType != undefined) {
273            secondParam.filterMediaType = this.filterMediaType;
274          }
275          this.photoDataImpl.getData(secondPatchDataCallback, secondParam);
276        }
277      }
278    };
279    let firstParam: QueryParam = {
280      albumUri: this.albumUri,
281      start: start,
282      count: limit,
283    };
284    if (this.filterMediaType != undefined) {
285      firstParam.filterMediaType = this.filterMediaType;
286    }
287    Log.info(TAG, `queryparam ${JSON.stringify(firstParam)}`);
288    this.photoDataImpl.getData(firstPatchDataCallback, firstParam);
289    this.loadData();
290  }
291
292  // Query quantity
293  loadData(): void {
294    Log.info(TAG, `loadData, ${this.getItemCountFinish}`);
295    let initCountCallback: GetMediaCountCallback = new GetMediaCountCallback(this);
296    if (this.filterMediaType != undefined) {
297      this.photoDataImpl.getDataCount(initCountCallback, {
298        albumUri: this.albumUri,
299        filterMediaType: this.filterMediaType
300      });
301    } else {
302      this.photoDataImpl.getDataCount(initCountCallback, { albumUri: this.albumUri });
303    }
304  }
305
306  getItemIndexByUri(uri: string, indexNotifyCallback: Function): void {
307    Log.info(TAG, `getItemIndexByUri, ${uri}`);
308    let itemIndexCallback: GetItemIndexCallback = new GetItemIndexCallback(this, indexNotifyCallback);
309    if (this.filterMediaType) {
310      this.photoDataImpl.getDataIndexByUri(itemIndexCallback, {
311        albumUri: this.albumUri,
312        filterMediaType: this.filterMediaType
313      }, uri);
314    } else {
315      this.photoDataImpl.getDataIndexByUri(itemIndexCallback, { albumUri: this.albumUri }, uri);
316    }
317  }
318
319  // update media count
320  updateMediaCount(requestTime: number, count: number): void {
321    TraceControllerUtils.startTraceWithTaskId('updateMediaCount', requestTime);
322    Log.info(TAG, `updateMediaCount requestTime: ${requestTime}, count: ${count}, real size: ${this.realSize}`);
323    this.lastUpdateTime = requestTime;
324
325    this.getItemCountFinish = true;
326    this.addedCount = (this.realSize > Constants.NUMBER_0) ? (count - this.realSize) : Constants.NUMBER_0;
327    this.realSize = count;
328    this.updateSize(requestTime, count);
329
330    TraceControllerUtils.finishTraceWithTaskId('updateMediaCount', requestTime);
331    if (this.isPendingUpdateData) {
332      this.isPendingUpdateData = false;
333      this.pendingUpdateData.flush();
334    }
335  }
336
337  /**
338   * Update related variables of count
339   *
340   * @param requestTime
341   * @param count
342   */
343  updateSize(requestTime: number, count: number): void {
344    Log.info(TAG, `updateSize, old size: ${this.size}, new size: ${count}`)
345    this.isCountChanged = count != this.size;
346    this.isCountReduced = count < this.size;
347    this.mediaCount = count;
348    this.size = this.mediaCount;
349    this.dataIndexes = [];
350    this.layoutIndexes = [];
351    for (let i = 0; i < this.size; i++) {
352      this.dataIndexes.push(i);
353      this.layoutIndexes.push(i);
354    }
355
356    this.updateCountPostProcess();
357  }
358
359  /**
360   * run after update count,Adjust sliding windows and query items as needed
361   */
362  updateCountPostProcess(): void {
363    Log.info(TAG, 'updateCountPostProcess');
364    // Exclude initData
365    if (this.hasNewChange) {
366      // when the total count less to activeEnd, the window need to change
367      if (this.activeEnd > this.mediaCount) {
368        let newActiveStart = Math.max(0, this.activeStart - (this.activeEnd - this.mediaCount));
369        let newActiveEnd = newActiveStart + this.windowSize;
370        // data reuse
371        if (newActiveEnd > this.activeStart) {
372          this.dataReuse(newActiveStart, this.activeStart, newActiveEnd);
373        }
374        this.activeStart = newActiveStart;
375        this.activeEnd = newActiveEnd;
376        Log.info(TAG, `updateSlidingWindow, newActiveStart:
377                ${this.activeStart} , newActiveEnd:${this.activeEnd}`);
378      }
379
380      if (this.mediaCount == 0) {
381        this.hasNewChange = false;
382        this.onDataReloaded();
383      } else if (this.isEditSaveReload || this.isCountChanged || this.isRefresh) {
384        // dirty data, need to get data again
385        Log.info(TAG, 'dirty data, need to get data again');
386        let callback: GetItemsCallback = new GetItemsCallback(this, this.activeStart);
387        let param: QueryParam = {
388          albumUri: this.albumUri,
389          start: this.activeStart,
390          count: this.windowSize,
391        };
392        if (this.filterMediaType != undefined) {
393          param.filterMediaType = this.filterMediaType;
394        }
395        this.photoDataImpl.getData(callback, param);
396      } else {
397        this.hasNewChange = false;
398      }
399    }
400    this.emitCountUpdateCallbacks();
401  }
402
403  emitCountUpdateCallbacks(): void {
404    this.mCallbacks['updateCount'] && this.mCallbacks['updateCount'](this.mediaCount);
405    this.broadCast && this.broadCast.emit(Constants.ON_LOADING_FINISHED, [this.mediaCount]);
406    this.notifySizeLoadingFinished(this.mediaCount);
407  }
408
409  // Update data in data callback
410  updateMediaData(requestTime: number, start: number, mediaItems: MediaItem[]): void {
411    if (requestTime == this.firstPatchDataRequestTime && this.isPendingUpdateData && this.size == 0) {
412      Log.info(TAG, 'the callback of mediaItems is earlier than that of count.');
413      this.updateCountThroughMediaItems(requestTime, mediaItems);
414      this.mediaDataUpdateExecutor(requestTime, start, mediaItems);
415      TraceControllerUtils.finishTrace(this.initDataTraceName);
416    } else if (this.isPendingUpdateData) {
417      Log.info(TAG, 'isPendingUpdateData execute start');
418      this.pendingUpdateData.execute(() => {
419        this.mediaDataUpdateExecutor(requestTime, start, mediaItems);
420        TraceControllerUtils.finishTrace(this.initDataTraceName);
421      });
422    } else {
423      this.mediaDataUpdateExecutor(requestTime, start, mediaItems);
424      TraceControllerUtils.finishTrace(this.initDataTraceName);
425    }
426  }
427
428  /**
429   * Update count through items
430   *
431   * @param requestTime
432   * @param mediaItems mediaItems
433   */
434  updateCountThroughMediaItems(requestTime: number, mediaItems: MediaItem[]): void {
435    Log.info(TAG, 'updateCountThroughMediaItems');
436    this.updateSize(0, mediaItems == null ? 0 : mediaItems.length);
437  }
438
439  /**
440   * Actual execution function of items update
441   *
442   * @param requestTime
443   * @param start items
444   * @param mediaItems mediaItems
445   */
446  mediaDataUpdateExecutor(requestTime: number, start: number, mediaItems: MediaItem[]): void {
447    TraceControllerUtils.startTraceWithTaskId('updateMediaData', requestTime);
448    Log.info(TAG, `updateMediaData requestTime: ${requestTime}, start: ${start}, this.addedCount: ${this.addedCount}, this.isEditSaveReload: ${this.isEditSaveReload}`);
449    if (this.lastUpdateTime < this.lastChangeTime && this.isActive) {
450      Log.info(TAG, 'request data expired, request again!');
451      this.loadData();
452    } else {
453      this.hasNewChange = false;
454    }
455
456    if (mediaItems == undefined || mediaItems.length == 0) {
457      Log.error(TAG, 'results are empty!');
458      return;
459    }
460
461    if (start >= this.activeEnd || start + mediaItems.length <= this.activeStart) {
462      Log.info(TAG, 'results are not in active window');
463      return;
464    }
465
466    Log.info(TAG, `updateMediaData mediaItems.length: ${mediaItems.length}`);
467    let fromIndex = Math.max(start, this.activeStart);
468    let endIndex = Math.min(this.activeEnd, start + mediaItems.length);
469    this.validEndIndex = endIndex - 1;
470    Log.info(TAG, `updateMediaData listeners size ${this.listeners.length}`)
471
472    for (let i = fromIndex; i < endIndex; i++) {
473      this.items[i - this.activeStart] = mediaItems[i - start];
474      Log.debug(TAG, `updateMediaData ${this.layoutIndexes[i]}, ${mediaItems[i - start].uri}, ${mediaItems[i - start].getTitle()}`);
475    }
476
477    if (this.isCountChanged || this.isRefresh) {
478      if (this.photosBroadCast && (this.isCountReduced || this.isRefresh || (this.addedCount > Constants.NUMBER_0)) && !this.isEditSaveReload) {
479        this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED, [this.addedCount]);
480        this.addedCount = Constants.NUMBER_0;
481      } else if (this.broadCast) {
482        this.broadCast.emit(BroadCastConstants.ON_DATA_RELOADED, []);
483      } else {
484        this.onDataReloaded();
485      }
486
487      this.isCountChanged = false;
488      this.isCountReduced = false;
489      this.isRefresh = false;
490    } else {
491      for (let i = fromIndex; i < endIndex; i++) {
492        this.onDataChanged(this.layoutIndexes[i]);
493      }
494    }
495
496    if (this.isEditSaveReload) {
497      if (this.addedCount >= 0) {
498        this.photosBroadCast && this.photosBroadCast.emit(BroadCastConstants.ON_DATA_RELOADED_WITH_EDIT, []);
499        this.addedCount = Constants.NUMBER_0;
500      }
501    } else {
502      this.notifyDataLoadingFinished();
503    }
504    TraceControllerUtils.finishTraceWithTaskId('updateMediaData', requestTime);
505  }
506
507  enableGetData(flag: boolean): void {
508    this.enableGetDataFlag = flag;
509  }
510
511  // Update sliding window
512  public updateSlidingWindow(dataIndex: number): void {
513    if (dataIndex == Constants.INVALID || dataIndex == undefined) {
514      return;
515    }
516    let windowCenter = Math.round((this.activeStart + this.activeEnd) / 2);
517    if (Math.abs(dataIndex - windowCenter) < Constants.STEP) {
518      return;
519    }
520    if (dataIndex < windowCenter && this.activeStart == 0) {
521      return;
522    }
523    if (dataIndex > windowCenter && this.activeEnd >= this.mediaCount) {
524      return;
525    }
526    let newActiveStart = this.getWindowActiveStart(dataIndex, windowCenter);
527    let newActiveEnd = newActiveStart + this.windowSize;
528    let requestStart = newActiveStart;
529    let requestCount = this.windowSize;
530    Log.info(TAG, `dataIndex: ${dataIndex}, windowCenter: ${windowCenter}, newActiveStart=${newActiveStart}`
531    + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`);
532
533    if (newActiveEnd < this.activeStart || newActiveStart > this.activeEnd) {
534      let limit = MediaDataSource.INIT_FIRST_PATCH_LOAD_COUNT;
535      let start = Math.max(0, (newActiveStart + newActiveEnd) / 2 - limit / 2);
536      this.firstPatchDataRequestTime = Date.now();
537      this.lastUpdateTime = this.firstPatchDataRequestTime;
538      let firstPatchDataCallback = {
539        callback: (assets: MediaItem[]): void => {
540          Log.info(TAG, `took  ${(Date.now() - this.firstPatchDataRequestTime)}\
541                     milliseconds to load first batch: ${(assets == null ? 0 : assets.length)}`);
542          if (assets.length > 0) {
543            this.updateMediaData(this.firstPatchDataRequestTime, start, assets);
544            let secondPatchDataCallback: GetItemsCallback = new GetItemsCallback(this, newActiveStart);
545            let secondParam: QueryParam = {
546              albumUri: this.albumUri,
547              start: newActiveStart,
548              count: this.windowSize
549            };
550            if (this.filterMediaType != undefined) {
551              secondParam.filterMediaType = this.filterMediaType;
552            }
553            this.photoDataImpl.getData(secondPatchDataCallback, secondParam);
554          }
555        }
556      };
557      let firstParam: QueryParam = {
558        albumUri: this.albumUri,
559        start: start,
560        count: limit
561      };
562      if (this.filterMediaType != undefined) {
563        firstParam.filterMediaType = this.filterMediaType;
564      }
565      this.photoDataImpl.getData(firstPatchDataCallback, firstParam);
566    }
567
568    if (newActiveEnd < this.activeEnd && newActiveEnd > this.activeStart) {
569      requestCount = this.activeStart - newActiveStart;
570      this.dataReuse(newActiveStart, this.activeStart, newActiveEnd);
571    }
572    if (newActiveStart > this.activeStart && newActiveStart < this.activeEnd) {
573      requestStart = this.activeEnd;
574      requestCount = newActiveEnd - this.activeEnd;
575      this.dataReuse(newActiveStart, newActiveStart, this.activeEnd);
576    }
577    if (newActiveStart > this.activeEnd || newActiveEnd < this.activeStart) {
578      this.items = new Array(this.windowSize);
579    }
580    Log.info(TAG, `activeStart=${this.activeStart}, activeEnd=${this.activeEnd}, newActiveStart=${newActiveStart}`
581    + `, newActiveEnd=${newActiveEnd}, requestStart=${requestStart}, requestCount=${requestCount}`);
582    this.activeStart = newActiveStart;
583    this.activeEnd = newActiveEnd;
584
585    let callback: GetItemsCallback = new GetItemsCallback(this, requestStart);
586    let param: QueryParam = {
587      albumUri: this.albumUri,
588      start: requestStart,
589      count: requestCount
590    };
591    if (this.filterMediaType != undefined) {
592      param.filterMediaType = this.filterMediaType;
593    }
594    this.photoDataImpl.getData(callback, param);
595  }
596
597  getMediaItemSafely(index: number): MediaItem {
598    let mediaItem: MediaItem = this.items[index];
599    if (mediaItem == null) {
600      Log.error(TAG, `invalid data, index:${index}, active Start:${this.activeStart}, End:${this.activeEnd}`);
601      mediaItem = new MediaItem(null);
602    }
603    return mediaItem;
604  }
605
606  // Forced refresh interface
607  forceUpdate() {
608    this.onDataReloaded();
609  }
610
611  // Packaging data for the view layer
612  getWrappedData(index: number): ViewData {
613    let mediaItemIndex: number = this.dataIndexes[index] - this.activeStart;
614    if (mediaItemIndex < 0 || mediaItemIndex >= this.items.length) {
615      return undefined;
616    }
617    let result = {
618      viewType: ViewType.ITEM,
619      mediaItem: this.getMediaItemSafely(mediaItemIndex),
620      viewIndex: index
621    };
622    return result;
623  }
624
625  setAlbumUri(uri: string): void {
626    Log.info(TAG, `setAlbumUri: ${uri}`)
627    this.albumUri = uri;
628  }
629
630  setFilterMediaType(filterMediaType: string): void {
631    Log.info(TAG, `set filterMediaType: ${filterMediaType}`)
632    this.filterMediaType = filterMediaType;
633  }
634
635  setBroadCast(broadCastParam: BroadCast): void {
636    this.broadCast = broadCastParam;
637  }
638
639  setPhotoBroadCast(broadCastParam: BroadCast): void {
640    this.photosBroadCast = broadCastParam;
641  }
642
643  releasePhotoBroadCast(): void {
644    this.photosBroadCast = null;
645  }
646
647  onPhotoBrowserActive(isActive: boolean, transition: string): void {
648    if (transition == Constants.PHOTO_TRANSITION_ALBUM || transition == Constants.PHOTO_TRANSITION_CAMERA) {
649      if (isActive) {
650        Log.info(TAG, 'onPhotoBrowserActive')
651        this.onActive();
652      } else {
653        this.onInActive();
654      }
655    } else if (transition == Constants.PHOTO_TRANSITION_EDIT) {
656      if (isActive) {
657        this.isEditSaveReload = true;
658        this.onActive();
659      } else {
660        this.isEditSaveReload = false;
661      }
662    }
663  }
664
665  getIndexByMediaItem(item: MediaItem): number {
666    for (let index = 0; index < this.items.length; index++) {
667      if (item.uri == this.items[index].uri) {
668        this.items[index] = item
669        return index;
670      }
671    }
672    return -1;
673  }
674
675  // 父类方法需要子类覆写
676  getPositionByIndex(index: number): number {
677    return index;
678  }
679
680  onChange(mediaType: string): void {
681    if (mediaType === 'image' || mediaType === 'video' || mediaType === 'album') {
682      Log.debug(TAG, `local onChange ${mediaType}`);
683      super.onChange(mediaType);
684    }
685  }
686
687  switchRefreshOn(): void {
688    this.isRefresh = true
689  }
690
691  getGroupCountBeforeItem(item: MediaItem): number {
692    return 0;
693  }
694
695  private getWindowActiveStart(dataIndex: number, windowCenter: number): number {
696    let isForward = (dataIndex > windowCenter);
697    let halfWinSize = Math.round(this.windowSize / 2);
698    // The end of the window does not exceed the total amount of data when the window moves forward.
699    if (isForward) {
700      let forwardStep = dataIndex - windowCenter;
701      forwardStep = forwardStep % Constants.STEP == 0
702        ? forwardStep
703        : Math.ceil(forwardStep / Constants.STEP) * Constants.STEP;
704      let forwardStepSize = Math.min(forwardStep, Math.abs(this.mediaCount - this.activeEnd));
705      Log.info(TAG, `forwardStep=${forwardStep}, stepSize=${forwardStepSize}, activeStart=${this.activeStart}`);
706      return (this.activeStart + forwardStepSize);
707    } else {
708      let backwardStep = windowCenter - dataIndex;
709      backwardStep = backwardStep % Constants.STEP == 0
710        ? backwardStep
711        : Math.ceil(backwardStep / Constants.STEP) * Constants.STEP;
712      Log.info(TAG, `backward step ${backwardStep}, activeStart=${this.activeStart}`);
713      return Math.max(0, this.activeStart - backwardStep);
714    }
715  }
716
717  /**
718   * data Reuse
719   *
720   * @param newActiveStart
721   * @param startIndex
722   * @param endIndex
723   */
724  private dataReuse(newActiveStart: number, startIndex: number, endIndex: number): void {
725    let newData: MediaItem[] = new Array(this.windowSize);
726    for (let i = startIndex; i < endIndex; i++) {
727      newData[i - newActiveStart] = this.items[i - this.activeStart];
728    }
729    this.items = newData;
730  }
731}