• 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 abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
17import app from '@system.app';
18import {
19  BigDataConstants,
20  BreakpointSystem,
21  BreakPointType,
22  BroadCast,
23  BroadCastConstants,
24  BroadCastManager,
25  BrowserConstants,
26  Constants,
27  JumpSourceToMain,
28  Log,
29  ReportToBigDataUtil,
30  ScreenManager,
31  TraceControllerUtils,
32  WindowUtil
33} from '@ohos/common';
34import {
35  BrowserController,
36  DEVICE_TYPE,
37  TabBar,
38  TabItem
39} from '@ohos/common/CommonComponents';
40import router from '@ohos.router';
41import { PhotoGridView } from '../view/PhotoGridView';
42import { TabContentComponent } from '../view/TabContentComponent';
43import { TimelineTabContentComponent } from '../view/TimelineContentComponent';
44
45import data_preferences from '@ohos.data.preferences';
46import common from '@ohos.app.ability.common';
47import { BusinessError } from '@ohos.base';
48import { Router } from '@ohos.arkui.UIContext';
49
50export type Preferences = data_preferences.Preferences;
51
52const TAG: string = 'index';
53const COMPONENT_KEY_PHOTOS: string = 'Photos';
54const COMPONENT_KEY_ALBUMS: string = 'Albums';
55const IMAGE_CACHE_COUNT: number = 100;
56const IMAGE_CACHE_SIZE: number = 1024 * 1024 * IMAGE_CACHE_COUNT;
57
58interface Params {
59  jumpSource: number;
60};
61
62// Application entry
63@Entry
64@Component
65struct IndexPage {
66  @StorageLink('app_key_tabs_index') preIndex: number = Constants.TIMELINE_PAGE_INDEX;
67  @StorageLink('isSplitMode') isSplitMode: boolean = ScreenManager.getInstance().isSplitMode();
68  @StorageLink('leftBlank') leftBlank: number[]
69    = [
70  Constants.NUMBER_0,
71  ScreenManager.getInstance().getStatusBarHeight(),
72  Constants.NUMBER_0,
73  ScreenManager.getInstance().getNaviBarHeight()
74  ];
75  @StorageLink('isSidebar') isSidebar: boolean = ScreenManager.getInstance().isSidebar();
76  @Provide isShow: boolean = true;
77  @Provide screenHeight: number = 0.0;
78  @Provide screenWidth: number = 0.0;
79  @Provide isSelectedMode: boolean = false;
80  @Provide isAlbumSetSelectedMode: boolean = false;
81  @Provide isShowSideBar: boolean = false;
82  @State currentIndex: number = this.preIndex;
83  @StorageLink('entryFromHap') entryFromHap: number = Constants.ENTRY_FROM_NONE;
84  @State controlButtonMarginLeft: number = 16;
85  @StorageLink('deviceType') deviceType: string = AppStorage.get<string>('deviceType') as string;
86  @StorageLink('currentBreakpoint') @Watch('updateParameters') currentBreakpoint: string = Constants.BREAKPOINT_MD;
87  @StorageLink('isShowPhotoGridView') @Watch('doAnimation') isShowPhotoGridView: boolean = false;
88  @State isShowTabBar: boolean = true;
89  @State pageStatus: boolean = false;
90  @State isShowPhotoBrowser: boolean = false;
91  @State isShowSelectPhotoBrowser: boolean = false;
92  @State browserController: BrowserController = new BrowserController(true);
93  private tabs: TabItem[] = [
94    new TabItem($r('app.string.tab_timeline'), $r('app.media.ic_photos'), $r('app.media.ic_photos_actived'), false, $r('sys.color.ohos_id_color_bottom_tab_text_off'), COMPONENT_KEY_PHOTOS),
95    new TabItem($r('app.string.tab_albums'), $r('app.media.ic_albums'), $r('app.media.ic_albums_actived'), false, $r('sys.color.ohos_id_color_bottom_tab_text_off'), COMPONENT_KEY_ALBUMS)
96  ];
97  private tabsController: TabsController = new TabsController();
98  private appBroadCast: BroadCast = BroadCastManager.getInstance().getBroadCast();
99  private jumpSource: number = 0;
100  private breakpointSystem: BreakpointSystem = new BreakpointSystem();
101  private photosPreferences: Preferences | null = null;
102  @State isShowBar: boolean = true;
103
104  doAnimation(): void {
105    if (this.isSidebar && this.currentBreakpoint !== Constants.BREAKPOINT_LG) {
106      this.isSidebar = this.isShowPhotoGridView ? false : true;
107    }
108    animateTo({
109      duration: this.isShowPhotoGridView ?
110      BrowserConstants.PHONE_LINK_IN_TAB_BAR_DURATION : BrowserConstants.PHONE_LINK_OUT_TAB_BAR_DURATION,
111      curve: Curve.Sharp
112    }, () => {
113      this.isShowTabBar = !this.isShowTabBar;
114    })
115  }
116
117  aboutToDisappear(): void {
118    Log.info(TAG, '[aboutToDisappear]');
119    this.breakpointSystem.unregister();
120  }
121
122  updateParameters(): void {
123    this.isSidebar = new BreakPointType({
124      sm: false,
125      md: false,
126      lg: true
127    }).getValue(this.currentBreakpoint);
128  }
129
130  initPhotosStore() {
131    this.photosPreferences = AppStorage.get<Preferences>(Constants.PHOTOS_STORE_KEY) as Preferences;
132    if (this.photosPreferences) {
133      this.photosPreferences.get('lastPage', 0).then((data: data_preferences.ValueType) => {
134        this.preIndex = data as number;
135        this.currentIndex = this.preIndex;
136      }).catch((err: Error) => {
137        this.updatePhotosStore('lastPage', 0);
138      });
139    } else {
140      Log.info(TAG, 'photosPreferences is undefined');
141    }
142  }
143
144  updatePhotosStore(key: string, value: number): void {
145    if (this.photosPreferences) {
146      this.photosPreferences.put(key, value).then((): void => {
147        Log.debug(TAG, `Succeeded in putting the value of '${key}'.`);
148        this.photosPreferences?.flush();
149      }).catch((err: Error) => {
150        Log.error(TAG, `Failed to put the value of '${key}'. Cause: ${err}`);
151      });
152    }
153  }
154
155  aboutToAppear(): void {
156    TraceControllerUtils.startTrace('indexPageAppearToShow');
157    this.isShowBar = true;
158    ScreenManager.getInstance().setSystemUi(true);
159    this.breakpointSystem.register();
160    this.appBroadCast.on('hideBar', () => {
161      if (this.isShowBar) {
162        this.isShowBar = false;
163      } else {
164        this.isShowBar = true;
165      }
166    });
167    this.updateParameters();
168    let param: Params = router.getParams() as Params;
169    Log.info(TAG, `[aboutToAppear] param: ${JSON.stringify(param)}`);
170    this.requestPermissions();
171    if (param != null) {
172      this.jumpSource = param.jumpSource;
173      if (this.jumpSource == JumpSourceToMain.CAMERA) {
174        this.entryFromHap = Constants.ENTRY_FROM_NONE;
175        this.currentIndex = Constants.TIMELINE_PAGE_INDEX;
176        Log.info(TAG, `Camera in, switch to Tab ${this.currentIndex}.`);
177        interface Msg {
178          type: string;
179        }
180        let msg: Msg = {
181          type: BigDataConstants.ENTER_BY_CAMERA
182        };
183        ReportToBigDataUtil.report(BigDataConstants.ENTER_PHOTOS_ID, msg);
184      }
185    } else {
186      this.initPhotosStore();
187    }
188  }
189
190  onPageShow(): void {
191    Log.info(TAG, `[onPageShow] entryFromHap: ${this.entryFromHap}`);
192    if (typeof AppStorage.get<boolean|undefined>('IsSetImageRawDataCacheSize') === 'undefined') {
193      Log.info(TAG, '[onPageShow] setImageRawDataCacheSize');
194
195      // ImageCacheCount:缓存解码后的图片,默认为0
196      app.setImageCacheCount(IMAGE_CACHE_COUNT);
197      // ImageRawDataCache:缓存解码前的图片数据和缩略图的数据(datashare thumbnail格式)
198      app.setImageRawDataCacheSize(IMAGE_CACHE_SIZE);
199      AppStorage.setOrCreate<boolean>('IsSetImageRawDataCacheSize', true);
200    }
201    this.appBroadCast.emit(BroadCastConstants.THIRD_ROUTE_PAGE, []);
202    setTimeout(() => {
203      this.isShow = true
204    }, 50);
205    let param: Params =router.getParams() as Params;
206    if (param != null) {
207      this.jumpSource = param.jumpSource;
208    }
209    Log.info(TAG, `router clear ${this.jumpSource}, routerLength=${router.getLength}`);
210    if (this.jumpSource == JumpSourceToMain.CAMERA) {
211      router.clear();
212    } else if (this.jumpSource == JumpSourceToMain.ALBUM) {
213      router.clear();
214
215      // To help AlbumSetPage show copy or move dialog
216      if (AppStorage.get<boolean>(Constants.IS_SHOW_MOVE_COPY_DIALOG)) {
217        this.appBroadCast.emit(BroadCastConstants.SEND_COPY_OR_MOVE_BROADCAST, [this.currentIndex]);
218        AppStorage.SetOrCreate(Constants.IS_SHOW_MOVE_COPY_DIALOG, false);
219      }
220    }
221    this.pageStatus = true;
222    TraceControllerUtils.finishTrace('indexPageAppearToShow');
223  }
224
225  onPageHide(): void {
226    Log.info(TAG, '[onPageHide]');
227    this.pageStatus = false;
228    setTimeout(() => {
229      this.isShow = false
230    }, 50);
231  }
232
233  onBackPress(): boolean {
234    if (this.isShowPhotoBrowser) {
235      this.doPhotoBrowserViewBack();
236      return true;
237    }
238    if (this.isShowSelectPhotoBrowser) {
239      this.doSelectPhotoBrowserViewBack();
240      return true;
241    }
242    if (this.currentIndex === Constants.ALBUM_PAGE_INDEX) {
243      if (this.isShowPhotoGridView) {
244        if (this.isSelectedMode) {
245          this.isSelectedMode = !this.isSelectedMode;
246        } else {
247          this.appBroadCast.emit(BroadCastConstants.DO_ANIMATION, []);
248        }
249        return true;
250      } else {
251        if (this.isAlbumSetSelectedMode) {
252          this.isAlbumSetSelectedMode = !this.isAlbumSetSelectedMode;
253          return true;
254        } else {
255          return false;
256        }
257      }
258    }
259    let isProcessed = false;
260    this.appBroadCast.emit(BroadCastConstants.BACK_PRESS_EVENT,
261      [(isModeChanged: boolean): void => { isProcessed = isModeChanged; }]);
262    return isProcessed;
263  }
264
265  doSelectPhotoBrowserViewBack() {
266    this.appBroadCast.emit(BroadCastConstants.SELECT_PHOTO_BROWSER_BACK_PRESS_EVENT, []);
267  }
268
269  doPhotoBrowserViewBack() {
270    this.appBroadCast.emit(BroadCastConstants.PHOTO_BROWSER_BACK_PRESS_EVENT, []);
271  }
272
273  build() {
274    Row() {
275      if (this.entryFromHap == Constants.ENTRY_FROM_NONE) {
276        Stack() {
277          Tabs({
278            barPosition: BarPosition.Start,
279            index: this.currentIndex,
280            controller: this.tabsController
281          }) {
282            TabContent() {
283              Column() {
284                TimelineTabContentComponent({
285                  currentIndex: this.currentIndex,
286                  isShowTabBar: $isShowTabBar,
287                  isShowPhotoBrowser: $isShowPhotoBrowser,
288                  isShowSelectPhotoBrowser: $isShowSelectPhotoBrowser,
289                  pageStatus: this.pageStatus
290                })
291              }
292              .width('100%')
293              .height('100%')
294              .visibility(this.currentIndex === Constants.TIMELINE_PAGE_INDEX ? Visibility.Visible : Visibility.Hidden)
295            }
296
297            TabContent() {
298              TabContentComponent({
299                currentIndex: this.currentIndex,
300                isShowTabBar: $isShowTabBar,
301                isShowPhotoBrowser: $isShowPhotoBrowser,
302                isShowSelectPhotoBrowser: $isShowSelectPhotoBrowser,
303                pageStatus: this.pageStatus
304              })
305                .visibility(this.currentIndex === Constants.ALBUM_PAGE_INDEX ? Visibility.Visible : Visibility.Hidden)
306            }
307          }
308          .animationDuration(Constants.NUMBER_0)
309          .vertical(true)
310          .scrollable(false)
311          .barMode(BarMode.Fixed)
312          .barWidth(Constants.NUMBER_0)
313          .barHeight(Constants.NUMBER_0)
314          .flexGrow(Constants.NUMBER_1)
315          .onChange((index: number) => {
316            AppStorage.SetOrCreate<string>(Constants.KEY_OF_ALBUM_ID, '');
317            AppStorage.SetOrCreate<string>(Constants.KEY_OF_ALBUM_URI, '');
318            this.resetTabState(this.currentIndex)
319            this.onTabChanged(index);
320            Log.info(TAG, `Switch to Tab ${this.currentIndex}.`)
321          })
322          .padding({ left: this.isSidebar ? $r('app.float.tab_bar_width') : Constants.NUMBER_0 })
323
324          TabBar({
325            currentIndex: $currentIndex,
326            tabs: this.tabs,
327            controller: this.tabsController,
328            isSidebar: $isSidebar,
329            deviceType: DEVICE_TYPE.PHONE_LIKE
330          })
331            .visibility(this.isShowTabBar ? Visibility.Visible : Visibility.Hidden)
332          if (this.isShowPhotoGridView && this.currentBreakpoint == Constants.BREAKPOINT_LG) {
333            PhotoGridView({
334              pageStatus: this.pageStatus,
335              browserController: this.browserController
336            })
337              .transition(TransitionEffect.opacity(0.99))
338          }
339        }
340        .alignContent(Alignment.BottomStart)
341        .flexGrow(Constants.NUMBER_1)
342      }
343    }
344    .backgroundColor($r('app.color.default_background_color'))
345    .padding({
346      top: this.leftBlank[1],
347      bottom: this.isShowBar ?  this.leftBlank[3] : this.leftBlank[1]
348    })
349
350  }
351
352  pageTransition() {
353    PageTransitionEnter({ duration: 300 })
354      .opacity(1)
355    PageTransitionExit({ duration: 300 })
356      .opacity(1)
357  }
358
359  // Reset the status of the removed tab. It is currently in the selection mode status (index is before switching)
360  private resetTabState(index: number): void {
361    this.appBroadCast.emit(BroadCastConstants.RESET_STATE_EVENT, [index]);
362  }
363
364  // Tab page switching callback (index after switching)
365  private onTabChanged(index: number): void {
366    this.updatePhotosStore('lastPage', index);
367    this.currentIndex = index;
368    this.preIndex = this.currentIndex;
369    this.appBroadCast.emit(BroadCastConstants.ON_TAB_CHANGED, [index]);
370  }
371
372  private async requestPermissions(): Promise<void> {
373    this.photosPreferences = AppStorage.get<Preferences>(Constants.PHOTOS_STORE_KEY) as Preferences;
374    let isRequested: boolean = false;
375    if (this.photosPreferences) {
376      isRequested = await this.photosPreferences.get(Constants.PHOTOS_PERMISSION_FLAG, false) as boolean;
377    } else {
378      Log.warn(TAG, 'photos preferences is undefined.');
379    }
380    Log.info(TAG, `Has permission been requested? ${isRequested}`);
381    if (!isRequested) {
382      let permissionList: Array<string> = [
383        "ohos.permission.READ_IMAGEVIDEO",
384        "ohos.permission.WRITE_IMAGEVIDEO",
385        "ohos.permission.MEDIA_LOCATION"
386      ];
387      let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
388      try {
389        atManager.requestPermissionsFromUser(AppStorage.get<common.UIAbilityContext>('photosAbilityContext') as common.UIAbilityContext,  permissionList as Permissions[])
390          .then((data) => {
391          Log.debug(TAG, `permissions: ${JSON.stringify(data.permissions)}`
392          + `, authResult: ${JSON.stringify(data.authResults)}`);
393          let sum: number = 0;
394          for (let i = 0; i < data.authResults.length; i++) {
395            sum += data.authResults[i];
396          }
397          Log.info(TAG, `permissions sum: ${sum}`);
398          if (this.photosPreferences) {
399            this.photosPreferences.put(Constants.PHOTOS_PERMISSION_FLAG, true).then(() => {
400              Log.debug(TAG, `Succeeded in putting the value of '${Constants.PHOTOS_PERMISSION_FLAG}'.`);
401              this.photosPreferences?.flush();
402            }).catch((err: Error) => {
403              Log.error(TAG, `Failed to put the value of '${Constants.PHOTOS_PERMISSION_FLAG}'. Cause: ${err}`);
404            });
405          }
406        }, (err: BusinessError) => {
407          Log.error(TAG, `Failed to start ability err code: ${err.code}`);
408        });
409      } catch (error) {
410        Log.error(TAG, `catch error: ${JSON.stringify(error)}`);
411      }
412    }
413  }
414}