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}