1/**
2 * Copyright (c) 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 bundle from '@ohos.bundle';
18import CommonEvent from '@ohos.commonEvent';
19import window from '@ohos.window';
20import { BrowserConstants, MenuOperation, ViewData } from '@ohos/common';
21import {
22  Action,
23  AddMenuOperation,
24  AlbumDefine,
25  AlbumInfo,
26  BigDataConstants,
27  BreakpointSystem,
28  BroadCast,
29  BroadCastConstants,
30  BroadCastManager,
31  CommonObserverCallback,
32  Constants,
33  DateUtil,
34  DeleteMenuOperation,
35  JumpSourceToMain,
36  Log,
37  MediaDataSource,
38  MediaItem,
39  MediaObserver,
40  MediaOperationType,
41  MenuContext,
42  MenuOperationFactory,
43  MoveMenuOperation,
44  BrowserConstants as PhotoConstants,
45  PhotoDataSource,
46  RemoveMenuOperation,
47  ReportToBigDataUtil,
48  ScreenManager,
49  ShareMenuOperation,
50  TraceControllerUtils,
51  UiUtil,
52  UriDataSource,
53  UserFileManagerAccess,
54  WindowUtil
55} from '@ohos/common';
56import {
57  BrowserController,
58  CustomDialogView,
59  PhotoBrowserComponentBg,
60  PhotoBrowserHolder,
61  PhotoSwiper
62} from '@ohos/common/CommonComponents';
63import {
64  AddNotesMenuOperation,
65  FavoriteMenuOperation,
66  GotoPhotosMenuOperation,
67  RecoverMenuOperation,
68  RenameMenuOperation,
69  RotateMenuOperation
70} from '@ohos/browser';
71import {
72  PhotoBrowserActionBar,
73  PhotoBrowserToolBar
74} from '@ohos/browser/BrowserComponents';
75import ability from '@ohos.ability.ability';
76import common from '@ohos.app.ability.common';
77
78const TAG: string = 'PhotoBrowserComponent';
79
80interface ParamBrowser {
81  pageFrom: number;
82  albumInfo: AlbumInfo;
83  clickThumbnailTime: number;
84  albumUri: string;
85  index: number;
86  uri: string;
87  viewData: ViewData;
88  viewDataIndex: string;
89  viewDataAlbum: string;
90  transition: string;
91  position: number;
92  isShowMenuFromThirdView: boolean;
93  deviceName: string;
94  deviceId: string;
95};
96
97interface Params {
98  pageType: string;
99  albumName: string;
100  albumUri: string;
101};
102
103interface TitleName {
104  title: string;
105  displayName: string;
106}
107
108// page of large photo
109@Component
110export struct PhotoBrowserComponent {
111  @Provide backgroundColorResource: Resource = $r('app.color.default_background_color');
112  @Provide('dateTitle') photoDate: string = '';
113  @Provide('timeLocationTitle') timeAndLocation: string = '';
114  @Provide menuList: Action[] = [];
115  @Provide toolMenuList: Action[] = [];
116  @Provide topMenuList: Action[] = [];
117  @Provide moreMenuList: Action[] = [];
118  @Provide broadCast: BroadCast = new BroadCast();
119  @Provide isShowBar: boolean = true;
120  @Provide onlyChangeBgColor: boolean = false;
121  @Provide canSwipe: boolean = true;
122  @Provide pageFrom: number = Constants.ENTRY_FROM.NORMAL;
123  @State @Watch('updateMoreMenu') currentShow: boolean = true;
124  @StorageLink('isHorizontal') @Watch('updateIsHorizontal') isHorizontal: boolean = ScreenManager.getInstance()
125    .isHorizontal();
126  @StorageLink('timelinePageIndex') timelinePageIndex: number = Constants.INVALID;
127  @StorageLink('photoGridPageIndex') photoGridPageIndex: number = Constants.INVALID;
128  @StorageLink('isSplitMode') isSplitMode: boolean = ScreenManager.getInstance().isSplitMode();
129  @StorageLink('leftBlank') leftBlank: number[] =
130    [0, ScreenManager.getInstance().getStatusBarHeight(), 0, ScreenManager.getInstance().getNaviBarHeight()];
131  @StorageLink('entryFromHap') entryFromHap: number = Constants.ENTRY_FROM_NONE;
132  mTransition: string = '';
133  albumUri = '';
134  // swiper currentIndex, there may not be onChanged callback during data refresh, so mediaItem cannot be saved
135  @Provide('transitionIndex') currentIndex: number = Constants.NUMBER_0;
136  controller?: SwiperController = new SwiperController();
137  @Prop @Watch('onPageChanged') pageStatus: boolean = false;
138  @StorageLink('geometryOpacity') geometryOpacity: number = 1;
139  @State geometryTransitionId: string = '';
140  @Link isRunningAnimation: boolean;
141  @ObjectLink browserController: BrowserController;
142  @Provide isDeleting: boolean = false;
143  @Provide hidePopup: boolean = false;
144  private isFirstLoad: boolean = true;
145  private bundleFlags = Constants.NUMBER_0;
146  // DataSource
147  private dataSource: PhotoDataSource = new PhotoDataSource();
148  private dataObserver: CommonObserverCallback = new CommonObserverCallback(this);
149  // The global BroadCast of the application process. Event registration and destruction should be paired
150  private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
151  private isFromCamera = false;
152  private isFromViewDataWithMediaUri = false;
153  private isFromViewDataWithThirdUri = false;
154  private isFromFACard = false;
155  private isPullDown = false;
156  // the source of jump to the index page
157  private jumpSourceToMain: number = JumpSourceToMain.None;
158  // time when clicks the thumbnail from the camera
159  private clickThumbnailTime = Constants.NUMBER_0;
160  // time to view the current picture
161  private checkedTransition: string = '';
162  private viewTime = Constants.NUMBER_0;
163  // When clicking quickly, only run aboutToAppear for the first time
164  private hasAppeared: boolean = false;
165  private albumInfo?: AlbumInfo;
166  private deviceName = '';
167  private backFromCopy = false;
168  private uriFromThirdPartyApp: string = '';
169  private editNewUri: string = '';
170  private favorCacheItemsMap = new Map<String, MediaItem>()
171  private breakpointSystem: BreakpointSystem = new BreakpointSystem();
172  private theDeleteItem: MediaItem | null = null;
173  private geometryTransitionEnable: boolean = false;
174  private isShowMenuFromThirdView: boolean = true;
175  private isToEdit = false;
176  private photoBrowserBackFunc: Function = (): void => this.photoBrowserBack();
177  private onToggleBarsFunc: Function = (backgroundColorResource?: Resource): void =>
178  this.onToggleBars(backgroundColorResource);
179  private showBarsFunc: Function = (backgroundColorResource?: Resource): void => this.showBars(backgroundColorResource);
180  private hideBarsFunc: Function = (backgroundColorResource?: Resource): void => this.hideBars(backgroundColorResource);
181  private pullDownStartFunc: Function = (): void => this.pullDownStart();
182  private pullDownEndFunc: Function = (): void => this.pullDownEnd();
183  private onDataSizeChangedFunc: Function = (size: number): void => this.onDataSizeChanged(size);
184  private onDataContentChangedFunc: Function = (): void => this.onDataContentChanged();
185  private setFavorFunc: Function = (isFavor: boolean): void => this.setFavor(isFavor);
186  private doRenameFunc: Function = (result: TitleName): void => this.doRename(result);
187  private doRotateFunc: Function = (result: number): void => this.doRotate(result);
188  private pullDownStartWithEventFunc: Function = (event: KeyEvent): void => this.pullDownStartWithEvent(event);
189  private pullDownCancelFunc: Function = (): void => this.pullDownCancel();
190  private onPhotoBrowserDeleteConfirmFunc: Function = (): void => this.onPhotoBrowserDeleteConfirm();
191  private onPhotoBrowserRemoveConfirmFunc: Function = (): void => this.onPhotoBrowserRemoveConfirm();
192  private doDeleteFunc: Function = (): void => this.doDelete();
193  private onPhotoShowStateChangedFunc: Function = (state: boolean): void => this.onPhotoShowStateChanged(state);
194  private setSwiperDisableFunc: Function = (value: boolean): void => this.setSwiperDisable(value);
195  private onDataReloadWithEditFunc: Function = (): void => this.onDataReloadWithEdit();
196
197
198  onPageChanged() {
199    Log.info(TAG, `call page status changed ${this.pageStatus}`)
200    if (this.pageStatus) {
201      this.onPageShow();
202    } else {
203      this.onPageHide();
204    }
205  }
206
207  discardCallback(): void {
208    Log.debug(TAG, 'discardCallback called');
209  }
210
211  updateIsHorizontal(): void {
212    if (this.isHorizontal) {
213      ScreenManager.getInstance().setSystemUi(false);
214    } else {
215      if (this.isShowBar) {
216        ScreenManager.getInstance().setSystemUi(true);
217      }
218    }
219
220    if (!this.isFirstLoad) {
221      this.updateMenu(this.currentIndex);
222    } else {
223      this.isFirstLoad = false;
224    }
225  }
226
227  onMenuClicked(action: Action): void {
228    let actionID: number = action.actionID;
229    Log.info(TAG, `onMenuClicked, actionID: ${actionID}`);
230    let menuOperation: MenuOperation | null = null;
231    let menuContext: MenuContext = new MenuContext();
232    let currentPhoto = this.getCurrentPhoto();
233    if (actionID === Action.BACK.actionID) {
234      this.onBackPress();
235    } else if (actionID === Action.INFO.actionID) {
236      if (currentPhoto == undefined) {
237        Log.warn(TAG, 'currentPhoto is undefined');
238        return;
239      }
240      this.broadCast.emit(BroadCastConstants.SHOW_DETAIL_DIALOG,
241        [currentPhoto, this.pageFrom == Constants.ENTRY_FROM.DISTRIBUTED]);
242    } else if (actionID === Action.SHARE.actionID) {
243      if (currentPhoto == undefined) {
244        Log.warn(TAG, 'currentPhoto is undefined');
245        return;
246      }
247      menuContext.withFromSelectMode(false).withMediaItem(currentPhoto);
248      menuOperation = MenuOperationFactory.getInstance()
249        .createMenuOperation(ShareMenuOperation, menuContext);
250    } else if (actionID === Action.NOT_FAVORITE.actionID || actionID === Action.FAVORITE.actionID) {
251      if (currentPhoto == undefined) {
252        Log.warn(TAG, 'currentPhoto is undefined.');
253        return;
254      }
255      currentPhoto.isFavor = !currentPhoto.isFavor;
256
257      if (this.albumUri !== UserFileManagerAccess.getInstance()
258        .getSystemAlbumUri(UserFileManagerAccess.FAVORITE_ALBUM_SUB_TYPE)) {
259        let currentPhoto = this.getCurrentPhoto();
260        menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
261        menuOperation = MenuOperationFactory.getInstance().createMenuOperation(FavoriteMenuOperation, menuContext);
262        this.appBroadCast.emit(BroadCastConstants.UPDATE_DATA_SOURCE, [currentPhoto]);
263      } else {
264        if (currentPhoto.isFavor === true) {
265          if (this.favorCacheItemsMap.has(currentPhoto.uri)) {
266            this.favorCacheItemsMap.delete(currentPhoto.uri);
267          } else {
268            Log.error(TAG, `not fount item uri ${currentPhoto.uri}`);
269          }
270        } else {
271          this.favorCacheItemsMap.set(currentPhoto.uri, currentPhoto);
272        }
273      }
274      this.updateMenu(this.currentIndex);
275      this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + 'false';
276      AppStorage.setOrCreate<string>('geometryTransitionBrowserId', this.geometryTransitionId);
277    } else if (actionID === Action.DELETE.actionID) {
278      if (currentPhoto == undefined) {
279        Log.warn(TAG, 'currentPhoto is undefined.');
280        return;
281      }
282      menuContext.withAlbumInfo(this.albumInfo);
283      this.theDeleteItem = currentPhoto;
284      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
285      menuOperation = MenuOperationFactory.getInstance()
286        .createMenuOperation(DeleteMenuOperation, menuContext);
287    } else if (actionID === Action.RECOVER.actionID) {
288      if (currentPhoto == undefined) {
289        Log.warn(TAG, 'currentPhoto is undefined.');
290        return;
291      }
292      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
293      menuOperation = MenuOperationFactory.getInstance()
294        .createMenuOperation(RecoverMenuOperation, menuContext);
295    } else if (actionID === Action.GOTO_PHOTOS.actionID) {
296      if (currentPhoto == undefined) {
297        Log.warn(TAG, 'currentPhoto is undefined.');
298        return;
299      }
300      menuContext.withJumpSourceToMain(this.jumpSourceToMain);
301      menuOperation = MenuOperationFactory.getInstance()
302        .createMenuOperation(GotoPhotosMenuOperation, menuContext);
303    } else if (actionID === Action.EDIT.actionID) {
304      if (currentPhoto == undefined || currentPhoto.size == 0) {
305        Log.warn(TAG, 'currentPhoto is undefined or size is 0.');
306        return;
307      }
308      AppStorage.setOrCreate<MediaItem | undefined>('EditorMediaItem', currentPhoto);
309      AppStorage.setOrCreate<string>('EditorAlbumUri', this.albumUri);
310      router.pushUrl({
311        url: 'pages/EditMain'
312      })
313      this.isToEdit = true;
314    } else if (actionID === Action.EDIT_INVALID.actionID) {
315      if (currentPhoto == undefined || currentPhoto.size == 0) {
316        Log.warn(TAG, 'currentPhoto is undefined or size is 0.');
317        return;
318      }
319    } else if (actionID === Action.RENAME.actionID) {
320      this.hidePopup = true;
321      if (currentPhoto == undefined) {
322        Log.warn(TAG, 'currentPhoto is undefined.');
323        return;
324      }
325      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast).withAlbumUri(this.albumUri);
326      menuOperation = MenuOperationFactory.getInstance().createMenuOperation(RenameMenuOperation, menuContext);
327    } else if (actionID === Action.ROTATE.actionID) {
328      if (currentPhoto == undefined) {
329        Log.warn(TAG, 'currentPhoto is undefined when onMenuClicked Action.RENAME.');
330        return;
331      }
332      let rotateValue = currentPhoto.orientation - Constants.DEFAULT_ROTATE_VALUE + Constants.ROTATE_AROUND;
333      if (rotateValue >= Constants.ROTATE_AROUND) {
334        rotateValue = rotateValue - Constants.ROTATE_AROUND;
335      }
336      currentPhoto.orientation = rotateValue;
337      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
338      menuOperation = MenuOperationFactory.getInstance().createMenuOperation(RotateMenuOperation, menuContext);
339    } else if (actionID === Action.ADD_NOTES.actionID) {
340      if (currentPhoto == undefined) {
341        Log.warn(TAG, 'currentPhoto is undefined when onMenuClicked Action.RENAME.');
342        return;
343      }
344      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
345      menuOperation = MenuOperationFactory.getInstance().createMenuOperation(AddNotesMenuOperation, menuContext);
346    } else if (actionID === Action.MOVE.actionID) {
347      if (currentPhoto == undefined) {
348        Log.warn(TAG, 'currentPhoto is undefined when onMenuClicked Action.MOVE.');
349        return;
350      }
351      this.backFromCopy = true;
352      currentPhoto && currentPhoto.mediaType &&
353      this.routeToSelectAlbumPage(MediaOperationType.Move, currentPhoto.mediaType);
354      return;
355    } else if (actionID === Action.ADD.actionID) {
356      if (currentPhoto == undefined) {
357        Log.warn(TAG, 'currentPhoto is undefined when onMenuClicked Action.ADD.');
358        return;
359      }
360      this.backFromCopy = true;
361      currentPhoto && currentPhoto.mediaType &&
362      this.routeToSelectAlbumPage(MediaOperationType.Add, currentPhoto.mediaType);
363    } else if (actionID === Action.REMOVE_FROM.actionID) {
364      if (currentPhoto == undefined) {
365        Log.warn(TAG, 'currentPhoto is undefined.');
366        return;
367      }
368
369      menuContext.withAlbumUri(this.albumUri);
370      menuContext.withMediaItem(currentPhoto).withBroadCast(this.broadCast);
371      menuOperation = MenuOperationFactory.getInstance()
372        .createMenuOperation(RemoveMenuOperation, menuContext);
373    } else if (actionID === Action.DOWNLOAD.actionID) {
374      this.downLoad();
375    }
376    if (!!menuOperation) {
377      menuOperation.doAction();
378    }
379  }
380
381  routeToSelectAlbumPage(pageType: string, mediaType: number): void {
382    router.pushUrl({
383      url: 'pages/MediaOperationPage',
384      params: {
385        pageFrom: Constants.MEDIA_OPERATION_FROM_PHOTO_BROWSER,
386        pageType: pageType,
387        albumInfo: this.albumInfo,
388        mediaType: mediaType
389      }
390    });
391  }
392
393  onPhotoChanged(index: number): void {
394    Log.info(TAG, `onPhotoChanged start, index=${index}`);
395    this.reportToBigDataForPhotoSlide(index);
396    this.currentIndex = index;
397    this.updateActionBar(index);
398    let currentPhoto: MediaItem | undefined = this.getPhotoByIndex(index);
399    if (currentPhoto == undefined) {
400      Log.error(TAG, 'onPhotoChanged, item is undefined');
401    } else {
402      let timelineIndex = this.dataSource.getPositionByIndex(index);
403      AppStorage.setOrCreate<number>('placeholderIndex', timelineIndex);
404      this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + 'false';
405      AppStorage.setOrCreate<string>('geometryTransitionBrowserId', this.geometryTransitionId);
406      Log.debug(TAG, `onPhotoChanged, index: ${index}, currentPhoto: ${currentPhoto.uri}, \
407        placeholderIndex ${AppStorage.get<number>('placeholderIndex') as number},\
408        geometryTransitionBrowserId ${this.geometryTransitionId}, this.mTransition ${this.mTransition}, \
409        pageFrom = ${this.pageFrom}`);
410    }
411    this.updatePixMapDataSource();
412  }
413
414  onDataSizeChanged(size: number): void {
415    Log.info(TAG, `onDataSizeChanged, size is ${size}`);
416    if (size == 0 && !this.isToEdit) {
417      if (this.uriFromThirdPartyApp) {
418        return;
419      }
420      this.onBackPress();
421    }
422  }
423
424  resetAlbum(albumUri: string): void {
425    this.currentIndex = 0;
426    Log.info(TAG, `not found in album[${this.albumUri}], so use ${albumUri} instead`);
427    this.albumUri = albumUri;
428    this.dataSource.resetAlbumUri(this.albumUri);
429    return;
430  }
431
432  updatePixMapDataSource(): void {
433    this.dataSource.updatePixMapDataSource(this.currentIndex);
434  }
435
436  updateActionBar(index: number): void {
437    let currentPhoto: MediaItem | undefined = this.getPhotoByIndex(index);
438    if (currentPhoto == undefined || this.isFromViewDataWithThirdUri) {
439      return;
440    }
441    this.photoDate = DateUtil.getLocalizedDate(currentPhoto?.getDataTaken());
442    if (this.pageFrom == Constants.ENTRY_FROM.DISTRIBUTED) {
443      this.timeAndLocation = `${this.deviceName}/${DateUtil.getLocalizedTime(currentPhoto?.getDataTaken())}`;
444    } else {
445      this.timeAndLocation = DateUtil.getLocalizedTime(currentPhoto?.getDataTaken());
446    }
447    this.updateMenu(index);
448  }
449
450  updateMenu(index: number): void {
451    let currentPhoto: MediaItem | undefined = this.getPhotoByIndex(index);
452    if (!currentPhoto) {
453      return;
454    }
455
456    if (this.albumUri == UserFileManagerAccess.getInstance()
457      .getSystemAlbumUri(UserFileManagerAccess.FAVORITE_ALBUM_SUB_TYPE)) {
458      let key = currentPhoto.uri;
459      if (this.favorCacheItemsMap.has(key)) {
460        let tempPhotoItem: MediaItem = this.favorCacheItemsMap.get(key) as MediaItem;
461        currentPhoto.isFavor = tempPhotoItem.isFavor;
462        this.favorCacheItemsMap.set(key, currentPhoto);
463      }
464    }
465
466    let pageFrom: number = this.pageFrom;
467    if (this.albumUri === UserFileManagerAccess.getInstance()
468      .getSystemAlbumUri(UserFileManagerAccess.TRASH_ALBUM_SUB_TYPE)) {
469      pageFrom = Constants.ENTRY_FROM.RECYCLE;
470    }
471
472    Log.info(TAG, `updateMenu album[${this.albumUri}]`);
473
474    let menuTemp: Action[] = [];
475    if (this.pageFrom == Constants.ENTRY_FROM.CAMERA ||
476      this.pageFrom == Constants.ENTRY_FROM.CARD ||
477      (this.isFromViewDataWithMediaUri == true &&
478        this.albumUri != UserFileManagerAccess.getInstance()
479          .getSystemAlbumUri(UserFileManagerAccess.TRASH_ALBUM_SUB_TYPE))) {
480      menuTemp = [Action.GOTO_PHOTOS, Action.INFO];
481    } else if (pageFrom == Constants.ENTRY_FROM.RECYCLE || this.isFromViewDataWithThirdUri == true) {
482      menuTemp = [];
483    } else {
484      menuTemp = [Action.INFO];
485    }
486    if (!UiUtil.isActionArrayEqual(this.menuList, menuTemp)) {
487      this.menuList = menuTemp;
488    }
489
490    let list: Action[] = [];
491    if (pageFrom === Constants.ENTRY_FROM.NORMAL || pageFrom === Constants.ENTRY_FROM.CAMERA) {
492      list.push(currentPhoto.isFavor ? Action.FAVORITE : Action.NOT_FAVORITE,
493        ((currentPhoto.mediaType == UserFileManagerAccess.MEDIA_TYPE_IMAGE))
494          ? Action.EDIT : Action.EDIT_INVALID, Action.DELETE, Action.MORE); // TODO: delete edit
495    } else if (pageFrom === Constants.ENTRY_FROM.RECYCLE) {
496      list.push(Action.RECOVER, Action.DELETE);
497    } else if (pageFrom === Constants.ENTRY_FROM.DISTRIBUTED) {
498      list.push(Action.DOWNLOAD);
499    } else {
500      list.push(currentPhoto.isFavor ? Action.FAVORITE : Action.NOT_FAVORITE,
501        ((currentPhoto.mediaType == UserFileManagerAccess.MEDIA_TYPE_IMAGE))
502          ? Action.EDIT : Action.EDIT_INVALID, Action.DELETE, Action.MORE); // TODO: delete edit
503    }
504
505    if (this.isHorizontal) {
506      if (this.isShowMenuFromThirdView) {
507        this.menuList = this.menuList.concat(list);
508      }
509      this.toolMenuList = [];
510    } else {
511      if (this.isShowMenuFromThirdView) {
512        if (!UiUtil.isActionArrayEqual(this.toolMenuList, list)) {
513          this.toolMenuList = list;
514        }
515      } else {
516        this.toolMenuList = [];
517      }
518    }
519    let menuTempList: Action[] = [];
520    if (!this.albumInfo) {
521      // 照片页
522      menuTempList = [Action.ADD, Action.RENAME];
523    } else {
524      // 指定相册
525      menuTempList = this.albumInfo.isSystemAlbum ?
526        [Action.ADD, Action.RENAME] : [Action.MOVE, Action.ADD, Action.REMOVE_FROM, Action.RENAME];
527    }
528    if (!UiUtil.isActionArrayEqual(this.moreMenuList, menuTempList)) {
529      this.moreMenuList = menuTempList;
530    }
531  }
532
533  isShowMenuBigData(isShowMenuFromThirdView: boolean): void {
534    interface ShowMenuMsg {
535      isShowMenuFromThirdView: string
536    }
537    let isShowMenuMsg: ShowMenuMsg;
538    if (isShowMenuFromThirdView) {
539      this.isShowMenuFromThirdView = true;
540      isShowMenuMsg = {
541        isShowMenuFromThirdView: BigDataConstants.SHOW_MENU
542      }
543    } else if (isShowMenuFromThirdView == false) {
544      this.isShowMenuFromThirdView = false;
545      isShowMenuMsg = {
546        isShowMenuFromThirdView: BigDataConstants.HIDE_MENU
547      }
548    } else {
549      this.isShowMenuFromThirdView = true;
550      isShowMenuMsg = {
551        isShowMenuFromThirdView: BigDataConstants.UNDEFINED_IS_SHOW_MENU
552      }
553    }
554    this.updateMenu(this.currentIndex);
555    ReportToBigDataUtil.statisticReport(BigDataConstants.IS_SHOW_MENU_ID, isShowMenuMsg);
556  }
557
558  updateMoreMenu(): void {
559    this.moreMenuList = this.albumInfo?.isSystemAlbum ?
560      [Action.ADD, Action.RENAME] : [Action.MOVE, Action.ADD, Action.REMOVE_FROM, Action.RENAME];
561  }
562
563  getCurrentPhoto(): MediaItem {
564    return this.dataSource?.getRawData(this.currentIndex).data;
565  }
566
567  getPhotoByIndex(index: number): MediaItem {
568    return this.dataSource?.getRawData(index).data;
569  }
570
571  async onMoveEnd(err: Object, count: number, total: number): Promise<void> {
572    Log.debug(TAG, `onMoveEnd count: ${count}, total: ${total}`);
573    if (err) {
574      UiUtil.showToast($r('app.string.move_failed_single'));
575      return;
576    }
577    let currentPhoto = this.getCurrentPhoto();
578    let newItem = await this.dataSource.getDataByUri(currentPhoto.uri)
579    this.appBroadCast.emit(BroadCastConstants.UPDATE_DATA_SOURCE, [currentPhoto]);
580  }
581
582  onCopyEnd(err: Object, count: number, total: number): void {
583    Log.debug(TAG, `onCopyEnd count: ${count}, total: ${total}`);
584    if (err) {
585      UiUtil.showToast($r('app.string.copy_failed_single'));
586    }
587  }
588
589  async onDownloadEnd(err: Object, count: number, total: number): Promise<void> {
590    Log.debug(TAG, `onDownloadEnd count: ${count}, total: ${total}`);
591    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [true, this.checkedTransition]);
592    if (err) {
593      UiUtil.showToast($r('app.string.download_failed_single'));
594    } else {
595      UiUtil.showToast($r('app.string.download_progress_done'));
596    }
597  }
598
599  onBackPress(): boolean {
600    Log.info(TAG, 'onBackPress');
601    this.appBroadCast.emit('hideBar', []);
602    let currentPhoto = this.getPhotoByIndex(this.currentIndex);
603    this.broadCast.emit(BrowserConstants.RESET_DEFAULT_SCALE + currentPhoto?.uri, []);
604    this.controller?.finishAnimation((): void => this.onBackPressInner());
605    return true;
606  }
607
608  onBackPressInner(): void {
609    Log.info(TAG, `onBackPressInner ${this.checkedTransition}`);
610    this.dataSource.release();
611    if (this.checkedTransition === Constants.PHOTO_TRANSITION_TIMELINE) {
612      Log.info(TAG, 'onBackPress TimelinePage');
613      this.timelinePageIndex = this.currentIndex; // call scrollTo
614      this.timelinePageIndex = Constants.INVALID;
615    } else if (this.checkedTransition === Constants.PHOTO_TRANSITION_ALBUM) {
616      Log.info(TAG, 'onBackPress PhotoGridPage');
617      this.photoGridPageIndex = this.currentIndex; // call scrollTo
618      this.photoGridPageIndex = Constants.INVALID;
619
620      if (this.isFromFACard) {
621        if (this.isPullDown) {
622          this.isPullDown = false;
623          let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext;
624          context.terminateSelf();
625        } else {
626          let displayName: string = AppStorage.get<string>('form_displayName') as string;
627          let uri: string = AppStorage.get<string>('form_albumUri') as string;
628          let item: AlbumInfo = new AlbumInfo(undefined);
629          item.uri = uri;
630          item.albumName = displayName;
631          router.replaceUrl({
632            url: 'pages/PhotoGridPage',
633            params: {
634              item: JSON.stringify(item),
635              isFromFACard: this.isFromFACard
636            }
637          });
638        }
639
640        if (this.geometryTransitionEnable) {
641          UiUtil.resetGeometryTransitionParams();
642        }
643        this.breakpointSystem.unregisterOrientationChange();
644        WindowUtil.setPreferredOrientation(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,
645          window.Orientation.UNSPECIFIED);
646        return;
647      }
648    } else if (this.checkedTransition === Constants.PHOTO_TRANSITION_CAMERA) {
649      Log.info(TAG, 'onBackPress Camera');
650      // Entering from the camera does not need to return to close directly
651      let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext;
652      context.terminateSelf();
653    } else if (this.checkedTransition === Constants.PHOTO_TRANSITION_THIRD_APP) {
654      Log.info(TAG, 'onBackPress third app');
655      this.setViewDataResult(true);
656    }
657    if (this.geometryTransitionEnable) {
658      this.browserController.hideBrowser();
659    } else {
660      router.back({
661        url: '',
662        params: {
663          index: this.currentIndex
664        }
665      });
666    }
667  }
668
669  updatePhotoName(result: TitleName): void {
670    let currentPhoto = this.getCurrentPhoto();
671    currentPhoto?.setTitle(result.title);
672    currentPhoto.displayName = result.displayName;
673    this.appBroadCast.emit(BroadCastConstants.UPDATE_DATA_SOURCE, [currentPhoto]);
674  }
675
676  aboutToDisappear(): void {
677    Log.info(TAG, 'photoBrowser aboutToDisappear');
678    this.favorCacheItemsMap.forEach((item) => {
679      let menuFavorContext = new MenuContext().withMediaItem(item).withBroadCast(this.broadCast);
680      let menuFavorOperation = MenuOperationFactory.getInstance()
681        .createMenuOperation(FavoriteMenuOperation, menuFavorContext);
682      menuFavorOperation.doAction();
683    });
684
685    if (!this.isShowBar && !this.isHorizontal) {
686      ScreenManager.getInstance().setSystemUi(true);
687    }
688    // Click the thumbnail quickly, hasAppeared is false if it is not the first click. Return directly
689    if (!this.hasAppeared) {
690      return;
691    }
692    MediaObserver.getInstance().unregisterObserver(this.dataObserver);
693    this.dataObserver.clearSource();
694
695    if (!this.isFromFACard) {
696      this.breakpointSystem.unregisterOrientationChange();
697      WindowUtil.setPreferredOrientation(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,
698        window.Orientation.UNSPECIFIED);
699    }
700
701    this.broadCast.off(PhotoConstants.TOGGLE_BAR, this.onToggleBarsFunc);
702    this.broadCast.off(PhotoConstants.HIDE_BARS, this.hideBarsFunc);
703    this.broadCast.off(PhotoConstants.SHOW_BARS, this.showBarsFunc);
704    this.broadCast.off(PhotoConstants.PULL_DOWN_START, this.pullDownStartFunc);
705    this.broadCast.off(PhotoConstants.PULL_DOWN_END, this.pullDownEndFunc);
706    this.broadCast.off(PhotoConstants.DATA_SIZE_CHANGED, this.onDataSizeChangedFunc);
707    this.broadCast.off(PhotoConstants.DATA_CONTENT_CHANGED, this.onDataContentChangedFunc);
708    this.broadCast.off(PhotoConstants.SET_FAVOR, this.setFavorFunc);
709    this.broadCast.off(PhotoConstants.RENAME, this.doRenameFunc);
710    this.broadCast.off(PhotoConstants.ROTATE, this.doRotateFunc);
711    this.broadCast.off(PhotoConstants.PULL_DOWN_START, this.pullDownStartWithEventFunc);
712    this.broadCast.off(PhotoConstants.PULL_DOWN_CANCEL, this.pullDownCancelFunc);
713    this.broadCast.off(PhotoConstants.PHOTO_BROWSER_DELETE_CONFIRM, this.onPhotoBrowserDeleteConfirmFunc);
714    this.broadCast.off(PhotoConstants.PHOTO_BROWSER_REMOVE_CONFIRM, this.onPhotoBrowserRemoveConfirmFunc);
715    this.broadCast.off(PhotoConstants.DELETE, this.doDeleteFunc);
716    this.broadCast.off(PhotoConstants.PHOTO_SHOW_STATE, this.onPhotoShowStateChangedFunc);
717    this.broadCast.off(PhotoConstants.SET_DISABLE_SWIPE, this.setSwiperDisableFunc);
718    this.broadCast.off(BroadCastConstants.ON_DATA_RELOADED_WITH_EDIT, this.onDataReloadWithEditFunc);
719    this.appBroadCast.off(BroadCastConstants.PHOTO_BROWSER_BACK_PRESS_EVENT, this.photoBrowserBackFunc);
720
721    this.controller = undefined;
722  }
723
724  getAlbumUriByUri(albumUri: string) {
725    if (albumUri && albumUri.length > 0) {
726      return albumUri;
727    }
728
729    return '';
730  }
731
732  aboutToAppear(): void {
733    TraceControllerUtils.startTrace('PhotoBrowserAboutToAppear');
734    Log.info(TAG, 'photoBrowser aboutToAppear');
735    AppStorage.setOrCreate('isEditFunc', false);
736    this.geometryTransitionId = AppStorage.get<string>('geometryTransitionBrowserId') as string;
737    this.hasAppeared = true;
738    this.updateIsHorizontal();
739
740    WindowUtil.setPreferredOrientation(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,
741      window.Orientation.AUTO_ROTATION_RESTRICTED);
742    this.appBroadCast.emit('hideBar', []);
743    let param: ParamBrowser = this.browserController.browserParam as ParamBrowser;
744    let entryFromCamera = (AppStorage.get<number>('entryFromHapCamera')) as number == Constants.ENTRY_FROM_CAMERA;
745    Log.info(TAG, `photoBrowser start with entryFrom ` + JSON.stringify(entryFromCamera));
746    if (entryFromCamera) {
747      param = {
748        pageFrom: Constants.ENTRY_FROM.CAMERA
749      } as ParamBrowser;
750      AppStorage.setOrCreate('entryFromHapCamera', Constants.ENTRY_FROM_NONE);
751    }
752
753    if (param) {
754      Log.info(TAG, `photoBrowser start with param`);
755      Log.debug(TAG, `param: ${JSON.stringify(param)}`);
756      if (param.pageFrom) {
757        this.pageFrom = param.pageFrom;
758      }
759      if (param.albumInfo) {
760        this.albumInfo = param.albumInfo;
761        this.albumUri = param.albumInfo.uri;
762        this.deviceName = param.albumInfo.deviceName;
763      }
764      if (this.pageFrom == Constants.ENTRY_FROM.CAMERA) {
765        this.dataSource = new PhotoDataSource();
766        this.dataSource.initData();
767        this.isFromCamera = true;
768        this.clickThumbnailTime = param.clickThumbnailTime ? param.clickThumbnailTime : 0;
769        this.albumUri = '';
770        this.jumpSourceToMain = JumpSourceToMain.CAMERA;
771        MediaObserver.getInstance().registerObserver(this.dataObserver);
772        AppStorage.setOrCreate('entryFromHap', Constants.ENTRY_FROM_NONE);
773      } else if (this.pageFrom == Constants.ENTRY_FROM.CARD) {
774        this.dataSource = new PhotoDataSource();
775        this.albumUri = param.albumUri;
776        this.dataSource.enableGetData(false);
777        this.dataSource.setAlbumUri(this.albumUri);
778        this.dataSource.initData();
779        this.isFromCamera = true;
780        this.jumpSourceToMain = JumpSourceToMain.CAMERA;
781        MediaObserver.getInstance().registerObserver(this.dataObserver);
782        this.uriFromThirdPartyApp = param.uri;
783        this.currentIndex = param.index;
784        this.onGetItemIndexByUri(this.currentIndex)
785        this.isFromFACard = true;
786        this.geometryTransitionEnable = true;
787      } else if (this.pageFrom == Constants.ENTRY_FROM.RECYCLE) {
788        this.dataSource.setAlbumUri(this.albumUri);
789        this.dataSource.setAlbumDataSource
790        (AppStorage.get<MediaDataSource>(Constants.APP_KEY_PHOTO_BROWSER) as MediaDataSource);
791      } else if (this.pageFrom == Constants.ENTRY_FROM.DISTRIBUTED) {
792        this.dataSource.setDeviceId(param.albumInfo.deviceId);
793        this.dataSource.setAlbumDataSource(
794            AppStorage.get<MediaDataSource>(Constants.APP_KEY_PHOTO_BROWSER) as MediaDataSource);
795      } else if (this.pageFrom == Constants.ENTRY_FROM.VIEW_DATA) {
796        if (String(param.viewData).length === 0) {
797          Log.error(TAG, 'Invalid uri');
798          this.setViewDataResult(false);
799          return;
800        }
801        Log.info(TAG, `Found viewIndex: ${String(param.viewDataIndex).length}`);
802        if (String(param.viewDataIndex).length > 0) {
803          Log.debug(TAG, `Found viewIndex`);
804          this.dataSource = new UriDataSource(String(param.viewData).split('?'));
805          let viewIndex = Number.parseInt(param.viewDataIndex);
806          if (Number.isNaN(viewIndex) || viewIndex <= 0 || viewIndex > String(param.viewData).split('?').length) {
807            viewIndex = 0;
808          } else {
809            viewIndex -= 1;
810          }
811          this.isFromViewDataWithThirdUri = true;
812          this.currentIndex = viewIndex;
813          param.position = viewIndex;
814        } else {
815          let uriCount = String(param.viewData).split(',').length;
816          if (uriCount > Constants.NUMBER_1) {
817            Log.error(TAG, 'Invalid uri');
818            this.setViewDataResult(false);
819            return;
820          }
821
822          if (String(param.viewData).startsWith(PhotoDataSource.MEIDA_URL_PREFIX_STR) ||
823          String(param.viewData).startsWith(PhotoDataSource.IMAGE_URL_PREFIX_STR_V10) ||
824          String(param.viewData).startsWith(PhotoDataSource.VIDEO_URL_PREFIX_STR_V10) ||
825          String(param.viewData).startsWith(PhotoDataSource.IMAGE_VIDEO_URL_PREFIX_STR_V10)) {
826            Log.debug(TAG, `Found media library uri`);
827            this.dataSource = new PhotoDataSource(this.albumUri);
828            this.albumUri = this.getAlbumUriByUri(param.viewDataAlbum);
829            Log.info(TAG, `album id: ${this.albumUri}`);
830            this.dataSource.enableGetData(false);
831            this.dataSource.setAlbumUri(this.albumUri);
832            this.dataSource.initData();
833            this.isFromViewDataWithMediaUri = true;
834            this.clickThumbnailTime = 0;
835            this.jumpSourceToMain = JumpSourceToMain.CAMERA;
836            MediaObserver.getInstance().registerObserver(this.dataObserver);
837            this.uriFromThirdPartyApp = String(param.viewData);
838            this.currentIndex = 0;
839            this.dataSource.getItemIndexByUri(this.uriFromThirdPartyApp,
840              (index: number): void => this.onGetItemIndexByUri(index));
841            // 是否显示菜单栏并且处理大数据打点
842            this.isShowMenuBigData(param.isShowMenuFromThirdView);
843          } else {
844            Log.debug(TAG, `Not found media library uri`);
845            this.dataSource = new UriDataSource(String(param.viewData).split(','));
846            this.isFromViewDataWithThirdUri = true;
847          }
848        }
849      } else {
850        MediaObserver.getInstance().registerObserver(this.dataObserver);
851        this.dataSource.setAlbumDataSource(
852          AppStorage.get<MediaDataSource>(Constants.APP_KEY_PHOTO_BROWSER) as MediaDataSource);
853      }
854
855      this.onPhotoChanged(param.position || 0);
856      if (this.isFromFACard) {
857        this.mTransition = Constants.PHOTO_TRANSITION_ALBUM;
858      } else if (this.isFromCamera) {
859        this.mTransition = Constants.PHOTO_TRANSITION_CAMERA;
860      } else if (this.isFromViewDataWithMediaUri || this.isFromViewDataWithThirdUri) {
861        this.mTransition = Constants.PHOTO_TRANSITION_THIRD_APP;
862      } else {
863        this.mTransition = param.transition;
864      }
865    } else {
866      Log.info(TAG, `photoBrowser start without param`);
867      if (this.entryFromHap == Constants.ENTRY_FROM_FORM_ABILITY) {
868        this.pageFrom = Constants.ENTRY_FROM.CARD;
869        this.albumUri = AppStorage.get<string>('form_albumUri') as string;
870      } else {
871        this.pageFrom = Constants.ENTRY_FROM.CAMERA;
872        this.albumUri = UserFileManagerAccess.getInstance()
873          .getSystemAlbumUri(UserFileManagerAccess.IMAGE_ALBUM_SUB_TYPE);
874      }
875      AppStorage.setOrCreate('entryFromHap', Constants.ENTRY_FROM_NONE);
876      let albumDataSource: MediaDataSource =
877        AppStorage.get<MediaDataSource>(Constants.APP_KEY_PHOTO_BROWSER) as MediaDataSource;
878      if (albumDataSource) {
879        this.dataSource.setAlbumDataSource(albumDataSource);
880      } else {
881        Log.error(TAG, `Constants.APP_KEY_PHOTO_BROWSER is null, so use all album instead`);
882        this.dataSource.initData();
883      }
884      this.isFromCamera = true;
885      this.jumpSourceToMain = JumpSourceToMain.CAMERA;
886      MediaObserver.getInstance().registerObserver(this.dataObserver);
887      this.onPhotoChanged(AppStorage.Get('form_currentIndex') || 0);
888      this.mTransition = Constants.PHOTO_TRANSITION_CAMERA;
889    }
890
891    this.checkedTransition = this.mTransition
892
893    if (this.mTransition.endsWith('ERROR')) {
894      this.checkedTransition = this.mTransition.substr(0, this.mTransition.length - 5)
895    }
896
897    this.dataSource.setBroadCast(this.broadCast);
898    this.dataSource.setBroadCastToAlbum(this.broadCast);
899
900    // register event handling
901    this.broadCast.on(PhotoConstants.TOGGLE_BAR, this.onToggleBarsFunc);
902    this.broadCast.on(PhotoConstants.HIDE_BARS, this.hideBarsFunc);
903    this.broadCast.on(PhotoConstants.SHOW_BARS, this.showBarsFunc);
904    this.broadCast.on(PhotoConstants.PULL_DOWN_START, this.pullDownStartFunc);
905    this.broadCast.on(PhotoConstants.PULL_DOWN_END, this.pullDownEndFunc);
906    this.broadCast.on(PhotoConstants.DATA_SIZE_CHANGED, this.onDataSizeChangedFunc);
907    this.broadCast.on(PhotoConstants.DATA_CONTENT_CHANGED, this.onDataContentChangedFunc);
908    this.broadCast.on(PhotoConstants.SET_FAVOR, this.setFavorFunc);
909    this.broadCast.on(PhotoConstants.RENAME, this.doRenameFunc);
910    this.broadCast.on(PhotoConstants.ROTATE, this.doRotateFunc);
911    this.broadCast.on(PhotoConstants.PULL_DOWN_START, this.pullDownStartWithEventFunc);
912    this.broadCast.on(PhotoConstants.PULL_DOWN_CANCEL, this.pullDownCancelFunc);
913    this.broadCast.on(PhotoConstants.PHOTO_BROWSER_DELETE_CONFIRM, this.onPhotoBrowserDeleteConfirmFunc);
914    this.broadCast.on(PhotoConstants.PHOTO_BROWSER_REMOVE_CONFIRM, this.onPhotoBrowserRemoveConfirmFunc);
915    this.broadCast.on(PhotoConstants.DELETE, this.doDeleteFunc);
916    this.broadCast.on(PhotoConstants.PHOTO_SHOW_STATE, this.onPhotoShowStateChangedFunc);
917    this.broadCast.on(PhotoConstants.SET_DISABLE_SWIPE, this.setSwiperDisableFunc);
918    this.broadCast.on(BroadCastConstants.ON_DATA_RELOADED_WITH_EDIT, this.onDataReloadWithEditFunc);
919
920    this.appBroadCast.on(BroadCastConstants.PHOTO_BROWSER_BACK_PRESS_EVENT, this.photoBrowserBackFunc);
921
922    interface MsgNavigation {
923      from: string;
924      fovMode: number;
925    }
926
927    let msg: MsgNavigation = {
928      from: BigDataConstants.LOCAL_MEDIA,
929      fovMode: 0
930    }
931    ReportToBigDataUtil.report(BigDataConstants.ENTER_PHOTO_BROWSER_ID, msg);
932    this.breakpointSystem.registerOrientationChange();
933    TraceControllerUtils.finishTrace('PhotoBrowserAboutToAppear');
934  }
935
936  onToggleBars(backgroundColorResource?: Resource): void {
937    if (this.isShowBar) {
938      this.hideBars(backgroundColorResource);
939    } else {
940      this.showBars(backgroundColorResource);
941    }
942    Log.info(TAG, `Toggle bars, isShowBar: ${this.isShowBar}`);
943  }
944
945  showBars(backgroundColorResource?: Resource): void {
946    this.backgroundColorResource = backgroundColorResource ?
947      backgroundColorResource : $r('app.color.default_background_color');
948    if (!this.isShowBar) {
949      this.isShowBar = !this.isShowBar;
950      if (!this.isHorizontal) {
951        ScreenManager.getInstance().setSystemUi(true);
952      }
953    } else {
954      this.onlyChangeBgColor = !this.onlyChangeBgColor;
955    }
956  }
957
958  hideBars(backgroundColorResource?: Resource): void {
959    this.backgroundColorResource = backgroundColorResource ?
960      backgroundColorResource : $r('app.color.black');
961    if (this.isShowBar) {
962      this.isShowBar = !this.isShowBar;
963      ScreenManager.getInstance().setSystemUi(false);
964    } else {
965      this.onlyChangeBgColor = !this.onlyChangeBgColor;
966    }
967  }
968
969  private pullDownStart(): void {
970    Log.info(TAG, 'pulling down start');
971  }
972
973  private pullDownEnd(): void {
974    Log.info(TAG, 'pulling down end');
975    if (this.isFromFACard) {
976      this.isPullDown = true;
977    }
978    this.onBackPress();
979  }
980
981  private onDataContentChanged(): void {
982    this.reportToBigDataForCameraIn();
983    Log.debug(TAG, `PhotoConstants.DATA_CONTENT_CHANGED`);
984    if (AppStorage.get('isEditFunc')) {
985      this.currentIndex = 0;
986    }
987    this.onPhotoChanged(this.currentIndex);
988  }
989
990  private setFavor(isFavor: boolean): void {
991    Log.debug(TAG, 'set favor !')
992    let currentPhoto = this.getCurrentPhoto();
993    if (!isFavor) {
994      currentPhoto.isFavor = isFavor;
995      this.updateMenu(this.currentIndex);
996    } else {
997      Log.debug(TAG, 'update favor !');
998    }
999  }
1000
1001  private doRename(result: TitleName): void {
1002    Log.info(TAG, `rename refresh: ${result.title}, ${result.displayName}`);
1003    this.updatePhotoName(result);
1004  }
1005
1006  private doRotate(result: number): void {
1007    Log.debug(TAG, `rotate finish: ${result}`);
1008    let currentPhoto = this.getCurrentPhoto();
1009    currentPhoto.orientation = result;
1010    let temp = currentPhoto.height;
1011    currentPhoto.height = currentPhoto.width;
1012    currentPhoto.width = temp;
1013    this.dataSource.onDataChanged(this.currentIndex);
1014    this.appBroadCast.emit(BroadCastConstants.UPDATE_DATA_SOURCE, [currentPhoto]);
1015  }
1016
1017  private pullDownStartWithEvent(event: KeyEvent): void {
1018    Log.debug(TAG, `pulling down start : ${JSON.stringify(event)}`);
1019    if (this.isFromViewDataWithThirdUri) {
1020      return;
1021    }
1022  }
1023
1024  private pullDownCancel(): void {
1025    Log.info(TAG, 'pulling down cancel');
1026  }
1027
1028  private onPhotoBrowserDeleteConfirm(): void {
1029    this.isDeleting = true;
1030    // clear temp not favorite items
1031    if (this.albumUri == UserFileManagerAccess.getInstance()
1032      .getSystemAlbumUri(UserFileManagerAccess.FAVORITE_ALBUM_SUB_TYPE)) {
1033      let key = this.theDeleteItem?.uri ?? '';
1034      if (this.favorCacheItemsMap.has(key)) {
1035        let item = this.favorCacheItemsMap.get(key)
1036        let menuFavorContext = new MenuContext().withMediaItem(item as MediaItem).withBroadCast(this.broadCast);
1037        let menuFavorOperation = MenuOperationFactory.getInstance()
1038          .createMenuOperation(FavoriteMenuOperation, menuFavorContext);
1039        menuFavorOperation.doAction();
1040        this.favorCacheItemsMap.delete(key);
1041      }
1042    }
1043  }
1044
1045  private onPhotoBrowserRemoveConfirm(): void {
1046    this.isDeleting = true;
1047  }
1048
1049  private doDelete(): void {
1050    Log.info(TAG, 'delete finish now update data');
1051  }
1052
1053  private onPhotoShowStateChanged(state: boolean): void {
1054    Log.debug(TAG, 'current photo show state change');
1055    this.currentShow = state;
1056  }
1057
1058  private setSwiperDisable(value: boolean): void {
1059    Log.info(TAG, `set swiper swipe ${value}`);
1060    this.canSwipe = value;
1061  }
1062
1063  private onDataReloadWithEdit(): void {
1064    Log.debug(TAG, 'animate to data reloaded start with edit');
1065    try {
1066      let uri: string = AppStorage.get<string>(BroadCastConstants.PHOTO_EDIT_SAVE_URI) as string;
1067      Log.debug(TAG, `data reloaded start with edit by uri ${uri}`);
1068      if (uri) {
1069        let newIndex = this.dataSource.getDataIndexByUri(uri);
1070        let oldIndex = this.currentIndex;
1071        if (newIndex != Constants.NOT_FOUND) {
1072          // Search for the position of new image/video after edit in current 500 items succeed
1073          Log.debug(TAG, `data reloaded from ${oldIndex} move to ${newIndex}`);
1074          this.onPhotoChanged(newIndex);
1075        } else {
1076          // Search for the position of new image/video after edit in current 500 items failed
1077          Log.debug(TAG, `data reloaded from ${oldIndex} move to unknown`);
1078          this.editNewUri = uri;
1079          this.dataSource.enableGetData(false);
1080          this.currentIndex = 0;
1081          this.dataSource.getItemIndexByUri(uri, (index: number): void => this.onGetItemIndexByNewEditUri(index));
1082        }
1083      }
1084    } catch (e) {
1085      Log.error(TAG, `ON_DATA_RELOADED_WITH_EDIT error ${e}`);
1086    } finally {
1087      this.appBroadCast.emit(BroadCastConstants.PHOTO_EDIT_SAVE_COMPLETE, []);
1088    }
1089  }
1090
1091  private photoBrowserBack(): void {
1092    Log.debug(TAG, 'hook back press from page.');
1093    this.onBackPress();
1094  }
1095
1096  onGetItemIndexByUri(index: number): void {
1097    Log.info(TAG, `onGetItemIndexByUri: index=${index}`);
1098    if (this.uriFromThirdPartyApp) {
1099      if (index != Constants.NOT_FOUND) {
1100        this.currentIndex = index;
1101        this.uriFromThirdPartyApp = '';
1102        this.dataSource.enableGetData(true);
1103        this.dataSource.getData(this.currentIndex);
1104        this.dataSource.onDataReloaded();
1105        Log.info(TAG, `Found: ${this.currentIndex}, ${this.albumUri}`);
1106      } else {
1107        if (this.albumUri === UserFileManagerAccess.getInstance()
1108          .getSystemAlbumUri(UserFileManagerAccess.TRASH_ALBUM_SUB_TYPE)) {
1109          if (this.isFromViewDataWithMediaUri || this.isFromViewDataWithThirdUri) {
1110            Log.error(TAG, `Uri from third party app is invalid`);
1111          } else if (this.isFromFACard) {
1112            Log.error(TAG, `Uri from FA is invalid`);
1113          } else {
1114            Log.error(TAG, `Uri from others is invalid`);
1115          }
1116          this.uriFromThirdPartyApp = '';
1117          this.dataSource.enableGetData(true);
1118          this.setViewDataResult(false);
1119        } else if (this.albumUri === '') {
1120          this.resetAlbum(UserFileManagerAccess.getInstance()
1121            .getSystemAlbumUri(UserFileManagerAccess.TRASH_ALBUM_SUB_TYPE));
1122          this.dataSource.getItemIndexByUri(this.uriFromThirdPartyApp,
1123            (index: number): void => this.onGetItemIndexByUri(index));
1124        } else {
1125          this.resetAlbum('');
1126          this.dataSource.getItemIndexByUri(this.uriFromThirdPartyApp,
1127            (index: number): void => this.onGetItemIndexByUri(index));
1128        }
1129      }
1130      this.onPhotoChanged(this.currentIndex);
1131    }
1132  }
1133
1134  onGetItemIndexByNewEditUri(index: number): void {
1135    Log.info(TAG, `onGetItemIndexByNewEditUri: index=${index}`);
1136    if (this.editNewUri.length > 0) {
1137      if (index != Constants.NOT_FOUND) {
1138        Log.info(TAG, `data reloaded move to ${index}`);
1139        this.currentIndex = index;
1140        if (this.checkedTransition == Constants.PHOTO_TRANSITION_TIMELINE) {
1141          this.timelinePageIndex = this.currentIndex; // call scrollTo
1142          this.timelinePageIndex = Constants.INVALID;
1143        } else if (this.checkedTransition == Constants.PHOTO_TRANSITION_ALBUM) {
1144          this.photoGridPageIndex = this.currentIndex; // call scrollTo
1145          this.photoGridPageIndex = Constants.INVALID;
1146        }
1147        this.dataSource.enableGetData(true);
1148        this.dataSource.getData(this.currentIndex);
1149        this.dataSource.onDataReloaded();
1150        this.editNewUri = '';
1151      } else {
1152        Log.error(TAG, `edit new uri ${this.editNewUri} is invalid`);
1153        this.editNewUri = '';
1154        this.currentIndex = 0;
1155        this.dataSource.enableGetData(true);
1156      }
1157      this.onPhotoChanged(this.currentIndex);
1158    }
1159  }
1160
1161  onPageShow(): void {
1162    TraceControllerUtils.startTrace('PhotoBrowseronPageShow');
1163    Log.info(TAG, 'photoBrowser page show');
1164    WindowUtil.setPreferredOrientation(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,
1165      window.Orientation.AUTO_ROTATION_RESTRICTED);
1166    let currentPhoto = this.getCurrentPhoto();
1167    if (currentPhoto) {
1168      this.syncPhotoName(currentPhoto).then((result) => {
1169        if (result) {
1170          this.updatePhotoName(result);
1171          this.broadCast.emit(PhotoConstants.UPDATE_PHOTO_NAME + currentPhoto?.uri, [result.title]);
1172        }
1173      });
1174    }
1175    this.updateActionBar(this.currentIndex);
1176    if (!this.isHorizontal) {
1177      ScreenManager.getInstance().setSystemUi(true);
1178    }
1179    this.appBroadCast.emit(BroadCastConstants.THIRD_ROUTE_PAGE, []);
1180    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [true, this.checkedTransition]);
1181    this.broadCast.emit(BroadCastConstants.CHANGE_SWIPER_DURATION, [400]);
1182    this.viewTime = Date.now();
1183    let params: Params = router.getParams() as Params;
1184    if (params != null && params.pageType != null && this.backFromCopy) {
1185      Log.debug(TAG, `MediaOperation back ${JSON.stringify(params)}`)
1186      let menuContext = new MenuContext();
1187      let menuOperation: MenuOperation | null = null;
1188      if (currentPhoto == undefined) {
1189        Log.error(TAG, 'MediaOperation currentPhoto is undefined');
1190        return;
1191      }
1192      if (params.pageType === MediaOperationType.Move) {
1193        let onMoveEndFunc = async (err: Error, count: number, total: number): Promise<void> => {
1194          await this.onMoveEnd(err as Object, count, total);
1195          }
1196        menuContext.withMediaItem(currentPhoto)
1197          .withBroadCast(this.broadCast)
1198          .withTargetAlbumName(params.albumName)
1199          .withAlbumUri(params.albumUri)
1200          .withOperationEndCallback(onMoveEndFunc);
1201        menuOperation = MenuOperationFactory.getInstance().createMenuOperation(MoveMenuOperation, menuContext);
1202        AppStorage.setOrCreate<string | undefined>(Constants.APP_KEY_NEW_ALBUM_SOURCE, this.albumInfo?.uri);
1203      } else if (params.pageType === MediaOperationType.Remove) {
1204        let onMoveEndFunc = async (err: Error, count: number, total: number): Promise<void> => {
1205          await this.onMoveEnd(err as Object, count, total);
1206          }
1207        menuContext.withMediaItem(currentPhoto)
1208          .withBroadCast(this.broadCast)
1209          .withTargetAlbumName(params.albumName)
1210          .withAlbumUri(params.albumUri)
1211          .withOperationEndCallback(onMoveEndFunc);
1212        menuOperation = MenuOperationFactory.getInstance().createMenuOperation(RemoveMenuOperation, menuContext);
1213        AppStorage.setOrCreate<string | undefined>(Constants.APP_KEY_NEW_ALBUM_SOURCE, this.albumInfo?.uri);
1214      } else if (params.pageType === MediaOperationType.Add) {
1215        // "添加到"不需要设置源相册
1216        let onCopyEndFunc = (err: Error, count: number, total: number): void => {
1217          this.onCopyEnd(err as Object, count, total);
1218          }
1219        menuContext.withMediaItem(currentPhoto)
1220          .withBroadCast(this.broadCast)
1221          .withTargetAlbumName(params.albumName)
1222          .withAlbumUri(params.albumUri)
1223          .withOperationEndCallback(onCopyEndFunc);
1224        menuOperation = MenuOperationFactory.getInstance().createMenuOperation(AddMenuOperation, menuContext);
1225        this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [false, this.checkedTransition]);
1226      }
1227
1228      if (menuOperation != null) {
1229        menuOperation.doAction();
1230      }
1231    }
1232    this.backFromCopy = false;
1233    TraceControllerUtils.finishTrace('PhotoBrowseronPageShow');
1234  }
1235
1236  onPageHide(): void {
1237    Log.info(TAG, `call onPageHide`);
1238    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [false, this.checkedTransition]);
1239    WindowUtil.setPreferredOrientation(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,
1240      window.Orientation.UNSPECIFIED);
1241  }
1242
1243  onMediaLibDataChange(changeType: string): void {
1244    Log.info(TAG, `onMediaLibDataChange type: ${changeType}`);
1245    this.dataSource.onChange(changeType);
1246  }
1247
1248  build() {
1249    Stack({ alignContent: Alignment.TopStart }) {
1250      PhotoBrowserComponentBg({ isShowBar: $isShowBar, isFromPhotoBrowser: true })
1251        .opacity(this.geometryOpacity)
1252        .transition(TransitionEffect.opacity(0))
1253
1254      PhotoSwiper({
1255        dataSource: this.dataSource,
1256        mTransition: this.mTransition,
1257        swiperController: this.controller,
1258        onPhotoChanged: (index: number): void => this.onPhotoChanged(index),
1259        geometryTransitionEnable: this.geometryTransitionEnable,
1260        broadCast: $broadCast,
1261        isRunningAnimation: $isRunningAnimation,
1262        isFromFACard: this.isFromFACard
1263      })
1264
1265      if (!this.isFromViewDataWithThirdUri) {
1266        PhotoBrowserActionBar({
1267          onMenuClicked: (action: Action): void => this.onMenuClicked(action),
1268        })
1269          .opacity(this.geometryOpacity)
1270          .transition(TransitionEffect.opacity(0))
1271
1272        if (!this.isHorizontal) {
1273          PhotoBrowserToolBar({
1274            onMenuClicked: (action: Action): void => this.onMenuClicked(action),
1275            isFromPhotoBrowser: true
1276          })
1277            .opacity(this.geometryOpacity)
1278            .transition(TransitionEffect.opacity(0))
1279            .markAnchor({ x: Constants.PERCENT_0, y: Constants.PERCENT_100 })
1280            .position({ x: Constants.PERCENT_0, y: Constants.PERCENT_100 })
1281        }
1282
1283        CustomDialogView({ broadCast: $broadCast })
1284          .opacity(this.geometryOpacity)
1285          .transition(TransitionEffect.opacity(0))
1286
1287      } else {
1288        PhotoBrowserActionBar({
1289          onMenuClicked: (action: Action): void => this.onMenuClicked(action),
1290        })
1291          .opacity(this.geometryOpacity)
1292          .transition(TransitionEffect.opacity(0))
1293      }
1294    }
1295  }
1296
1297  private setViewDataResult(result: boolean): void {
1298    if (!this.isFromViewDataWithMediaUri && !this.isFromViewDataWithThirdUri) {
1299      return;
1300    }
1301
1302    let resultCode = 0;
1303    if (result == false) {
1304      resultCode = -1;
1305    }
1306
1307    let abilityResult: ability.AbilityResult = {
1308      resultCode: resultCode,
1309      want: {}
1310    };
1311
1312    let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext;
1313    context.terminateSelfWithResult(abilityResult).then((result: void) => {
1314      Log.info(TAG, `terminateSelfWithResult result: ${result}`);
1315    });
1316  }
1317
1318  private downLoad(): void {
1319    Log.info(TAG, 'downLoad run');
1320    let menuContext = new MenuContext();
1321    let menuOperation: MenuOperation;
1322    let currentPhoto = this.getCurrentPhoto();
1323    let onDownloadEndFunc = async (err: Error, count: number, total: number): Promise<void> => {
1324      await this.onDownloadEnd(err as Object, count, total);
1325      }
1326    if (currentPhoto == undefined) {
1327      Log.error(TAG, 'MediaOperation currentPhoto is undefined');
1328      return;
1329    }
1330    menuContext
1331      .withMediaItem(currentPhoto)
1332      .withBroadCast(this.broadCast)
1333      .withRemoteDevice('0') // TODO input deviceId
1334      .withOperationEndCallback(onDownloadEndFunc)
1335    menuOperation = MenuOperationFactory.getInstance().createMenuOperation(AddMenuOperation, menuContext);
1336    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [false, this.checkedTransition]);
1337    menuOperation.doAction();
1338  }
1339
1340  private reportToBigDataForPhotoSlide(index: number): void {
1341    let currentPhoto = this.getCurrentPhoto();
1342    if (currentPhoto && index != this.currentIndex && this.viewTime != 0) {
1343      let currentTime = Date.now();
1344      interface Msg {
1345        type: string;
1346        duration: number;
1347      }
1348      let msg: Msg = {
1349        type: currentPhoto.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO ?
1350        BigDataConstants.VIDEO : BigDataConstants.NORMAL_PHOTO,
1351        duration: (currentTime - this.viewTime)
1352      }
1353      ReportToBigDataUtil.report(BigDataConstants.PHOTO_BROWSER_SLIDE_ID, msg);
1354      this.viewTime = Date.now();
1355    }
1356  }
1357
1358  private reportToBigDataForCameraIn(): void {
1359    if (this.clickThumbnailTime == 0 || !this.isFromCamera) {
1360      return;
1361    }
1362    interface Msg {
1363      clickThumbnailTime: number;
1364      showSinglePhoto: number;
1365      fileName?: string;
1366    }
1367    let msg: Msg = {
1368      clickThumbnailTime: this.clickThumbnailTime,
1369      showSinglePhoto: Date.now()
1370    }
1371    let fileName = ReportToBigDataUtil.getFileNameOfPhotoTakenByCamera(this.getPhotoByIndex(0));
1372    ReportToBigDataUtil.setFileNameProperty(msg, fileName);
1373    ReportToBigDataUtil.report(BigDataConstants.BROWSE_PHOTO_FROM_CAMERA_ID, msg);
1374    this.clickThumbnailTime = 0;
1375  }
1376
1377  private async syncPhotoName(currentPhoto: MediaItem): Promise<TitleName> {
1378    Log.debug(TAG, 'syncPhotoName start');
1379    let renameResult: TitleName = {title: '', displayName: ''};
1380    let fileAsset = await this.dataSource?.getDataByUri(currentPhoto?.uri);
1381    if (fileAsset) {
1382      renameResult = {
1383        title: fileAsset.get(UserFileManagerAccess.FILE_KEY_TITLE.toString()) as string,
1384        displayName: fileAsset.displayName
1385      };
1386    } else {
1387      let key: string = 'renameResult' + currentPhoto?.uri;
1388      renameResult = AppStorage.get<TitleName>(key) as TitleName;
1389      AppStorage.delete(key);
1390    }
1391    Log.debug(TAG, `syncPhotoName end, renameResult : ${JSON.stringify(renameResult)}`);
1392    return renameResult;
1393  }
1394}
1395