1/* 2 * Copyright (c) 2022 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 */ 15import MediaLib from '@ohos.multimedia.mediaLibrary'; 16import { Log } from '../utils/Log'; 17import { AnimationConstants } from '../constants/AnimationConstants'; 18import { MediaDataItem } from '../data/MediaDataItem'; 19import { DateUtil } from '../utils/DateUtil'; 20import { Broadcast } from '../utils/Broadcast'; 21import { BroadcastConstants } from '../constants/BroadcastConstants'; 22import { startTrace } from '../utils/TraceControllerUtils'; 23import { MediaConstants } from '../constants/MediaConstants'; 24import { startTraceWithTaskId, finishTraceWithTaskId } from '../utils/TraceControllerUtils'; 25import { LazyItem} from '../vm/ItemDataSource'; 26 27const TAG = "ImageGridItemComponent" 28// General grid picture control 29@Component 30export struct ImageGridItemComponent { 31 @Consume broadCast: Broadcast; 32 @Consume @Watch('restPress') isSelectedMode: boolean; 33 @Consume @Watch('onShow') isShow: boolean; 34 @State thumbnail: string = ""; 35 @State isFavourite: boolean = false; 36 @State pressAnimScale: number = 1.0; 37 @State isError: boolean = false; 38 pageName = ''; 39 lazyItem: LazyItem<MediaDataItem> 40 mediaItem: MediaDataItem; 41 isThird = false; 42 private isEnteringPhoto = false; 43 private isPush = false; 44 private isPreview = false; 45 @Link isSelectUpperLimited : boolean; 46 47 private resetPressAnim(): void { 48 this.pressAnimScale = 1; 49 this.isEnteringPhoto = false; 50 } 51 52 private restPress(): void { 53 this.pressAnimScale = 1; 54 } 55 56 aboutToAppear(): void { 57 Log.debug(TAG, 'aboutToAppear'); 58 this.resetShow(); 59 } 60 61 private resetShow() { 62 Log.info(TAG, `getThumbnail1 ${this.thumbnail}`); 63 if (this.mediaItem.isLoad()) { 64 this.thumbnail = this.mediaItem.getThumbnail(MediaConstants.DEFAULT_SIZE, MediaConstants.DEFAULT_SIZE); 65 this.mediaItem.isFavor().then((status: boolean) => { 66 this.isFavourite = status; 67 }) 68 } else { 69 startTraceWithTaskId("load MediaItem", this.mediaItem.index); 70 this.mediaItem.load(false).then(() => { 71 finishTraceWithTaskId("load MediaItem", this.mediaItem.index); 72 this.thumbnail = this.mediaItem.getThumbnail(MediaConstants.DEFAULT_SIZE, MediaConstants.DEFAULT_SIZE); 73 this.isError = false; 74 Log.info(TAG, `getThumbnail2 ${this.thumbnail}`); 75 this.mediaItem.isFavor().then((status: boolean) => { 76 this.isFavourite = status; 77 }) 78 }) 79 } 80 this.resetPressAnim(); 81 this.isPush = false; 82 } 83 84 aboutToDisappear(): void { 85 Log.debug(TAG, `aboutToDisappear`); 86 this.resetPressAnim(); 87 } 88 89 private onShow(): void { 90 if(!this.isShow){ 91 return; 92 } 93 if (this.isEnteringPhoto) { 94 this.resetShow(); 95 } else { 96 this.resetPressAnim(); 97 } 98 } 99 100 async routePage(isError: boolean) { 101 Log.info(TAG, `routePage ${isError}`); 102 try { 103 startTrace('enterPhotoBrowser'); 104 if (this.isThird || this.isPreview) { 105 this.broadCast.emit(BroadcastConstants.JUMP_THIRD_PHOTO_BROWSER, [this.pageName, this.mediaItem]); 106 } else { 107 this.broadCast.emit(BroadcastConstants.JUMP_PHOTO_BROWSER, [this.pageName, this.mediaItem]); 108 } 109 } catch (err) { 110 Log.error(TAG, `fail callback, code: ${err.code}, msg: ${err.msg}`); 111 } 112 } 113 114 async routeToPreviewPage() { 115 try { 116 Log.info(TAG, 'routeToPreviewPage'); 117 this.broadCast.emit(BroadcastConstants.JUMP_THIRD_PHOTO_BROWSER, [this.pageName, this.mediaItem]); 118 } catch (err) { 119 Log.error(TAG, `fail callback, code: ${err.code}, msg: ${err.msg}`); 120 } 121 } 122 123 private onSelected() { 124 Log.info(TAG, 'onSelected'); 125 if ((this.isSelectUpperLimited != undefined && !this.isSelectUpperLimited) || this.mediaItem.isSelect) { 126 this.mediaItem.setSelect(!this.mediaItem.isSelect); 127 if (this.lazyItem) { 128 this.lazyItem.update(this.mediaItem) 129 } 130 } 131 this.broadCast.emit(BroadcastConstants.SELECT, [this.mediaItem.index]); 132 } 133 134 private selectStateChange() { 135 Log.info(TAG, `change selected ${this.isSelectedMode}`); 136 if (!this.isSelectedMode) { 137 this.isSelectedMode = true; 138 this.pressAnimScale = 1; 139 } 140 this.onSelected(); 141 } 142 143 jumpToPhotoBrowser() { 144 if(this.isEnteringPhoto){ 145 return; 146 } 147 this.isEnteringPhoto = true; 148 this.thumbnail = this.mediaItem.getThumbnail(this.mediaItem.imgWidth, this.mediaItem.imgHeight); 149 console.info("column click, first change source:"+this.thumbnail) 150 this.isPush = true; 151 } 152 153 build() { 154 Stack({ alignContent: Alignment.Start }) { 155 Image(this.isError ? this.mediaItem.getAlt() : this.thumbnail) 156 .aspectRatio(1) 157 .rotate({ x: 0, y: 0, z: 1, angle: 0 }) 158 .objectFit(ImageFit.Cover) 159 .autoResize(false) 160 .onComplete(() => { 161 Log.debug(TAG, `Draw the image! ${this.thumbnail}`); 162 if (this.isEnteringPhoto && this.isPush) { 163 Log.info(TAG, "image onComplete, broadCast, thumbnail:"+this.thumbnail); 164 this.isPush = false; 165 if (this.isThird || this.isPreview) { 166 this.broadCast.emit(BroadcastConstants.JUMP_THIRD_PHOTO_BROWSER, [this.pageName, this.mediaItem]); 167 } else { 168 this.broadCast.emit(BroadcastConstants.JUMP_PHOTO_BROWSER, [this.pageName, this.mediaItem]); 169 } 170 } 171 172 }) 173 .onError(() => { 174 Log.error(TAG, `alt Image ${this.mediaItem.index} error :${this.thumbnail}`); 175 if (this.thumbnail.length == 0 || this.mediaItem.width == 0 || this.mediaItem.height == 0) { 176 this.resetShow() 177 } else { 178 this.isError = true 179 } 180 }) 181 .sharedTransition(this.pageName + this.mediaItem.getHashCode(), { 182 duration: AnimationConstants.SHARE_TRANSITION_DURATION, 183 zIndex: 0, 184 }) 185 186 if (this.mediaItem.mediaType == MediaLib.MediaType.VIDEO) { 187 Column() 188 .position({ x: '0%', y: '50%' }) 189 .height('50%') 190 .width('100%') 191 .linearGradient({ 192 angle: 0, 193 colors: [[$r('app.color.album_cover_gradient_start_color'), 0], [$r('app.color.transparent'), 1.0]] 194 }) 195 Text(DateUtil.getFormattedDuration(this.mediaItem.duration)) 196 .fontSize($r('sys.float.ohos_id_text_size_caption')) 197 .fontFamily($r('app.string.id_text_font_family_regular')) 198 .fontColor($r('app.color.text_color_above_picture')) 199 .lineHeight($r('app.float.grid_item_text_line_height')) 200 .position({ x: '0%', y: '100%' }) 201 .markAnchor({ 202 x: $r('app.float.grid_item_duration_markAnchor_x'), 203 y: $r('app.float.grid_item_duration_markAnchor_y') 204 }) 205 .margin({ right: $r('app.float.grid_item_duration_margin_right') }) 206 } 207 if (this.isFavourite) { 208 Image($r('app.media.ic_favorite_overlay')) 209 .height($r('app.float.icon_size')) 210 .width($r('app.float.icon_size')) 211 .objectFit(ImageFit.Contain) 212 .position({ x: '100%', y: '0%' }) 213 .markAnchor({ 214 x: $r('app.float.grid_item_favor_markAnchor_x'), 215 y: $r('app.float.grid_item_favor_markAnchor_y') 216 }) 217 } 218 219 Column() 220 .height('100%') 221 .width('100%') 222 .backgroundColor(this.isSelectedMode && this.mediaItem.isSelect ? 223 $r('app.color.item_selection_bg_color') : $r('app.color.transparent')) 224 .onClick(() => { 225 if (this.isSelectedMode) { 226 this.onSelected(); 227 } else { 228 this.isPreview = false; 229 this.jumpToPhotoBrowser(); 230 } 231 }) 232 if (this.isSelectedMode) { 233 Image($r('app.media.ic_photo_preview')) 234 .onClick(() => { 235 this.isPreview = true; 236 this.jumpToPhotoBrowser(); 237 Log.info(TAG, 'expand.'); 238 }) 239 .height($r('app.float.icon_size_hot')) 240 .width($r('app.float.icon_size_hot')) 241 .position({ x: '0%', y: '0%' }) 242 .markAnchor({ x: 0, y: 0 }) 243 .padding($r('app.float.grid_item_preview_padding')) 244 Image(this.mediaItem.isSelect 245 ? $r('app.media.ic_gallery_public_checkbox_filled') : $r('app.media.ic_checkbox_off_overlay')) 246 .height($r('app.float.icon_size')) 247 .width($r('app.float.icon_size')) 248 .position({ x: '100%', y: '100%' }) 249 .markAnchor({ 250 x: $r('app.float.grid_item_checkbox_markAnchor'), 251 y: $r('app.float.grid_item_checkbox_markAnchor') 252 }) 253 .onClick(() => { 254 this.onSelected(); 255 }) 256 } 257 } 258 .aspectRatio(1) 259 .scale({ 260 x: this.pressAnimScale, 261 y: this.pressAnimScale 262 }) 263 .animation({ 264 duration: AnimationConstants.PRESS_ANIM_DURATION, 265 curve: Curve.Ease 266 }) 267 .onTouch(event => { 268 Log.info(TAG, `onTouch trigger: isSelectedMode: ${this.isSelectedMode}, 269 isEnteringPhoto: ${this.isEnteringPhoto}, ${JSON.stringify(event)}`); 270 if (this.isSelectedMode) { 271 return; 272 } 273 274 // Press animation 275 if (event.type == TouchType.Down) { 276 this.pressAnimScale = AnimationConstants.PRESS_ANIM_SCALE; 277 } 278 279 if (event.type == TouchType.Up && !this.isEnteringPhoto && this.pressAnimScale != 1) { 280 this.pressAnimScale = 1; 281 } 282 }) 283 .gesture(LongPressGesture().onAction((event: GestureEvent) => { 284 if (!this.isThird) { 285 Log.info(TAG, `LongPressGesture ${this.isSelectedMode}`); 286 this.selectStateChange(); 287 } 288 })) 289 } 290}