1/*
2 * Copyright (c) 2022 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 type { SmartPickerDataInterface } from '../interface/SmartPickerDataInterface';
17import type common from '@ohos.app.ability.common';
18import SmartPickerRecommendInfo from '../common/SmartPickerRecommendInfo';
19import SmartPickerConstants from '../common/SmartPickerConstants';
20import { Log } from '@ohos/common/src/main/ets/default/utils/Log';
21import dataSharePredicates from '@ohos.data.dataSharePredicates';
22import type { AsyncCallback } from '@ohos/common/src/main/ets/default/model/common/AsyncCallback';
23import { MediaItem } from '@ohos/common/src/main/ets/default/model/browser/photo/MediaItem';
24import type { QueryParam } from '@ohos/common/src/main/ets/default/model/browser/BrowserDataImpl';
25import { SmartPickerUtils } from '../utils/SmartPickerUtils';
26import { Constants } from '@ohos/common/src/main/ets/default/model/common/Constants';
27import photoAccessHelper from '@ohos.file.photoAccessHelper';
28import type SmartPickerDataAdapter from '../common/SmartPickerDataAdapter';
29import type { RecommendationOptions } from '../common/SmartPickerManager';
30import { StringUtil } from '@ohos/common/src/main/ets/default/utils/StringUtil';
31
32const TAG: string = 'SmartPickerPhotosDataImpl';
33
34export class SmartPickerPhotosDataImpl implements SmartPickerDataInterface {
35  private uriLabelAlbumMap: Map<string, photoAccessHelper.Album> = new Map<string, photoAccessHelper.Album>();
36  private labelIdNameMap: Map<string, string> = new Map<string, string>();
37  private context: common.Context;
38  private dataAdapter: SmartPickerDataAdapter;
39
40  constructor(context: common.Context, dataAdapter: SmartPickerDataAdapter) {
41    this.context = context;
42    this.dataAdapter = dataAdapter;
43  }
44
45  private resetData(): void {
46    this.uriLabelAlbumMap.clear();
47    this.labelIdNameMap.clear();
48  }
49
50  async getTabInfoList(recommendationOptions: RecommendationOptions, param?: string): Promise<Array<SmartPickerRecommendInfo>> {
51    this.resetData();
52    let recommendationType = recommendationOptions.recommendationType;
53    if (recommendationType >= SmartPickerConstants.QR_OR_BAR_CODE && recommendationType <= SmartPickerConstants.PROFILE_PICTURE) {
54      this.addLabelIdNameInfo(recommendationType);
55    }
56    if (this.labelIdNameMap.size <= 0) {
57      Log.error(TAG, 'getTabInfoList labelIdNameMap size is 0');
58      return [];
59    }
60    return this.getLabelAlbumTabInfoList();
61  }
62
63  private addLabelIdNameInfo(recommendationType?: number): void {
64    try {
65      if (recommendationType !== undefined) {
66        switch (recommendationType) {
67          case SmartPickerConstants.QR_OR_BAR_CODE:
68            this.labelIdNameMap.set(SmartPickerConstants.LABEL_QR_CODE, SmartPickerConstants.NAME_QR_CODE);
69            this.labelIdNameMap.set(SmartPickerConstants.LABEL_BAR_CODE, SmartPickerConstants.NAME_BAR_CODE);
70            break;
71          case SmartPickerConstants.QR_CODE:
72            this.labelIdNameMap.set(SmartPickerConstants.LABEL_QR_CODE, SmartPickerConstants.NAME_QR_CODE);
73            break;
74          case SmartPickerConstants.BAR_CODE:
75            this.labelIdNameMap.set(SmartPickerConstants.LABEL_BAR_CODE, SmartPickerConstants.NAME_BAR_CODE);
76            break;
77          case SmartPickerConstants.ID_CARD:
78            this.labelIdNameMap.set(SmartPickerConstants.LABEL_ID_CARD, SmartPickerConstants.NAME_ID_CARD);
79            break;
80          case SmartPickerConstants.PROFILE_PICTURE:
81            this.labelIdNameMap.set(SmartPickerConstants.LABEL_AVATAR, SmartPickerConstants.NAME_AVATAR);
82            break;
83        }
84      }
85    } catch (err) {
86      Log.error(TAG, 'addLabelIdNameInfo err:' + err);
87    }
88  }
89
90  private buildFetchOptions(): photoAccessHelper.FetchOptions {
91    let dataPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
92    let labelAlbumFetchOpt: photoAccessHelper.FetchOptions = {
93      fetchColumns: [],
94      predicates: dataPredicates
95    };
96    return labelAlbumFetchOpt;
97  }
98
99  async getLabelAlbumTabInfoList(): Promise<Array<SmartPickerRecommendInfo>> {
100    try {
101      let photoHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
102      let albumFetOpt = this.buildFetchOptions();
103      albumFetOpt.predicates.in(SmartPickerConstants.LABEL_ID_KEY, Array.from(this.labelIdNameMap.keys()));
104      albumFetOpt.predicates.orderByAsc(SmartPickerConstants.LABEL_ID_KEY);
105      //@ts-ignore
106      let albumsFetchResult = await photoHelper.getAlbums(photoAccessHelper.AlbumKeys.SMART,
107        //@ts-ignore
108        photoAccessHelper.AlbumSubtype.CLASSIFY, albumFetOpt);
109      if (albumsFetchResult === undefined || albumsFetchResult === null || albumsFetchResult.getCount() <= 0) {
110        this.labelIdNameMap.clear();
111        Log.error(TAG, 'albumsFetchResult is invalid');
112        return [];
113      }
114      let albumList = await albumsFetchResult.getAllObjects();
115      albumsFetchResult.close();
116      let pickerRecommendInfoList: SmartPickerRecommendInfo[] = [];
117      for (let i = 0; i < albumList.length; i++) {
118        let album: photoAccessHelper.Album = albumList[i];
119        if (album.count > 0) {
120          let tabInfo = this.buildTabInfoByAlbum(album);
121          pickerRecommendInfoList.push(tabInfo);
122          this.uriLabelAlbumMap.set(album.albumUri, album);
123        } else {
124          this.labelIdNameMap.delete(album.albumName);
125        }
126      }
127      return pickerRecommendInfoList;
128    } catch (err) {
129      Log.error(TAG, 'getLabelAlbumTabInfoList err' + err);
130    }
131    return [];
132  }
133
134  private buildTabInfoByAlbum(album: photoAccessHelper.Album): SmartPickerRecommendInfo {
135    let tabInfo = new SmartPickerRecommendInfo();
136    tabInfo.setAlbumUri(album.albumUri);
137    tabInfo.setRecommendType(SmartPickerConstants.RECOMMEND_TYPE_LABEL);
138    tabInfo.setLabelId(album.albumName);
139    tabInfo.setTotalCount(album.count);
140    return tabInfo;
141  }
142
143  getData(callback: AsyncCallback<MediaItem[]>, param: QueryParam): void {
144    let albumUri = param.albumUri;
145    let start = param.start;
146    let count = param.count;
147    let filterMediaType = param.filterMediaType;
148    this.getLabelPhotoData(callback, albumUri, start, count, filterMediaType);
149  }
150
151  private getLabelPhotoData(callback: AsyncCallback<MediaItem[]>, albumUri: string, startIndex?: number, count?: number, filterMediaType?: string): void {
152    if (StringUtil.isEmpty(albumUri)) {
153      callback.callback([], albumUri);
154      return;
155    }
156    try {
157      let labelPhotoFetchOption = this.buildFetchOptions();
158      labelPhotoFetchOption.predicates.limit(count, startIndex);
159      labelPhotoFetchOption.fetchColumns = Array.prototype.slice.call(SmartPickerConstants.ALL_IMAGE_VIDEO_FETCH_COLUMNS) as string[];
160      let album = this.uriLabelAlbumMap.get(albumUri);
161      this.getPhotoAssetDataByAlbum(album, labelPhotoFetchOption).then(async (mediaItemList: Array<MediaItem>) => {
162        if (mediaItemList) {
163          callback.callback(mediaItemList, albumUri);
164        } else {
165          callback.callback([], albumUri);
166        }
167      });
168    } catch (err) {
169      Log.error(TAG, 'getLabelPhotoData err:' + err);
170      callback.callback([], albumUri);
171    }
172  }
173
174  async getPhotoAssetDataByAlbum(album: photoAccessHelper.Album, fileFetchOpt: photoAccessHelper.FetchOptions): Promise<Array<MediaItem>> {
175    if (album === null || album === undefined) {
176      Log.error(TAG, 'getPhotoAssetDataByAlbum album is invalid');
177      return [];
178    }
179    try {
180      let photoFetchResult = await album.getAssets(fileFetchOpt);
181      let fileAssetList = await photoFetchResult.getAllObjects();
182      photoFetchResult.close();
183      let mediaItemList: MediaItem[] = [];
184      for (let photoAsset of fileAssetList) {
185        let mediaItem: MediaItem = new MediaItem(photoAsset);
186        if (this.dataAdapter) {
187          mediaItem.setThumbnail(SmartPickerUtils.getThumbnailSafe(photoAsset.uri, String(photoAsset.get(Constants.KEY_FILE_DATA))));
188          mediaItemList.push(mediaItem);
189        }
190      }
191      return mediaItemList;
192    } catch (err) {
193      Log.error(TAG, 'getPhotoAssetDataByAlbum err:' + err);
194    }
195    return [];
196  }
197
198  async getDataCount(albumUri: string, filterMediaType?: string): Promise<number> {
199    let count = 0;
200    if (!StringUtil.isEmpty(albumUri) && this.uriLabelAlbumMap.get(albumUri) !== undefined) {
201      let album = this.uriLabelAlbumMap.get(albumUri);
202      if (!filterMediaType) {
203        return album.count;
204      }
205      let fetchOpt = this.buildFetchOptions();
206      fetchOpt.fetchColumns = Array.prototype.slice.call(SmartPickerConstants.ALL_IMAGE_VIDEO_FETCH_COLUMNS) as string[];
207      let fetResult = await album.getAssets(fetchOpt);
208      count = fetResult.getCount();
209      fetResult.close();
210    }
211    return count;
212  }
213}