• 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 router from '@ohos.router';
17import {
18  Action,
19  BigDataConstants,
20  BroadCast,
21  BroadCastConstants,
22  BroadCastManager,
23  BrowserConstants,
24  BrowserController,
25  Constants,
26  Log,
27  MediaItem,
28  mMultimodalInputManager,
29  PhotoBrowserBg,
30  PhotoDataSource,
31  PhotoSwiper,
32  ReportToBigDataUtil,
33  ScreenManager,
34  SelectUtil,
35  ThirdSelectManager,
36  ThirdSelectPhotoBrowserActionBar,
37} from '@ohos/common';
38import { FormConstants, IS_HORIZONTAL } from '../utils/ThirdSelectConstants';
39import { ThirdSelectedPanel } from './ThirdSelectedPanel';
40import { MouseTurnPageOperation } from '@ohos/browser';
41
42const TAG: string = 'thiSel_ThirdSelectPhotoBrowserBase';
43
44// third selection photoBrowser
45@Component
46export struct ThirdSelectPhotoBrowserBase {
47  @Provide backgroundColorResource: Resource = $r('app.color.default_background_color');
48  @State totalSelectedCount: number = 0;
49  @Provide broadCast: BroadCast = new BroadCast();
50  @Provide isSelected: boolean = true;
51  @State isShowBar: boolean = true;
52  @Provide isDefaultBackgroundColor: boolean = true;
53  @State isPhotoScaled: boolean = false;
54  @Provide pageFrom: number = Constants.ENTRY_FROM.NORMAL;
55  selectManager: ThirdSelectManager;
56  bundleName: string = '';
57  isMultiPick = true;
58  mTransition: string;
59  controller: SwiperController = new SwiperController();
60  @Provide('transitionIndex') currentIndex: number = 0;
61  @State currentUri: string = '';
62  isFromFa: boolean = false;
63  @Provide canSwipe: boolean = true;
64  // position
65  mPosition: number;
66  @State title: string = '';
67  @Prop @Watch('onPageChanged') pageStatus: boolean;
68  @StorageLink(IS_HORIZONTAL) isHorizontal: boolean = ScreenManager.getInstance().isHorizontal();
69  maxSelectCount: number;
70  @StorageLink('geometryOpacity') geometryOpacity: number = 1;
71  @State @Watch('onGeometryChanged') geometryTransitionId: string = 'default_id';
72  @Link isRunningAnimation: boolean;
73  @ObjectLink browserController: BrowserController;
74  @Provide isDeleting: boolean = false;
75  // DataSource
76  private dataSource: ThirdBrowserDataSource = new ThirdBrowserDataSource();
77  private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
78  private geometryTransitionEnable: boolean = false;
79  private isSelectMode: boolean;
80
81  onGeometryChanged() {
82    AppStorage.SetOrCreate<string>('geometryTransitionBrowserId', this.geometryTransitionId);
83  }
84
85  aboutToAppear(): void {
86    Log.info(TAG, 'photoBrowser aboutToAppear');
87    this.backgroundColorResource = $r('app.color.black');
88    this.isDefaultBackgroundColor = false;
89    this.geometryTransitionId = AppStorage.Get('geometryTransitionBrowserId');
90    this.browserController.browserBackFunc = this.onBackPress.bind(this);
91    mMultimodalInputManager.registerListener((control: number) => {
92      Log.info(TAG, `key control : ${control} index ${this.currentIndex}`);
93      if (control == 0) {
94        if (this.currentIndex > 0) {
95          this.onPhotoChanged(this.currentIndex - 1);
96        }
97      } else if (control == 1) {
98        if (this.currentIndex < this.dataSource.totalCount() - 1) {
99          this.onPhotoChanged(this.currentIndex + 1);
100        }
101      } else {
102        this.onBackPress();
103      }
104    });
105    this.selectManager = AppStorage.Get(Constants.THIRD_SELECT_MANAGER);
106    this.dataSource.setAlbumDataSource(AppStorage.Get(Constants.APP_KEY_PHOTO_BROWSER));
107    this.isMultiPick = this.selectManager.getIsMultiPick();
108    if (this.isMultiPick == true) {
109      this.totalSelectedCount = this.selectManager.getSelectedCount();
110    } else {
111      this.totalSelectedCount = Constants.NUMBER_1;
112    }
113
114    let param: any = this.browserController.browserParam;
115    this.isFromFa = param.isFromFa;
116    this.isSelectMode = param.selectMode;
117    if (param.selectMode) {
118      this.dataSource.setSelectMode(this.selectManager);
119    }
120    this.onPhotoChanged(param.position);
121    this.mTransition = param.transition;
122    this.bundleName = param.bundleName;
123    this.title = param.title;
124    this.maxSelectCount = param.maxSelectCount;
125    this.onMenuClicked = this.onMenuClicked.bind(this);
126
127    this.dataSource.setBroadCast(this.broadCast);
128
129    this.broadCast.on(BrowserConstants.PULL_DOWN_END, this.onBackPress.bind(this));
130    this.broadCast.on(BrowserConstants.DATA_SIZE_CHANGED, this.onDataSizeChanged.bind(this));
131    this.broadCast.on(BroadCastConstants.SELECT, this.selectCallback.bind(this));
132    this.broadCast.on(BrowserConstants.DATA_CONTENT_CHANGED, this.onPhotoChanged.bind(this, this.currentIndex));
133    this.broadCast.on(BroadCastConstants.JUMP_THIRD_PHOTO_BROWSER, this.jumpBrowserCallback.bind(this));
134    this.broadCast.on(BrowserConstants.SET_DISABLE_SWIPE, (value: boolean) => {
135      Log.info(TAG, `set swiper swipe ${value}`);
136      this.canSwipe = value;
137    });
138    if (this.pageStatus) {
139      this.onPageShow();
140    }
141  }
142
143  aboutToDisappear(): void {
144    this.broadCast.release();
145    this.dataSource.release();
146    mMultimodalInputManager.unregisterListener();
147    this.controller = undefined;
148  }
149
150  onDataSizeChanged(size: number): void {
151    Log.info(TAG, `onDataSizeChanged, size is ${size}`);
152    if (size == 0) {
153      this.onBackPress();
154    }
155  }
156
157  onPhotoChanged(index: number): void {
158    this.currentIndex = index;
159    let currentPhoto = this.getCurrentPhoto();
160    if (currentPhoto == undefined) {
161      Log.error(TAG, 'onPhotoChanged, item is undefined');
162    } else {
163      this.isSelected = this.selectManager.isItemSelected(currentPhoto.uri);
164      this.currentUri = currentPhoto.uri;
165
166      let dataSourceIndex = this.isSelectMode ? this.selectManager.getSelectItemDataSourceIndex(currentPhoto) : index;
167      let timelineIndex = this.dataSource.getPositionByIndex(dataSourceIndex);
168      AppStorage.SetOrCreate<number>('placeholderIndex', timelineIndex);
169      this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + this.isSelected;
170      Log.info(TAG, `onPhotoChanged, index: ${index}, currentPhoto: ${currentPhoto.uri}, \
171        geometryTransitionId = ${this.geometryTransitionId}, placeholderIndex = ${timelineIndex}`);
172    }
173  }
174
175  selectStateChange() {
176    Log.info(TAG, 'change selected.');
177    let currentPhoto = this.getCurrentPhoto();
178    if (currentPhoto == undefined) {
179      return;
180    }
181    this.isSelected = !this.isSelected;
182    if (this.isSelected) {
183      this.selectManager.toggle(currentPhoto.uri, true);
184    } else {
185      this.selectManager.toggle(currentPhoto.uri, false);
186    }
187    this.totalSelectedCount = this.selectManager.getSelectedCount();
188    this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + this.isSelected;
189    Log.info(TAG, `totalSelectedCount: ${this.totalSelectedCount} after state change geometryTransitionId ${this.geometryTransitionId}`);
190  }
191
192  selectCallback(position: number, key: string, value: boolean) {
193    if (key === this.currentUri) {
194      this.isSelected = value;
195    }
196    if (this.selectManager) {
197      this.selectManager.toggle(key, value);
198    }
199    this.totalSelectedCount = this.selectManager.getSelectedCount();
200    Log.info(TAG, `totalSelectedCount: ${this.totalSelectedCount} after select callback`);
201  }
202
203  onPageChanged() {
204    if (this.pageStatus) {
205      this.onPageShow();
206    } else {
207      this.onPageHide();
208    }
209  }
210
211  onPageShow() {
212    Log.debug(TAG, 'onPageShow');
213    this.appBroadCast.emit(BroadCastConstants.THIRD_ROUTE_PAGE, []);
214    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [true, this.mTransition]);
215  }
216
217  onPageHide() {
218    Log.debug(TAG, 'onPageHide');
219    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [false, this.mTransition]);
220  }
221
222  onMenuClicked(action: Action) {
223    Log.info(TAG, `onMenuClicked, action: ${action.actionID}`);
224    switch (action.actionID) {
225      case Action.BACK.actionID:
226        let msg = {
227          'From': BigDataConstants.BY_CLICK,
228        }
229        ReportToBigDataUtil.report(BigDataConstants.ESC_PHOTO_BROWSER_WAY, msg);
230        this.onBackPress();
231        return;
232      case Action.MATERIAL_SELECT.actionID:
233        Log.info(TAG, 'click UN_SELECTED');
234        this.selectStateChange();
235        return;
236      case Action.SELECTED.actionID:
237        Log.info(TAG, 'click SELECTED');
238        this.selectStateChange();
239        return;
240      case Action.OK.actionID:
241        Log.info(TAG, 'click OK');
242        this.setPickResult();
243        break;
244      default:
245        break;
246    }
247  }
248
249  getCurrentPhoto(): MediaItem {
250    Log.debug(TAG, 'getCurrentPhoto  ' + this.currentIndex);
251    return this.dataSource.getData(this.currentIndex)?.data;
252  }
253
254  onBackPress() {
255    if (this.geometryTransitionEnable) {
256      this.controller.finishAnimation(this.onBackPressInner.bind(this));
257    } else {
258      router.back({
259        url: '',
260        params: { index: this.currentIndex }
261      });
262    }
263    return true;
264  }
265
266  @Builder buildCheckBox() {
267    if (this.isMultiPick) {
268      Row() {
269        Image(this.isSelected ? $r('app.media.picker_checkbox_selected_dark') : $r('app.media.picker_checkbox_unselected_dark'))
270          .width($r('app.float.icon_size'))
271          .aspectRatio(1)
272          .key('Checkbox_' + this.currentIndex)
273          .margin({
274            right: $r('sys.float.ohos_id_max_padding_end'),
275            bottom: $r('app.float.picker_browser_checkbox_margin_bottom')
276          })
277          .onClick(() => {
278            this.selectStateChange();
279          })
280      }
281      .justifyContent(FlexAlign.End)
282      .width('100%')
283      .visibility(this.isShowBar ? Visibility.Visible : Visibility.Hidden)
284      .opacity(this.geometryOpacity)
285      // @ts-ignore
286      .transition(TransitionEffect.opacity(0))
287      .hitTestBehavior(HitTestMode.Transparent)
288    }
289  }
290
291  @Builder buildPanel() {
292    ThirdSelectedPanel({
293      maxSelectCount: this.maxSelectCount,
294      onMenuClicked: this.onMenuClicked,
295      isBrowserMode: true,
296      isMultiPick: this.isMultiPick,
297      mTransition: TAG,
298      isFromFa: this.isFromFa,
299      currentUri: this.currentUri,
300      isShowBar: $isShowBar,
301      totalSelectedCount: $totalSelectedCount
302    })
303      .opacity(this.geometryOpacity)
304        // @ts-ignore
305      .transition(TransitionEffect.opacity(0))
306      .hitTestBehavior(HitTestMode.Transparent)
307  }
308
309  build() {
310    Stack({ alignContent: Alignment.Bottom }) {
311      Stack({ alignContent: Alignment.TopStart }) {
312        PhotoBrowserBg({ isShowBar: $isShowBar })
313          .opacity(this.geometryOpacity)
314            // @ts-ignore
315          .transition(TransitionEffect.opacity(0))
316
317        PhotoSwiper({
318          dataSource: this.dataSource,
319          mTransition: this.mTransition,
320          onPhotoChanged: this.onPhotoChanged.bind(this),
321          swiperController: this.controller,
322          verifyPhotoScaledFunc: this.verifyPhotoScaled.bind(this),
323          geometryTransitionEnable: true,
324          broadCast: $broadCast,
325          isRunningAnimation: $isRunningAnimation
326        })
327
328        if (this.isHorizontal) {
329          MouseTurnPageOperation({
330            dataSource: this.dataSource,
331            controller: this.controller,
332            isPhotoScaled: this.isPhotoScaled,
333            isShowBar: this.isShowBar
334          })
335            .opacity(this.geometryOpacity)
336              // @ts-ignore
337            .transition(TransitionEffect.opacity(0))
338            .hitTestBehavior(HitTestMode.Transparent)
339
340        }
341        ThirdSelectPhotoBrowserActionBar({
342          isMultiPick: this.isMultiPick,
343          onMenuClicked: this.onMenuClicked,
344          title: this.title,
345          isThird: true,
346          isShowBar: $isShowBar,
347          totalSelectedCount: $totalSelectedCount
348        })
349          .opacity(this.geometryOpacity)
350            // @ts-ignore
351          .transition(TransitionEffect.opacity(0))
352          .hitTestBehavior(HitTestMode.Transparent)
353      }
354
355      this.buildCheckBox()
356      this.buildPanel()
357    }
358    .padding({ bottom: $r('app.float.title_default') })
359  }
360
361  pageTransition() {
362    PageTransitionEnter({ type: RouteType.None, duration: BrowserConstants.PAGE_SHOW_ANIMATION_DURATION })
363      .opacity(0)
364    PageTransitionExit({ duration: BrowserConstants.PAGE_SHOW_ANIMATION_DURATION })
365      .opacity(0)
366  }
367
368  verifyPhotoScaled(matrix: any) {
369    if (!!matrix) {
370      let mat = matrix.copy().matrix4x4
371      let xScale = mat[Constants.NUMBER_0]
372      let yScale = mat[Constants.NUMBER_5]
373      Log.info(TAG, `photo in PhotoItem has Scaled x scale: ${xScale}, y scale: ${yScale}, mat: ${mat}`)
374      this.isPhotoScaled = xScale != 1 || yScale != 1
375    } else {
376      this.isPhotoScaled = false
377      Log.info(TAG, `photo in PhotoItem has not Scaled isPhotoScaled: ${this.isPhotoScaled}`)
378    }
379  }
380
381  private onBackPressInner(): void {
382    this.browserController.hideBrowser();
383  }
384
385  private jumpBrowserCallback(name: string, item: MediaItem, isSelectMode = false) {
386    if (this.dataSource && item && this.currentUri != item.uri) {
387      let tgtIndex = this.dataSource.getDataIndex(item);
388      Log.debug(TAG, `jump to index ${tgtIndex}`)
389      this.onPhotoChanged(tgtIndex);
390    }
391  }
392
393  private setPickResult(): void {
394    if (this.isFromFa) {
395      let currentPhoto = this.getCurrentPhoto();
396      if (currentPhoto) {
397        Log.debug(TAG, `setPickResult. updateFormData obj: ${currentPhoto.uri}  currentIndex: ${this.currentIndex}`);
398        this.appBroadCast.emit(BroadCastConstants.SAVE_FORM_EDITOR_DATA,
399          ["", AppStorage.Get(FormConstants.FORM_ITEM_ALBUM_URI), AppStorage.Get(FormConstants.FORM_ITEM_DISPLAY_NAME),
400          currentPhoto.uri, false]);
401      } else {
402        Log.error(TAG, 'Fa setPickResult is null');
403      }
404      return;
405    }
406    let uriArray;
407    if (this.isMultiPick) {
408      uriArray = SelectUtil.getUriArray(this.selectManager.clickedSet);
409      Log.info(TAG, `uri size: ${uriArray}`);
410    } else {
411      let currentPhoto = this.getCurrentPhoto();
412      if (currentPhoto == undefined) {
413        return;
414      }
415      uriArray = [currentPhoto.uri];
416    }
417    let promise: Promise<void> = SelectUtil.grantPermissionForUris(uriArray, this.bundleName);
418    let abilityResult = {
419      'resultCode': 0,
420      'want': {
421        'parameters': {
422          'select-item-list': uriArray,
423        }
424      }
425    };
426    promise.then(function () {
427      Log.info(TAG, `grant permission success.`);
428      globalThis.photosAbilityContext.terminateSelfWithResult(abilityResult).then((result) => {
429        Log.info(TAG, `terminateSelf result: ${result}`);
430      });
431    }).catch(function (err) {
432      Log.error(TAG, `grant permission error: ${JSON.stringify(err)}`);
433    });
434  }
435}
436
437/**
438 * 用于预览已选中的图片的dataSource
439 * 数据源取自selectManager的当前已选中图片
440 */
441class ThirdBrowserDataSource extends PhotoDataSource {
442  private isSelectMode = false;
443  private selectedItems: MediaItem[] = new Array<MediaItem>();
444
445  totalCount() {
446    if (this.isSelectMode) {
447      return this.selectedItems.length;
448    }
449    return super.totalCount();
450  }
451
452  getData(index: number): any {
453    if (this.isSelectMode) {
454      return this.packData(index, this.selectedItems[index]);
455    }
456    return super.getData(index);
457  }
458
459  setSelectMode(manager: ThirdSelectManager) {
460    this.isSelectMode = true;
461    this.selectedItems = manager.getSelectItems();
462  }
463}
464