100aff185Sopenharmony_ci/*
200aff185Sopenharmony_ci * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
300aff185Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
400aff185Sopenharmony_ci * you may not use this file except in compliance with the License.
500aff185Sopenharmony_ci * You may obtain a copy of the License at
600aff185Sopenharmony_ci *
700aff185Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
800aff185Sopenharmony_ci *
900aff185Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1000aff185Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1100aff185Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1200aff185Sopenharmony_ci * See the License for the specific language governing permissions and
1300aff185Sopenharmony_ci * limitations under the License.
1400aff185Sopenharmony_ci */
1500aff185Sopenharmony_ci
1600aff185Sopenharmony_ciimport type { BrowserDataInterface, PendingCondition } from '@ohos/common';
1700aff185Sopenharmony_ciimport {
1800aff185Sopenharmony_ci  BroadCast,
1900aff185Sopenharmony_ci  BrowserDataFactory,
2000aff185Sopenharmony_ci  CommonObserverCallback,
2100aff185Sopenharmony_ci  Constants,
2200aff185Sopenharmony_ci  DateUtil,
2300aff185Sopenharmony_ci  Log,
2400aff185Sopenharmony_ci  MediaDataSource,
2500aff185Sopenharmony_ci  MediaItem,
2600aff185Sopenharmony_ci  MediaObserver,
2700aff185Sopenharmony_ci  PendingTask,
2800aff185Sopenharmony_ci  TimelineData,
2900aff185Sopenharmony_ci  TraceControllerUtils,
3000aff185Sopenharmony_ci  ViewData,
3100aff185Sopenharmony_ci  ViewType
3200aff185Sopenharmony_ci} from '@ohos/common';
3300aff185Sopenharmony_ciimport { GetTimelineDataCallback } from './GetTimelineDataCallback';
3400aff185Sopenharmony_ci
3500aff185Sopenharmony_ciconst TITLE_DATA_INDEX = -1;
3600aff185Sopenharmony_ciconst TAG: string = 'timeline_TimelineDataSource';
3700aff185Sopenharmony_ci
3800aff185Sopenharmony_ci// TimelineDataSource
3900aff185Sopenharmony_ciexport class TimelineDataSource extends MediaDataSource {
4000aff185Sopenharmony_ci  initDataTraceName = 'TimeLinePageInitData';
4100aff185Sopenharmony_ci  dataObserver: CommonObserverCallback = new CommonObserverCallback(this);
4200aff185Sopenharmony_ci  groups: TimelineData[] = [];
4300aff185Sopenharmony_ci
4400aff185Sopenharmony_ci  // layoutIndex to groupIndex
4500aff185Sopenharmony_ci  groupIndexes: number[] = [];
4600aff185Sopenharmony_ci  groupViewIndexes: number[] = [];
4700aff185Sopenharmony_ci  isActive = false;
4800aff185Sopenharmony_ci  pendingEmitCallbacks: PendingTask;
4900aff185Sopenharmony_ci  groupBrowserDataImpl: BrowserDataInterface;
5000aff185Sopenharmony_ci
5100aff185Sopenharmony_ci  constructor(windowSize: number, broadCast: BroadCast) {
5200aff185Sopenharmony_ci    super(windowSize);
5300aff185Sopenharmony_ci    this.groupBrowserDataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_GROUP);
5400aff185Sopenharmony_ci    this.broadCast = broadCast;
5500aff185Sopenharmony_ci    this.pendingEmitCallbacks = new PendingTask(<PendingCondition> {
5600aff185Sopenharmony_ci      shouldPending: () => {
5700aff185Sopenharmony_ci        return !this.isActive;
5800aff185Sopenharmony_ci      }
5900aff185Sopenharmony_ci    });
6000aff185Sopenharmony_ci    this.initData();
6100aff185Sopenharmony_ci  }
6200aff185Sopenharmony_ci
6300aff185Sopenharmony_ci  initialize(): void {
6400aff185Sopenharmony_ci  }
6500aff185Sopenharmony_ci
6600aff185Sopenharmony_ci  getGroupData(): TimelineData[] {
6700aff185Sopenharmony_ci    return this.groups;
6800aff185Sopenharmony_ci  }
6900aff185Sopenharmony_ci
7000aff185Sopenharmony_ci  loadData() {
7100aff185Sopenharmony_ci    let callback: GetTimelineDataCallback = new GetTimelineDataCallback(this);
7200aff185Sopenharmony_ci    this.groupBrowserDataImpl.getData(callback, null);
7300aff185Sopenharmony_ci  }
7400aff185Sopenharmony_ci
7500aff185Sopenharmony_ci  public registerTimelineObserver(): void {
7600aff185Sopenharmony_ci    MediaObserver.getInstance().registerObserver(this.dataObserver);
7700aff185Sopenharmony_ci  }
7800aff185Sopenharmony_ci
7900aff185Sopenharmony_ci  public unregisterTimelineObserver(): void {
8000aff185Sopenharmony_ci    MediaObserver.getInstance().unregisterObserver(this.dataObserver);
8100aff185Sopenharmony_ci  }
8200aff185Sopenharmony_ci
8300aff185Sopenharmony_ci  onMediaLibDataChange(changeType: string): void {
8400aff185Sopenharmony_ci    Log.info(TAG, `onMediaLibDataChange type: ${changeType}`);
8500aff185Sopenharmony_ci    if (!this.isActive) {
8600aff185Sopenharmony_ci      this.pendingEmitCallbacks.clear();
8700aff185Sopenharmony_ci    }
8800aff185Sopenharmony_ci    this.switchRefreshOn();
8900aff185Sopenharmony_ci    this.onChange(changeType);
9000aff185Sopenharmony_ci  }
9100aff185Sopenharmony_ci
9200aff185Sopenharmony_ci  updateGroupData(requestTime: number, groups: TimelineData[]): void {
9300aff185Sopenharmony_ci    TraceControllerUtils.startTraceWithTaskId('updateGroupData', requestTime);
9400aff185Sopenharmony_ci    Log.info(TAG, 'updateGroupData begin');
9500aff185Sopenharmony_ci    this.lastUpdateTime = requestTime;
9600aff185Sopenharmony_ci
9700aff185Sopenharmony_ci    this.isPendingUpdateData = true;
9800aff185Sopenharmony_ci    this.pendingEmitCallbacks.execute(() => {
9900aff185Sopenharmony_ci      this.updateGroupSize(requestTime, groups);
10000aff185Sopenharmony_ci    })
10100aff185Sopenharmony_ci
10200aff185Sopenharmony_ci    TraceControllerUtils.finishTraceWithTaskId('updateGroupData', requestTime);
10300aff185Sopenharmony_ci    this.isPendingUpdateData = false;
10400aff185Sopenharmony_ci    this.pendingUpdateData.flush();
10500aff185Sopenharmony_ci
10600aff185Sopenharmony_ci  }
10700aff185Sopenharmony_ci
10800aff185Sopenharmony_ci  /**
10900aff185Sopenharmony_ci   * Update related variables of group count
11000aff185Sopenharmony_ci   *
11100aff185Sopenharmony_ci   * @param requestTime
11200aff185Sopenharmony_ci   * @param groups
11300aff185Sopenharmony_ci   */
11400aff185Sopenharmony_ci  updateGroupSize(requestTime: number, groups: TimelineData[]): void {
11500aff185Sopenharmony_ci    Log.info(TAG, 'updateGroupSize');
11600aff185Sopenharmony_ci    let previousSize: number = this.size;
11700aff185Sopenharmony_ci    let previousMediaCount = this.mediaCount;
11800aff185Sopenharmony_ci    this.groups = groups;
11900aff185Sopenharmony_ci    this.mCallbacks['updateGroupData'] && this.mCallbacks['updateGroupData'](this.groups)
12000aff185Sopenharmony_ci    this.size = 0;
12100aff185Sopenharmony_ci    this.mediaCount = 0;
12200aff185Sopenharmony_ci    this.dataIndexes = [];
12300aff185Sopenharmony_ci    this.layoutIndexes = [];
12400aff185Sopenharmony_ci    this.groupIndexes = [];
12500aff185Sopenharmony_ci    this.groupViewIndexes = [];
12600aff185Sopenharmony_ci    let dataIndex = 0;
12700aff185Sopenharmony_ci    for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
12800aff185Sopenharmony_ci      let group = groups[groupIndex];
12900aff185Sopenharmony_ci      this.mediaCount += group.count;
13000aff185Sopenharmony_ci
13100aff185Sopenharmony_ci      // title
13200aff185Sopenharmony_ci      this.size++;
13300aff185Sopenharmony_ci      this.dataIndexes.push(TITLE_DATA_INDEX);
13400aff185Sopenharmony_ci      this.groupIndexes.push(groupIndex);
13500aff185Sopenharmony_ci      this.groupViewIndexes.push(TITLE_DATA_INDEX);
13600aff185Sopenharmony_ci
13700aff185Sopenharmony_ci      // items
13800aff185Sopenharmony_ci      for (let i = 0; i < group.count; i++) {
13900aff185Sopenharmony_ci        this.dataIndexes.push(dataIndex);
14000aff185Sopenharmony_ci        this.groupIndexes.push(groupIndex);
14100aff185Sopenharmony_ci        this.layoutIndexes.push(this.size);
14200aff185Sopenharmony_ci        this.groupViewIndexes.push(i);
14300aff185Sopenharmony_ci        this.size++;
14400aff185Sopenharmony_ci        dataIndex++;
14500aff185Sopenharmony_ci      }
14600aff185Sopenharmony_ci    }
14700aff185Sopenharmony_ci
14800aff185Sopenharmony_ci    Log.info(TAG, `updateGroupSize, old size: ${previousSize} , old mediaCount: ${previousMediaCount},\
14900aff185Sopenharmony_ci            new size: ${this.size}, new mediaCount: ${this.mediaCount}, real size: ${this.realSize}`);
15000aff185Sopenharmony_ci
15100aff185Sopenharmony_ci    this.isCountChanged = previousSize != this.size;
15200aff185Sopenharmony_ci    this.isCountReduced = previousSize > this.size;
15300aff185Sopenharmony_ci    if (requestTime !== Constants.NUMBER_0) {
15400aff185Sopenharmony_ci      this.addedCount = (this.realSize > Constants.NUMBER_0) ? (this.size - this.realSize) : Constants.NUMBER_0;
15500aff185Sopenharmony_ci      this.realSize = this.size;
15600aff185Sopenharmony_ci    }
15700aff185Sopenharmony_ci    this.updateCountPostProcess();
15800aff185Sopenharmony_ci  }
15900aff185Sopenharmony_ci
16000aff185Sopenharmony_ci  emitCountUpdateCallbacks(): void {
16100aff185Sopenharmony_ci    this.pendingEmitCallbacks.execute(() => {
16200aff185Sopenharmony_ci      super.emitCountUpdateCallbacks();
16300aff185Sopenharmony_ci    })
16400aff185Sopenharmony_ci  }
16500aff185Sopenharmony_ci
16600aff185Sopenharmony_ci  updateCountThroughMediaItems(requestTime: number, mediaItems: MediaItem[]): void {
16700aff185Sopenharmony_ci    Log.info(TAG, 'updateCountThroughMediaItems');
16800aff185Sopenharmony_ci    this.updateGroupSize(0, this.getGroupDataThroughMediaItems(mediaItems));
16900aff185Sopenharmony_ci  }
17000aff185Sopenharmony_ci
17100aff185Sopenharmony_ci  // Get grouping information through media item
17200aff185Sopenharmony_ci  getGroupDataThroughMediaItems(mediaItems: MediaItem[]): TimelineData[] {
17300aff185Sopenharmony_ci    Log.info(TAG, 'getGroupDataThroughMediaItems');
17400aff185Sopenharmony_ci    let groupDataList: TimelineData[] = [];
17500aff185Sopenharmony_ci    if (mediaItems == null || mediaItems.length == 0) {
17600aff185Sopenharmony_ci      Log.error(TAG, 'getGroupDataThroughMediaItems, mediaItems are empty!');
17700aff185Sopenharmony_ci      return groupDataList;
17800aff185Sopenharmony_ci    }
17900aff185Sopenharmony_ci    let groupCount = 1;
18000aff185Sopenharmony_ci    let startTime = mediaItems[0].getDataTaken();
18100aff185Sopenharmony_ci    let endTime = mediaItems[0].getDataTaken();
18200aff185Sopenharmony_ci    for (let i = 1; i < mediaItems.length; i++) {
18300aff185Sopenharmony_ci      let dateTaken = mediaItems[i].getDataTaken();
18400aff185Sopenharmony_ci      if (DateUtil.isTheSameDay(startTime, dateTaken)) {
18500aff185Sopenharmony_ci        groupCount++;
18600aff185Sopenharmony_ci        endTime = dateTaken;
18700aff185Sopenharmony_ci      } else {
18800aff185Sopenharmony_ci        let groupData = new TimelineData(startTime, endTime, groupCount);
18900aff185Sopenharmony_ci        groupDataList.push(groupData);
19000aff185Sopenharmony_ci        groupCount = 1;
19100aff185Sopenharmony_ci        startTime = dateTaken;
19200aff185Sopenharmony_ci        endTime = dateTaken;
19300aff185Sopenharmony_ci      }
19400aff185Sopenharmony_ci    }
19500aff185Sopenharmony_ci    let groupData = new TimelineData(startTime, endTime, groupCount);
19600aff185Sopenharmony_ci    groupDataList.push(groupData);
19700aff185Sopenharmony_ci    return groupDataList;
19800aff185Sopenharmony_ci  }
19900aff185Sopenharmony_ci
20000aff185Sopenharmony_ci  // Packaging data for the view layer
20100aff185Sopenharmony_ci  getWrappedData(index: number): ViewData {
20200aff185Sopenharmony_ci    if (index < 0 || index >= this.dataIndexes.length) {
20300aff185Sopenharmony_ci      Log.error(TAG, `getWrappedData, index out of the total size, index: ${index},
20400aff185Sopenharmony_ci                total size: ${this.dataIndexes.length}`);
20500aff185Sopenharmony_ci      return undefined;
20600aff185Sopenharmony_ci    }
20700aff185Sopenharmony_ci    // title
20800aff185Sopenharmony_ci    if (this.dataIndexes[index] == TITLE_DATA_INDEX) {
20900aff185Sopenharmony_ci      let result: ViewData = {
21000aff185Sopenharmony_ci        viewType: ViewType.GROUP_TITLE,
21100aff185Sopenharmony_ci        viewData: this.groups[this.groupIndexes[index]],
21200aff185Sopenharmony_ci        viewIndex: this.groupIndexes[index],
21300aff185Sopenharmony_ci      };
21400aff185Sopenharmony_ci      Log.debug(TAG, `index: ${index}, type: ${result.viewType},\
21500aff185Sopenharmony_ci        data: ${result.viewData.startDate}, viewIndex: ${result.viewIndex}`);
21600aff185Sopenharmony_ci      return result;
21700aff185Sopenharmony_ci    } else {
21800aff185Sopenharmony_ci      let dataIndexInWindow = this.dataIndexes[index] - this.activeStart;
21900aff185Sopenharmony_ci      let result: ViewData;
22000aff185Sopenharmony_ci      if (dataIndexInWindow > this.items.length || dataIndexInWindow < 0) {
22100aff185Sopenharmony_ci        Log.error(TAG, 'index out of active window');
22200aff185Sopenharmony_ci        return undefined;
22300aff185Sopenharmony_ci      } else {
22400aff185Sopenharmony_ci        result = {
22500aff185Sopenharmony_ci          viewType: ViewType.ITEM,
22600aff185Sopenharmony_ci          mediaItem: this.getMediaItemSafely(dataIndexInWindow),
22700aff185Sopenharmony_ci          viewIndex: index,
22800aff185Sopenharmony_ci          indexInGroup: this.groupViewIndexes[index]
22900aff185Sopenharmony_ci        };
23000aff185Sopenharmony_ci      }
23100aff185Sopenharmony_ci      Log.debug(TAG, `index: ${index}, type: ${result.viewType},\
23200aff185Sopenharmony_ci        data: ${result.mediaItem.uri} indexInGroup: ${result.indexInGroup}`);
23300aff185Sopenharmony_ci      return result;
23400aff185Sopenharmony_ci    }
23500aff185Sopenharmony_ci  }
23600aff185Sopenharmony_ci
23700aff185Sopenharmony_ci  getPositionByIndex(index: number): number {
23800aff185Sopenharmony_ci    let pos = (this.dataIndexes || []).findIndex((item) => item === index);
23900aff185Sopenharmony_ci    Log.info(TAG, `pos ${index}, ${this.dataIndexes[pos]} , ${this.groupIndexes[pos]}`);
24000aff185Sopenharmony_ci    return this.dataIndexes[pos] + this.groupIndexes[pos] + 1;
24100aff185Sopenharmony_ci  }
24200aff185Sopenharmony_ci
24300aff185Sopenharmony_ci  getMediaItemByPosition(position: number): MediaItem {
24400aff185Sopenharmony_ci    // title
24500aff185Sopenharmony_ci    let index = position
24600aff185Sopenharmony_ci    if (this.dataIndexes[position] == TITLE_DATA_INDEX) {
24700aff185Sopenharmony_ci      index = index + 1
24800aff185Sopenharmony_ci    }
24900aff185Sopenharmony_ci    let dataIndexInWindow = this.dataIndexes[index] - this.activeStart;
25000aff185Sopenharmony_ci    if (dataIndexInWindow > this.items.length || dataIndexInWindow < 0) {
25100aff185Sopenharmony_ci      Log.error(TAG, 'index out of active window');
25200aff185Sopenharmony_ci      return undefined;
25300aff185Sopenharmony_ci    } else {
25400aff185Sopenharmony_ci      return this.getMediaItemSafely(dataIndexInWindow)
25500aff185Sopenharmony_ci    }
25600aff185Sopenharmony_ci  }
25700aff185Sopenharmony_ci
25800aff185Sopenharmony_ci  onPhotoBrowserActive(isActive: boolean, transition: string): void {
25900aff185Sopenharmony_ci    Log.debug(TAG, `onPhotoBrowserActive ${isActive}, ${transition}`);
26000aff185Sopenharmony_ci    if (transition == Constants.PHOTO_TRANSITION_TIMELINE) {
26100aff185Sopenharmony_ci      if (isActive) {
26200aff185Sopenharmony_ci        this.onActive();
26300aff185Sopenharmony_ci      } else {
26400aff185Sopenharmony_ci        this.onInActive();
26500aff185Sopenharmony_ci      }
26600aff185Sopenharmony_ci    } else if (transition == Constants.PHOTO_TRANSITION_EDIT) {
26700aff185Sopenharmony_ci      if (isActive) {
26800aff185Sopenharmony_ci        this.isEditSaveReload = true;
26900aff185Sopenharmony_ci        this.onActive();
27000aff185Sopenharmony_ci      } else {
27100aff185Sopenharmony_ci        this.isEditSaveReload = false;
27200aff185Sopenharmony_ci      }
27300aff185Sopenharmony_ci    }
27400aff185Sopenharmony_ci  }
27500aff185Sopenharmony_ci
27600aff185Sopenharmony_ci  onActive(): void {
27700aff185Sopenharmony_ci    super.onActive();
27800aff185Sopenharmony_ci    this.pendingEmitCallbacks.flush();
27900aff185Sopenharmony_ci  }
28000aff185Sopenharmony_ci
28100aff185Sopenharmony_ci  getGroupCountBeforeItem(item: MediaItem): number {
28200aff185Sopenharmony_ci    let groupCount = 0;
28300aff185Sopenharmony_ci    if (!item) {
28400aff185Sopenharmony_ci      return groupCount;
28500aff185Sopenharmony_ci    }
28600aff185Sopenharmony_ci    let itemTime: number = item.getDataTaken();
28700aff185Sopenharmony_ci    for (let index = 0; index < this.groups.length; index++) {
28800aff185Sopenharmony_ci      const group: TimelineData = this.groups[index];
28900aff185Sopenharmony_ci      if (DateUtil.isTheSameDay(itemTime, group.startDate)) {
29000aff185Sopenharmony_ci        groupCount = index + 1;
29100aff185Sopenharmony_ci        break;
29200aff185Sopenharmony_ci      }
29300aff185Sopenharmony_ci    }
29400aff185Sopenharmony_ci    return groupCount;
29500aff185Sopenharmony_ci  }
29600aff185Sopenharmony_ci}
297