• 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  AlbumInfo,
20  BroadCast,
21  BroadCastConstants,
22  BroadCastManager,
23  Constants,
24  JumpSourceToMain,
25  Log,
26  MediaDataSource,
27  MediaItem,
28  ScreenManager,
29  SelectManager,
30  TraceControllerUtils,
31  UiUtil,
32  ViewData,
33} from '@ohos/common';
34import {
35  BrowserController,
36  CustomDialogView,
37  GridScrollBar,
38  ImageGridItemComponent,
39  MoveOrCopyBroadCastProp,
40  NoPhotoComponent
41} from '@ohos/common/CommonComponents';
42import { AlbumSelectActionBar } from '@ohos/browser/BrowserComponents';
43import { PhotoBrowserComponent } from '../view/PhotoBrowserComponent';
44import { SelectPhotoBrowserView } from '../view/SelectPhotoBrowserView';
45
46const TAG: string = 'NewAlbumPage';
47AppStorage.SetOrCreate('PhotoGridPageIndex', Constants.INVALID);
48
49interface Params {
50  item: string;
51}
52
53@Entry
54@Component
55export struct NewAlbumPage {
56  @State isEmpty: boolean = false;
57  @State isShowScrollBar: boolean = false;
58  @State gridRowCount: number = 0;
59  @Provide isSelectedMode: boolean = true;
60  @Provide isAllSelected: boolean = false;
61  @State totalSelectedCount: number = 0;
62  @StorageLink('isHorizontal') isHorizontal: boolean = ScreenManager.getInstance().isHorizontal();
63  @Provide broadCast: BroadCast = new BroadCast();
64  @Provide isShow: boolean = true;
65  @Provide isShowBar: boolean = true;
66  @State moreMenuList: Array<Action> = new Array<Action>();
67  @Provide rightClickMenuList: Array<Action> = new Array<Action>();
68  @State isClickScrollBar: boolean = false;
69  @StorageLink('PhotoGridPageIndex') @Watch('onIndexChange') PhotoGridPageIndex: number = Constants.INVALID;
70  @StorageLink('isSplitMode') isSplitMode: boolean = ScreenManager.getInstance().isSplitMode();
71  @StorageLink('leftBlank') leftBlank: number[]
72    = [0, ScreenManager.getInstance().getStatusBarHeight(), 0, ScreenManager.getInstance().getNaviBarHeight()];
73  title: string = '';
74  @StorageLink('placeholderIndex') @Watch('onPlaceholderChanged') placeholderIndex: number = -1;
75  @State pageStatus: boolean = false;
76  @State isRunningAnimation: boolean = false;
77  @State @Watch('updateAnimationStatus') browserController: BrowserController = new BrowserController(true);
78  private dataSource: MediaDataSource = new MediaDataSource(Constants.DEFAULT_SLIDING_WIN_SIZE);
79  private scroller: Scroller = new Scroller();
80  private isDataFreeze = false;
81  private mSelectManager: SelectManager | null = null;
82  private isActive = false;
83  private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
84  private isNewAlbum: boolean = AppStorage.get<boolean>(Constants.APP_KEY_NEW_ALBUM) as boolean;
85  private onWindowSizeChangeCallBack: Function = () => {
86    // 后续phone缩略图支持横竖屏后再放开
87    // this.initGridRowCount;
88  }
89  private onUpdateFavorStateFunc: Function = (item: MediaItem): void => this.onUpdateFavorState(item);
90  private selectFunc: Function = (position: number, key: string, value: boolean, callback: Function): void => this.select(position, key, value, callback);
91  private jumpPhotoBrowserFunc: Function = (name: string, item: MediaItem): void => this.jumpPhotoBrowser(name, item);
92  private jumpThirdPhotoBrowserFunc: Function = (name: string, item: MediaItem, geometryTapIndex: number, geometryTransitionString: string): void =>
93  this.jumpThirdPhotoBrowser(name, item, geometryTapIndex, geometryTransitionString);
94  private onDataReloadedFunc: Function = (size: number): void => this.onDataReloaded(size);
95  private onLoadingFinishedFunc: Function = (): void => this.onLoadingFinished();
96
97  private select(position: number, key: string, value: boolean, callback: Function): void {
98      if (this.mSelectManager?.toggle(key, value, position)) {
99      Log.info(TAG, 'enter event process');
100      callback();
101    }
102  }
103
104  private jumpPhotoBrowser(name: string, item: MediaItem): void {
105    let targetIndex = this.dataSource.getDataIndex(item);
106    if (targetIndex == Constants.NOT_FOUND) {
107      Log.error(TAG, 'targetIndex is not found');
108      return;
109    }
110    Log.info(TAG, `jump to photo browser at index: ${targetIndex}`);
111    AppStorage.SetOrCreate(Constants.APP_KEY_PHOTO_BROWSER, this.dataSource);
112    interface Params {
113      position: number;
114      transition: string;
115      leftBlank: number[];
116    }
117    let params: Params = {
118      position: targetIndex,
119      transition: name,
120      leftBlank: this.leftBlank,
121    }
122    this.browserController.showBrowserWithNoAnimation(params);
123  }
124
125  private jumpThirdPhotoBrowser(name: string, item: MediaItem, geometryTapIndex: number, geometryTransitionString: string): void {
126    let targetIndex = this.dataSource.getDataIndex(item);
127    Log.info(TAG, `jump to photo browser, index: ${targetIndex}, transition: ${name}`);
128    AppStorage.SetOrCreate(Constants.PHOTO_GRID_SELECT_MANAGER, this.mSelectManager);
129    AppStorage.SetOrCreate(Constants.APP_KEY_PHOTO_BROWSER, this.dataSource);
130    interface Params {
131      position: number;
132      transition: string;
133      leftBlank: number[];
134    }
135
136    const params: Params = {
137      position: targetIndex,
138      transition: name,
139      leftBlank: this.leftBlank,
140    };
141    if (geometryTapIndex && geometryTransitionString) {
142      this.browserController.showSelectBrowser(geometryTapIndex, geometryTransitionString, TAG, params);
143    } else {
144      this.browserController.showSelectBrowserWithNoAnimation(params);
145    }
146  }
147
148  private onDataReloaded(size: number): void {
149    Log.info(TAG, `ON_LOADING_FINISHED size: ${size}`);
150    this.isEmpty = size == 0;
151    Log.info(TAG, `isEmpty: ${this.isEmpty}`);
152  }
153
154  private onLoadingFinished(): void {
155    Log.info(TAG, 'ON_DATA_RELOADED');
156    this.dataSource.onDataReloaded();
157  }
158
159  onIndexChange() {
160    Log.info(TAG, `onIndexChange ${this.PhotoGridPageIndex}`)
161    if (this.PhotoGridPageIndex != Constants.INVALID) {
162      this.scroller.scrollToIndex(this.PhotoGridPageIndex);
163    }
164  }
165
166  onPlaceholderChanged() {
167    Log.debug(TAG, 'onPlaceholderChanged placeholderIndex is ' + this.placeholderIndex);
168    if (this.placeholderIndex != -1) {
169      this.scroller.scrollToIndex(this.placeholderIndex);
170    }
171  }
172
173  onMenuClicked(action: Action) {
174    Log.info(TAG, `onMenuClicked, action: ${action.actionID}`);
175    if (action.actionID === Action.CANCEL.actionID) {
176      router.back();
177    } else if (action.actionID === Action.OK.actionID) {
178      if (this.mSelectManager?.getSelectedCount() == 0) {
179        Log.info(TAG, `onMenuClicked, action: ${action.actionID}, count = 0`);
180      }
181      Log.info(TAG, `onMenuClicked, action: ${action.actionID} newAlbum: ${this.isNewAlbum}`);
182      if (this.isNewAlbum) {
183        AppStorage.SetOrCreate(Constants.IS_SHOW_MOVE_COPY_DIALOG, true);
184        let url = 'pages/index';
185        router.back({
186          url: url,
187          params: {
188            jumpSource: JumpSourceToMain.ALBUM,
189          }
190        })
191      } else {
192        MoveOrCopyBroadCastProp.getInstance().doAddOperation(this.broadCast);
193      }
194    }
195  }
196
197  onModeChange() {
198    Log.info(TAG, 'onModeChange');
199  }
200
201  onPageShow() {
202    this.appBroadCast.emit(BroadCastConstants.THIRD_ROUTE_PAGE, []);
203    this.isShow = true;
204    this.pageStatus = this.isShow;
205    this.onActive();
206  }
207
208  onPageHide() {
209    this.isShow = false;
210    this.pageStatus = this.isShow;
211    this.onInActive();
212  }
213
214  onActive() {
215    if (!this.isActive) {
216      Log.info(TAG, 'onActive');
217      this.isActive = true;
218
219      this.dataSource && this.dataSource.onActive();
220      if (this.isSelectedMode && this.mSelectManager) {
221        this.totalSelectedCount = this.mSelectManager.getSelectedCount();
222        this.dataSource.forceUpdate();
223      }
224    }
225  }
226
227  onInActive() {
228    if (this.isActive) {
229      Log.info(TAG, 'onInActive');
230      this.isActive = false;
231      this.dataSource && this.dataSource.onInActive();
232    }
233  }
234
235  onUpdateFavorState(item: MediaItem) {
236    Log.debug(TAG, 'onUpdateFavorState');
237    let index = this.dataSource.getIndexByMediaItem(item);
238    if (index != -1) {
239      this.dataSource.onDataChanged(index);
240    }
241  }
242
243  onBackPress() {
244    if (this.browserController.isBrowserShow) {
245      this.doPhotoBrowserViewBack();
246      return true;
247    }
248    if (this.browserController.isSelectBrowserShow) {
249      this.doSelectPhotoBrowserViewBack();
250      return true;
251    }
252    return false;
253  }
254
255  doSelectPhotoBrowserViewBack() {
256    this.appBroadCast.emit(BroadCastConstants.SELECT_PHOTO_BROWSER_BACK_PRESS_EVENT, []);
257  }
258
259  doPhotoBrowserViewBack() {
260    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_BACK_PRESS_EVENT, []);
261  }
262
263  aboutToAppear(): void {
264    TraceControllerUtils.startTrace('PhotoGridPageAboutToAppear');
265    this.mSelectManager = AppStorage.Get<SelectManager>(Constants.APP_KEY_NEW_ALBUM_SELECTED) as SelectManager;
266    if (this.mSelectManager == null) {
267      this.mSelectManager = new SelectManager();
268      AppStorage.SetOrCreate(Constants.APP_KEY_NEW_ALBUM_SELECTED, this.mSelectManager);
269    }
270    let param: Params = router.getParams() as Params;
271    if (param != null) {
272      Log.debug(TAG, `After router.getParams, param is: ${JSON.stringify(param)}`);
273      let item: AlbumInfo = JSON.parse(param.item) as AlbumInfo;
274      this.title = item.albumName;
275      this.dataSource.setAlbumUri(item.uri);
276      AppStorage.SetOrCreate(Constants.APP_KEY_NEW_ALBUM_SOURCE, item.uri);
277    } else {
278      this.title = '';
279      this.dataSource.setAlbumUri("");
280    }
281
282    let self = this;
283    this.dataSource.setBroadCast(this.broadCast)
284    this.mSelectManager.setPhotoDataImpl();
285    this.initGridRowCount();
286    ScreenManager.getInstance().on(ScreenManager.ON_WIN_SIZE_CHANGED, this.onWindowSizeChangeCallBack);
287    this.broadCast.on(BroadCastConstants.SELECT, this.selectFunc);
288    this.broadCast.on(BroadCastConstants.JUMP_PHOTO_BROWSER, this.jumpPhotoBrowserFunc);
289    this.broadCast.on(BroadCastConstants.JUMP_THIRD_PHOTO_BROWSER, this.jumpThirdPhotoBrowserFunc);
290    this.broadCast.on(Constants.ON_LOADING_FINISHED, this.onDataReloadedFunc);
291    this.broadCast.on(BroadCastConstants.ON_DATA_RELOADED, this.onLoadingFinishedFunc);
292
293    this.appBroadCast.on(BroadCastConstants.UPDATE_DATA_SOURCE, this.onUpdateFavorStateFunc);
294    AppStorage.SetOrCreate(Constants.PHOTO_GRID_SELECT_MANAGER, this.mSelectManager);
295    this.mSelectManager.registerCallback('allSelect', (newState: boolean) => {
296      Log.info(TAG, `allSelect ${newState}`);
297      this.isDataFreeze = AppStorage.get<boolean>(Constants.IS_DATA_FREEZE) as boolean;
298      if (this.isDataFreeze) {
299        return;
300      }
301      this.isAllSelected = newState;
302      this.dataSource.forceUpdate();
303    });
304    this.mSelectManager.registerCallback('select', (newState: number) => {
305      Log.info(TAG, `select ${newState}`);
306      this.isDataFreeze = AppStorage.get<boolean>(Constants.IS_DATA_FREEZE) as boolean;
307      if (this.isDataFreeze) {
308        return;
309      }
310      this.dataSource.onDataChanged(newState);
311    });
312    this.mSelectManager.registerCallback('updateCount', (newState: number) => {
313      Log.info(TAG, `updateSelectedCount ${newState}`);
314      this.isDataFreeze = AppStorage.get<boolean>(Constants.IS_DATA_FREEZE) as boolean;
315      if (this.isDataFreeze) {
316        return;
317      }
318      this.moreMenuList = [];
319      this.moreMenuList.push(Boolean(newState) ? Action.INFO : Action.INFO_INVALID);
320      this.totalSelectedCount = newState;
321    });
322    this.dataSource.registerCallback('updateCount', (newState: number) => {
323      Log.info(TAG, `updateTotalCount ${newState}`);
324      self.isShowScrollBar = (newState > Constants.PHOTOS_CNT_FOR_HIDE_SCROLL_BAR);
325      self.mSelectManager?.setTotalCount(newState);
326    })
327
328    this.moreMenuList = [];
329    this.moreMenuList.push(Action.INFO);
330    TraceControllerUtils.finishTrace('PhotoGridPageAboutToAppear');
331  }
332
333  aboutToDisappear(): void {
334    if(this.broadCast) {
335      this.broadCast.off(BroadCastConstants.SELECT, this.selectFunc);
336      this.broadCast.off(BroadCastConstants.JUMP_PHOTO_BROWSER, this.jumpPhotoBrowserFunc);
337      this.broadCast.off(BroadCastConstants.JUMP_THIRD_PHOTO_BROWSER, this.jumpThirdPhotoBrowserFunc);
338      this.broadCast.off(Constants.ON_LOADING_FINISHED, this.onDataReloadedFunc);
339      this.broadCast.off(BroadCastConstants.ON_DATA_RELOADED, this.onLoadingFinishedFunc);
340    }
341    this.appBroadCast.off(BroadCastConstants.UPDATE_DATA_SOURCE, this.onUpdateFavorStateFunc);
342    this.dataSource.releaseBroadCast();
343    ScreenManager.getInstance().off(ScreenManager.ON_WIN_SIZE_CHANGED, this.onWindowSizeChangeCallBack);
344    AppStorage.Delete(Constants.PHOTO_GRID_SELECT_MANAGER);
345  }
346
347  build() {
348    Stack() {
349      Column() {
350        AlbumSelectActionBar({
351          onMenuClicked: (action: Action): void => this.onMenuClicked(action),
352          totalSelectedCount: $totalSelectedCount,
353          menuList: $moreMenuList
354        })
355        if (this.isEmpty) {
356          NoPhotoComponent({
357            title: $r('app.string.no_distributed_photo_head_title_album')
358          })
359        } else {
360          Stack() {
361            Grid(this.scroller) {
362              LazyForEach(this.dataSource, (item: ViewData, index?: number) => {
363                if (!!item) {
364                  GridItem() {
365                    ImageGridItemComponent({
366                      dataSource: this.dataSource,
367                      item: item.mediaItem,
368                      isSelected: this.isSelectedMode ? this.mSelectManager?.isItemSelected((item.mediaItem as MediaItem).uri as string, item.viewIndex) : false,
369                      pageName: Constants.PHOTO_TRANSITION_ALBUM,
370                      mPosition: index,
371                      geometryTransitionString: this.getGeometryTransitionId(item, index as number),
372                      selectedCount: $totalSelectedCount
373                    })
374                  }
375                  .aspectRatio(1)
376                  .columnStart(item.viewIndex % this.gridRowCount)
377                  .columnEnd(item.viewIndex % this.gridRowCount)
378                  .key('NewAlbumPageImage' + index)
379                  .zIndex(index === this.placeholderIndex ? 1 : 0)
380                }
381              },  (item: ViewData, index?: number) => {
382                if (item == null || item == undefined) {
383                  return JSON.stringify(item) + index;
384                }
385                return this.getGeometryTransitionId(item, index as number);
386              })
387            }
388            .edgeEffect(EdgeEffect.Spring)
389            .columnsTemplate('1fr '.repeat(this.gridRowCount))
390            .columnsGap(Constants.GRID_GUTTER)
391            .rowsGap(Constants.GRID_GUTTER)
392            .cachedCount(Constants.GRID_CACHE_ROW_COUNT)
393
394            if (this.isShowScrollBar) {
395              GridScrollBar({ scroller: this.scroller });
396            }
397          }
398          .layoutWeight(1)
399        }
400        CustomDialogView({ broadCast: $broadCast })
401      }
402      .backgroundColor($r('app.color.default_background_color'))
403      .margin({
404        top: this.leftBlank[1],
405        bottom: this.leftBlank[3]
406      })
407
408      if (this.browserController.isBrowserShow) {
409        Column() {
410          PhotoBrowserComponent({
411            pageStatus: this.pageStatus,
412            geometryTransitionEnable: true,
413            isRunningAnimation: $isRunningAnimation,
414            browserController: this.browserController
415          })
416        }
417        .width("100%")
418        .height("100%")
419
420        // Opacity must change for TransitionEffect taking effect
421        .transition(TransitionEffect.asymmetric(TransitionEffect.opacity(0.99), TransitionEffect.opacity(0.99)))
422      }
423
424      if (this.browserController.isSelectBrowserShow) {
425        Column() {
426          SelectPhotoBrowserView({
427            pageStatus: this.pageStatus,
428            geometryTransitionEnable: true,
429            isRunningAnimation: $isRunningAnimation,
430            browserController: this.browserController
431          })
432        }
433        .width("100%")
434        .height("100%")
435
436        // Opacity must change for TransitionEffect taking effect
437        .transition(TransitionEffect.asymmetric(TransitionEffect.opacity(0.99), TransitionEffect.opacity(0.99)))
438      }
439    }
440  }
441
442  pageTransition() {
443    PageTransitionEnter({ type: RouteType.Pop, duration: 300 })
444      .opacity(1)
445    PageTransitionExit({ type: RouteType.Push, duration: 300 })
446      .opacity(1)
447  }
448
449  private updateAnimationStatus() {
450    this.isRunningAnimation = this.browserController.isAnimating;
451  }
452
453  private getGeometryTransitionId(item: ViewData, index: number): string {
454    let mediaItem = item.mediaItem as MediaItem;
455    if (mediaItem) {
456      return TAG + mediaItem.getHashCode() + (this.mSelectManager?.isItemSelected(mediaItem.uri as string) ?? false);
457    } else {
458      return TAG + item.viewIndex;
459    }
460  }
461
462  private initGridRowCount(): void {
463    let contentWidth = ScreenManager.getInstance().getWinWidth();
464    let margin = 0;
465    let maxThumbWidth = px2vp(Constants.GRID_IMAGE_SIZE) * Constants.GRID_MAX_SIZE_RATIO;
466    this.gridRowCount = Math.max(Math.round(((contentWidth - Constants.NUMBER_2 * margin)
467      + Constants.GRID_GUTTER) / (maxThumbWidth + Constants.GRID_GUTTER)), Constants.DEFAULT_ALBUM_GRID_COLUMN_MIN_COUNT);
468    Log.info(TAG, `initGridRowCount contentWidth: ${contentWidth}`);
469  }
470}