• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
18  Action,
19  BroadCast,
20  BroadCastConstants,
21  BroadCastManager,
22  Constants,
23  Log,
24  MediaItem,
25  mMultimodalInputManager,
26  BrowserConstants as PhotoConstants,
27  PhotoDataSource,
28  ScreenManager,
29  SelectManager,
30  SelectUtil,
31  UiUtil,
32  MediaDataSource
33} from '@ohos/common';
34import {
35  BrowserController,
36  PhotoBrowserBg,
37  PhotoSwiper,
38  ThirdSelectPhotoBrowserActionBar
39} from '@ohos/common/CommonComponents';
40import ability from '@ohos.ability.ability';
41import common from '@ohos.app.ability.common';
42
43const TAG: string = 'SelectPhotoBrowserView';
44
45interface Params {
46  pageFrom: number;
47  deviceId: string;
48  position: number;
49  transition: string;
50};
51
52// select mode
53@Component
54export struct SelectPhotoBrowserView {
55  @Provide backgroundColorResource: Resource = $r('app.color.default_background_color');
56  @State selectedCount: number = 0;
57  @State broadCast: BroadCast = new BroadCast();
58  @Provide isSelected: boolean = true;
59  @State isShowBar: boolean = true;
60  @Provide pageFrom: number = Constants.ENTRY_FROM.NORMAL;
61  @Provide canSwipe: boolean = true;
62  selectManager: SelectManager | null = null;
63  isMultiPick = true;
64  mTransition: string = '';
65  controller: SwiperController = new SwiperController();
66  @Provide isDeleting: boolean = false;
67  @Provide canEdit: boolean = false;
68
69  // swiper currentIndex, there may not be onChanged callback during data refresh, so mediaItem cannot be saved
70  @Provide('transitionIndex') currentIndex: number = 0;
71
72  // position
73  mPosition: number = 0;
74  timelineIndex: number = -1;
75  @Prop @Watch('onPageChanged') pageStatus: boolean = false;
76  @StorageLink('geometryOpacity') geometryOpacity: number = 1;
77  @State @Watch('onGeometryChanged') geometryTransitionId: string = 'default_id';
78  @Link isRunningAnimation: boolean;
79  @ObjectLink browserController: BrowserController;
80  // dataSource
81  private dataSource: PhotoDataSource = new PhotoDataSource();
82  // The global BroadCast of the application process. Event registration and destruction should be paired
83  private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
84  private geometryTransitionEnable: boolean = false;
85  private pullDownEndFunc: Function = (): void => this.pullDownEnd();
86  private dataSizeChangedFunc: Function = (size: number): void => this.onDataSizeChanged(size);;
87  private dataContentChangedFunc: Function = (size: number): void => this.dataContentChanged(size);
88  private setDisableSwipeFunc: Function = (value: boolean): void => this.setDisableSwipe(value);
89
90  private pullDownEnd(): void {
91    this.onBackPress();
92  }
93
94  private dataContentChanged(size: number): void {
95    // onContentChanged only the current item is updated
96    Log.info(TAG, `DATA_CONTENT_CHANGED : ${size}`);
97    this.onPhotoChanged(this.currentIndex);
98  }
99
100  private setDisableSwipe(value: boolean): void {
101    Log.info(TAG, `set swiper swipe ${value}`);
102    this.canSwipe = value;
103  }
104
105  onPageChanged() {
106    if (this.pageStatus) {
107      this.onPageShow();
108    } else {
109      this.onPageHide();
110    }
111  }
112
113  onGeometryChanged() {
114    AppStorage.SetOrCreate<string>('geometryTransitionBrowserId', this.geometryTransitionId);
115  }
116
117  aboutToAppear(): void {
118    Log.info(TAG, 'photoBrowser aboutToAppear');
119    this.geometryTransitionId = AppStorage.get<string>('geometryTransitionBrowserId') as string;
120    Log.info(TAG, `photoBrowser aboutToAppear  ${this.geometryTransitionId}`);
121    this.backgroundColorResource = $r('app.color.black');
122    mMultimodalInputManager.registerListener((control: number) => {
123      Log.info(TAG, `key control : ${control} index ${this.currentIndex}`);
124      if (control == 0) {
125        if (this.currentIndex > 0) {
126          this.onPhotoChanged(this.currentIndex - 1);
127        }
128      } else if (control == 1) {
129        if (this.currentIndex < this.dataSource.totalCount() - 1) {
130          this.onPhotoChanged(this.currentIndex + 1);
131        }
132      } else {
133        this.onBackPress();
134      }
135    });
136    this.selectManager = AppStorage.get<SelectManager>(Constants.PHOTO_GRID_SELECT_MANAGER) as SelectManager;
137    try {
138      let param: Params = this.browserController.selectBrowserParam as Params;
139      if (param.pageFrom) {
140        this.pageFrom = param.pageFrom;
141      }
142      if (this.pageFrom == Constants.ENTRY_FROM.RECYCLE) {
143        this.dataSource = new PhotoDataSource('Recycle');
144      } else if (this.pageFrom == Constants.ENTRY_FROM.DISTRIBUTED) {
145        this.dataSource.setDeviceId(param.deviceId);
146      }
147      this.dataSource.setAlbumDataSource(AppStorage.get<MediaDataSource>(Constants.APP_KEY_PHOTO_BROWSER) as MediaDataSource);
148      if (this.isMultiPick == true && this.selectManager) {
149        this.selectedCount = this.selectManager.getSelectedCount();
150      }
151      this.onPhotoChanged(param.position);
152      this.mTransition = param.transition;
153    } catch (e) {
154      Log.error(TAG, `param error ${e}`);
155    }
156    this.dataSource.setBroadCast(this.broadCast);
157    this.broadCast.on(PhotoConstants.PULL_DOWN_END, this.pullDownEndFunc);
158    this.broadCast.on(PhotoConstants.DATA_SIZE_CHANGED, this.dataSizeChangedFunc);
159    this.broadCast.on(PhotoConstants.DATA_CONTENT_CHANGED, this.dataContentChangedFunc);
160    this.broadCast.on(PhotoConstants.SET_DISABLE_SWIPE, this.setDisableSwipeFunc);
161    this.appBroadCast.on(BroadCastConstants.SELECT_PHOTO_BROWSER_BACK_PRESS_EVENT, this.pullDownEndFunc);
162  }
163
164  aboutToDisappear(): void {
165    this.broadCast.release();
166    this.dataSource.release();
167    mMultimodalInputManager.unregisterListener();
168    if(this.broadCast) {
169      this.broadCast.off(PhotoConstants.PULL_DOWN_END, this.pullDownEndFunc);
170      this.broadCast.off(PhotoConstants.DATA_SIZE_CHANGED, this.dataSizeChangedFunc);
171      this.broadCast.off(PhotoConstants.DATA_CONTENT_CHANGED, this.dataContentChangedFunc);
172      this.broadCast.off(PhotoConstants.SET_DISABLE_SWIPE, this.setDisableSwipeFunc);
173    }
174    this.appBroadCast.off(BroadCastConstants.SELECT_PHOTO_BROWSER_BACK_PRESS_EVENT, this.pullDownEndFunc);
175  }
176
177  onDataSizeChanged(size: number): void {
178    Log.info(TAG, `onDataSizeChanged, size is ${size}`);
179    if (size == 0) {
180      this.onBackPress();
181    }
182  }
183
184  onPhotoChanged(index: number): void {
185    this.currentIndex = index;
186    this.timelineIndex = this.dataSource.getPositionByIndex(index);
187    let currentPhoto = this.getCurrentPhoto();
188    if (currentPhoto == undefined) {
189      Log.error(TAG, 'onPhotoChanged, item is undefined');
190    } else {
191      this.isSelected = this.selectManager?.isItemSelected(currentPhoto.uri, this.timelineIndex) ?? false;
192      AppStorage.SetOrCreate<number>('placeholderIndex', this.timelineIndex);
193      this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + this.isSelected;
194      Log.info(TAG, `onPhotoChanged, index: ${index}, currentPhoto: ${currentPhoto.uri},\
195        isSelected: ${this.isSelected}  geometryTransitionId ${this.geometryTransitionId}`);
196    }
197  }
198
199  selectStateChange() {
200    Log.info(TAG, `change selected, timeline index ${this.timelineIndex}`);
201    let currentPhoto = this.getCurrentPhoto();
202    if (currentPhoto == undefined) {
203      return;
204    }
205    this.isSelected = !this.isSelected;
206    if (this.selectManager?.toggle(currentPhoto.uri, this.isSelected, this.timelineIndex)) {
207      this.selectedCount = this.selectManager?.getSelectedCount() ?? 0;
208    }
209    this.geometryTransitionId = this.browserController.pageFrom + currentPhoto.getHashCode() + this.isSelected;
210    Log.info(TAG, `selectedCount: ${this.selectedCount} after state change`)
211  }
212
213  onPageShow() {
214    Log.info(TAG, 'onPageShow');
215    this.appBroadCast.emit(BroadCastConstants.THIRD_ROUTE_PAGE, []);
216    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [true, this.mTransition]);
217  }
218
219  onPageHide() {
220    Log.info(TAG, 'onPageHide');
221    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_ACTIVE, [false, this.mTransition]);
222  }
223
224  onMenuClicked(action: Action) {
225    Log.debug(TAG, `onMenuClicked, action: ${action.actionID}`);
226    if (action.actionID === Action.BACK.actionID) {
227      this.onBackPress();
228    } else if (action.actionID === Action.MATERIAL_SELECT.actionID) {
229      Log.info(TAG, 'click UN_SELECTED');
230      this.selectStateChange();
231    } else if (action.actionID === Action.SELECTED.actionID) {
232      Log.info(TAG, 'click SELECTED');
233      this.selectStateChange();
234    } else if (action.actionID === Action.OK.actionID) {
235      Log.info(TAG, 'click OK');
236      this.setPickResult();
237    }
238  }
239
240  getCurrentPhoto(): MediaItem {
241    return this.dataSource.getData(this.currentIndex)?.data;
242  }
243
244  onBackPress() {
245    Log.info(TAG, 'onBackPress');
246    if (this.geometryTransitionEnable) {
247      this.browserController.hideSelectBrowser();
248    } else {
249      router.back({
250        url: '',
251        params: { index: this.currentIndex }
252      });
253    }
254    return true;
255  }
256
257  build() {
258    Stack({ alignContent: Alignment.TopStart }) {
259      PhotoBrowserBg({ isShowBar: $isShowBar })
260        .opacity(this.geometryOpacity)
261        .transition(TransitionEffect.opacity(0))
262      PhotoSwiper({
263        dataSource: this.dataSource,
264        mTransition: this.mTransition,
265        onPhotoChanged: (index: number): void => this.onPhotoChanged(index),
266        swiperController: this.controller,
267        geometryTransitionEnable: this.geometryTransitionEnable,
268        broadCast: $broadCast,
269        isInSelectedMode: true,
270        isRunningAnimation: $isRunningAnimation
271      })
272
273      ThirdSelectPhotoBrowserActionBar({
274        isMultiPick: this.isMultiPick,
275        onMenuClicked: (action: Action): void => this.onMenuClicked(action),
276        isShowBar: $isShowBar,
277        totalSelectedCount: $selectedCount
278      })
279        .opacity(this.geometryOpacity)
280        .transition(TransitionEffect.opacity(0))
281    }
282  }
283
284  pageTransition() {
285    PageTransitionEnter({ type: RouteType.None, duration: PhotoConstants.PAGE_SHOW_ANIMATION_DURATION })
286      .opacity(0)
287    PageTransitionExit({ duration: PhotoConstants.PAGE_SHOW_ANIMATION_DURATION })
288      .opacity(0)
289  }
290
291  private setPickResult(): void {
292    let uriArray: string[];
293    if (this.isMultiPick) {
294      uriArray = SelectUtil.getUriArray(this.selectManager?.clickedSet ?? new Set());
295      Log.debug(TAG, `uri size: ${uriArray}`);
296    } else {
297      let currentPhoto = this.getCurrentPhoto();
298      if (currentPhoto == undefined) {
299        return;
300      }
301      uriArray = [currentPhoto.uri];
302    }
303    let abilityResult: ability.AbilityResult = {
304      resultCode: 0,
305      want: {
306        parameters: {
307          'select-item-list': uriArray
308        }
309      }
310    };
311    let context: common.UIAbilityContext = AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext;
312    context.terminateSelfWithResult(abilityResult).then((result: void) => {
313      Log.info(TAG, `terminateSelfWithResult result: ${result}`);
314    });
315  }
316}
317