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 {
17  Action,
18  BroadCast,
19  BroadCastConstants,
20  Constants,
21  DateUtil,
22  Log,
23  MediaDataSource,
24  MediaItem,
25  ScreenManager,
26  ThirdSelectManager,
27  UserFileManagerAccess
28} from '@ohos/common';
29import { IS_HORIZONTAL, THIRD_SELECT_IS_ORIGIN, THUMBNAIL_WIDTH } from '../utils/ThirdSelectConstants';
30
31const TAG: string = 'thiSel_ThirdSelectedPanel';
32
33interface WidthAndHeight {
34  height: number;
35  width: number;
36};
37
38@Component
39export struct ThirdSelectedPanel {
40  onMenuClicked: Function = (): void => {};
41  maxSelectCount: number = 0;
42  @Provide selectedCount: number = 0;
43  @Link @Watch('onSelectedCountChanged') totalSelectedCount: number;
44  selectManager: ThirdSelectManager | null = null;
45  @State itemArray: Array<MediaItem> = [];
46  @StorageLink(THIRD_SELECT_IS_ORIGIN) isOriginalChecked: boolean = false;
47  @StorageLink(IS_HORIZONTAL) isHorizontal: boolean = ScreenManager.getInstance().isHorizontal();
48  @Consume broadCast: BroadCast;
49  @Link isShowBar: boolean;
50  @Prop currentUri: string = '';
51  isBrowserMode: boolean = false;
52  isMultiPick = true;
53  mTransition: string = '';
54  isFromFa: boolean = false;
55  dataSource: MediaDataSource | null = null;
56  private selectedScroller: Scroller = new Scroller();
57
58  aboutToAppear(): void {
59    this.selectManager = AppStorage.get<ThirdSelectManager>(Constants.THIRD_SELECT_MANAGER) as ThirdSelectManager;
60    this.onSelectedCountChanged();
61  }
62
63  onSelectedCountChanged(): void {
64    this.selectedCount = this.totalSelectedCount;
65    this.itemArray = this.selectManager == null ? [] : this.selectManager.getSelectItems();
66    Log.debug(TAG, `call scroll to edge, current count ${this.itemArray.length} this.selectedCount is: ${this.selectedCount}`);
67    this.selectedScroller.scrollEdge(Edge.End);
68  }
69
70  refreshSelectData(refresh: boolean): void {
71    this.selectedCount = this.totalSelectedCount;
72    this.itemArray = this.selectManager == null ? [] : this.selectManager.getSelectItems();
73    Log.debug(TAG, `call scroll to edge, current count ${this.itemArray.length}`)
74    this.selectedScroller.scrollEdge(Edge.End);
75  }
76
77  getThumbnailSafe(sourceUri: string, path: string, size?: WidthAndHeight): string {
78    try {
79      if (size) {
80        if (size.width != 0 && size.height != 0) {
81          return `${sourceUri}?oper=thumbnail&width=${size.width}&height=${size.height}&path=${path}`;
82        } else {
83          Log.warn(TAG, 'getThumbnailSafe with width==0 and height==0, so do not use thumbnail' + JSON.stringify(size));
84          return `${sourceUri}`;
85        }
86      } else {
87        return `${sourceUri}?oper=thumbnail&width=${THUMBNAIL_WIDTH}&height=${THUMBNAIL_WIDTH}&path=${path}`;
88      }
89    } catch (err) {
90      Log.warn(TAG, `get Thumbnail Failed! msg:${err}`);
91      return '';
92    }
93  }
94
95  @Builder
96  buildRadio() {
97    if (this.isBrowserMode) {
98      Image(this.isOriginalChecked ? $r('app.media.picker_radio_selected') : $r('app.media.picker_radio_unselected'))
99        .key('Original')
100        .width($r('app.float.icon_size'))
101        .aspectRatio(1)
102        .onClick((): void => this.clickOriginButton())
103    } else {
104      Radio({ value: '', group: this.mTransition })
105        .key('Original')
106        .checked(this.isOriginalChecked)
107        .margin(0)
108        .onClick((): void => this.clickOriginButton())
109    }
110  }
111
112  @Builder
113  buildTitle() {
114    Stack() {
115      Row() {
116        Text($r('app.string.selected_photos_count', this.selectedCount, this.maxSelectCount))
117          .fontSize($r('sys.float.ohos_id_text_size_body1'))
118          .fontFamily($r('app.string.id_text_font_family_regular'))
119          .fontColor(this.selectTextColor())
120          .fontWeight(FontWeight.Regular)
121          .key('currentSelectCount')
122
123        Button($r('app.string.complete'))
124          .key('Complete')
125          .enabled(this.isButtonEnabled() ? true : false)
126          .opacity(this.isButtonEnabled() ? 1 : $r('app.float.disable_button_opacity'))
127          .fontSize($r('sys.float.ohos_id_text_size_button3'))
128          .fontFamily($r('app.string.id_text_font_family_regular'))
129          .backgroundColor(this.isBrowserMode ? $r('sys.color.ohos_id_color_activated_dark') : $r('sys.color.ohos_id_color_activated'))
130          .width($r('app.float.picker_panel_button_width'))
131          .height($r('app.float.picker_panel_button_height'))
132          .fontWeight(FontWeight.Medium)
133          .onClick(() => {
134            this.onMenuClicked(Action.OK);
135          })
136      }
137      .width('100%')
138      .height($r('app.float.third_selected_panel_title_height'))
139      .alignItems(VerticalAlign.Center)
140      .justifyContent(FlexAlign.SpaceBetween)
141      .padding({
142        left: $r('sys.float.ohos_id_max_padding_start'),
143        right: $r('sys.float.ohos_id_max_padding_end')
144      })
145
146      Row() {
147        this.buildRadio()
148        Text($r('app.string.filter_original_text'))
149          .fontSize($r('sys.float.ohos_id_text_size_body1'))
150          .fontFamily($r('app.string.id_text_font_family_regular'))
151          .fontColor(this.isBrowserMode ? $r('sys.color.ohos_id_color_text_primary_dark') : $r('sys.color.ohos_id_color_text_primary'))
152          .fontWeight(FontWeight.Regular)
153          .margin({ left: $r('app.float.third_selected_toggle_icon_margin_right') })
154      }
155      .align(Alignment.Center)
156      .alignItems(VerticalAlign.Center)
157      .visibility(this.isFromFa ? Visibility.Hidden : Visibility.Visible)
158    }
159  }
160
161  @Builder
162  buildThumbnailItem(item: MediaItem, index: number) {
163    Stack({ alignContent: Alignment.Start }) {
164      Image(item?.thumbnail)
165        .height('100%')
166        .aspectRatio(1)
167        .objectFit(ImageFit.Cover)
168        .autoResize(false)
169        .onError(() => {
170          Log.error(TAG, 'item Image error');
171        })
172        .onComplete(() => {
173          Log.debug(TAG, `Draw the image! ${item?.uri}`);
174        })
175        .onClick(() => {
176          this.broadCast.emit(BroadCastConstants.JUMP_THIRD_PHOTO_BROWSER, [
177            '',
178            item,
179          this.getPosition(item),
180            'thiSel_ThirdSelectPhotoGridBase' + item?.getHashCode() + true,
181            true
182          ]);
183        })
184      // video duration
185      if (item?.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
186        Row() {
187          Text(DateUtil.getFormattedDuration(item?.duration))
188            .fontSize($r('sys.float.ohos_id_text_size_caption'))
189            .fontFamily($r('app.string.id_text_font_family_regular'))
190            .fontColor($r('app.color.text_color_above_picture'))
191            .lineHeight(12)
192            .margin({
193              left: $r('app.float.grid_item_text_margin_lr'),
194              bottom: $r('app.float.grid_item_text_margin_bottom')
195            })
196        }
197        .height('100%')
198        .width('100%')
199        .hitTestBehavior(HitTestMode.None)
200        .alignItems(VerticalAlign.Bottom)
201      }
202      Image($r('app.media.ic_gallery_public_cancel_bg'))
203        .height($r('app.float.icon_size'))
204        .width($r('app.float.icon_size'))
205        .key('ThirdSelectCancel' + item?.uri)
206        .objectFit(ImageFit.Contain)
207        .position({
208          x: $r('app.float.picker_panel_cancel_x'),
209          y: $r('app.float.picker_panel_cancel_y')
210        })
211        .onClick(() => {
212          Log.debug(TAG, `click cancel item ${item?.uri}`);
213          this.broadCast.emit(BroadCastConstants.SELECT, [0, item?.uri, false]);
214        })
215      Image($r('app.media.picker_border_img'))
216        .height('100%')
217        .aspectRatio(1)
218        .fillColor($r('sys.color.ohos_id_color_focused_bg_dark'))
219        .objectFit(ImageFit.Cover)
220        .autoResize(false)
221        .visibility(item?.uri === this.currentUri ? Visibility.Visible : Visibility.None)
222        .hitTestBehavior(HitTestMode.None)
223    }
224    .height('100%')
225    .aspectRatio(1)
226  }
227
228  @Builder
229  buildThumbnailList() {
230    List({ scroller: this.selectedScroller, space: 8 }) {
231      ForEach(this.itemArray, (item: MediaItem, index?: number) => {
232        ListItem() {
233          this.buildThumbnailItem(item, index as number);
234        }
235        .width($r('app.float.third_selected_panel_image_height'))
236        .aspectRatio(1)
237        .margin({
238          left: index == 0 ? $r('sys.float.ohos_id_max_padding_start') : 0,
239          right: index == this.itemArray.length - 1 ? $r('sys.float.ohos_id_max_padding_end') : 0
240        })
241      }, (item: MediaItem) => item?.getHashCode())
242    }
243    .width('100%')
244    .height('100%')
245    .listDirection(Axis.Horizontal)
246    .edgeEffect(EdgeEffect.Spring)
247    .scrollBar(BarState.Off)
248  }
249
250  @Builder
251  buildDefault() {
252    Column() {
253      Text($r('app.string.select_items_to_add'))
254        .fontSize($r('sys.float.ohos_id_text_size_body2'))
255        .fontFamily($r('app.string.id_text_font_family_regular'))
256        .fontColor(this.isBrowserMode ? $r('sys.color.ohos_id_color_secondary_dark') : $r('sys.color.ohos_id_color_secondary'))
257        .fontWeight(FontWeight.Regular)
258    }
259    .width('100%')
260    .height($r('app.float.third_selected_panel_empty_height'))
261    .justifyContent(FlexAlign.Center)
262    .alignItems(HorizontalAlign.Center)
263    .margin({
264      top: $r('app.float.third_selected_panel_image_padding_top'),
265    })
266    .padding({
267      left: $r('sys.float.ohos_id_max_padding_start'),
268      right: $r('sys.float.ohos_id_max_padding_end')
269    })
270  }
271
272  build() {
273    Column() {
274      this.buildTitle()
275
276      if (this.isMultiPick) {
277        if (this.selectedCount > 0) {
278          Row() {
279            this.buildThumbnailList()
280          }
281          .width('100%')
282          .padding({
283            top: $r('app.float.third_selected_panel_image_padding_top'),
284            bottom: $r('sys.float.ohos_id_default_padding_bottom_fixed')
285          })
286        } else {
287          this.buildDefault()
288        }
289      }
290    }
291    .width('100%')
292    .height(this.isMultiPick ? $r('app.float.third_selected_panel_height') : $r('app.float.third_selected_panel_title_height'))
293    .backgroundColor(this.isBrowserMode ? $r('sys.color.ohos_id_color_card_bg_dark') : $r('sys.color.ohos_id_color_card_bg'))
294    .sharedTransition('ThirdSelectPhotoBrowserActionBar', {
295      curve: Curve.Linear,
296      zIndex: Constants.NUMBER_1,
297      type: SharedTransitionEffectType.Static
298    })
299    .visibility(this.isShowBar ? Visibility.Visible : Visibility.Hidden)
300  }
301
302  private getPosition(item: MediaItem): number {
303    if (this.dataSource) {
304      return this.dataSource.getDataIndex(item) + this.dataSource.getGroupCountBeforeItem(item);
305    } else {
306      return Constants.NOT_FOUND;
307    }
308  }
309
310  private selectTextColor() {
311    let normalColor = this.isBrowserMode ?
312    $r('sys.color.ohos_id_color_text_primary_dark') : $r('sys.color.ohos_id_color_text_primary');
313    if (!this.isHorizontal) {
314      return normalColor;
315    }
316    let warnColor = this.isBrowserMode ?
317    $r('sys.color.ohos_id_color_warning_dark') : $r('sys.color.ohos_id_color_warning');
318    return this.isMultiPick && this.selectedCount >= this.maxSelectCount ? warnColor : normalColor;
319  }
320
321  private isButtonEnabled(): boolean {
322    return !this.isMultiPick || this.selectedCount > 0;
323  }
324
325  private clickOriginButton(): void {
326    this.isOriginalChecked = !this.isOriginalChecked;
327    Log.info(TAG, `origin clicked: ${this.isOriginalChecked}`);
328  }
329}