• 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 resourceManager from '@ohos.resourceManager';
17import Matrix4 from '@ohos.matrix4';
18import router from '@ohos.router';
19import { MediaItem } from '../model/browser/photo/MediaItem';
20import type { AnimationParam } from '../model/browser/photo/EventPipeline';
21import { EventPipeline } from '../model/browser/photo/EventPipeline';
22import { BroadCast } from '../utils/BroadCast';
23import { Log } from '../utils/Log';
24import { Constants as PhotoConstants } from '../model/browser/photo/Constants';
25import { UserFileManagerAccess } from '../access/UserFileManagerAccess';
26import { ColumnSize, ScreenManager } from '../model/common/ScreenManager';
27import { Constants } from '../model/common/Constants';
28import { LoadingPanel } from './LoadingPanel';
29import { ImageUtil } from '../utils/ImageUtil';
30import { BigDataConstants, ReportToBigDataUtil } from '../utils/ReportToBigDataUtil';
31import { MultimodalInputManager } from '../model/common/MultimodalInputManager';
32import { BroadCastConstants } from '../model/common/BroadCastConstants';
33import { PhotoDataSource } from '../model/browser/photo/PhotoDataSource';
34
35const TAG: string = 'common_PhotoItem';
36
37@Component
38export struct PhotoItem {
39  @State @Watch('onViewDataChanged') item: MediaItem = new MediaItem(null);
40  @State matrix: Matrix4.Matrix4Transit = Matrix4.identity().copy();
41  @State mDirection: PanDirection = PanDirection.Vertical;
42  //  @Consume broadCast: BroadCast;
43  @Link broadCast: BroadCast;
44  @State visible: Visibility = Visibility.Hidden;
45  @State objectFit: ImageFit = ImageFit.Cover;
46  @State thumbnail: string = '';
47  @State ratio: number = 1.0;
48  @State showError: boolean = false;
49  @State lcdPixelMapUpdate: boolean = false;
50  @Consume pageFrom: number;
51  @Consume('transitionIndex') @Watch('onTransitionChange') updateTransition: number;
52  mPosition: number;
53  transitionTag: string;
54  @State isLoading: boolean = true;
55  @State usePixmap: boolean = false;
56  @Provide listCardWidth: number = 0;
57  verifyPhotoScaled: (matrix: Matrix4.Matrix4Transit) => void;
58  @Consume currentIndex: number;
59  @StorageLink('geometryScale') geometryScale: number = 1;
60  @StorageLink('geometryOffsetX') geometryOffsetX: number = 0;
61  @StorageLink('geometryOffsetY') geometryOffsetY: number = 0;
62  @State isPullDownAndDragPhoto: boolean = false;
63  @Link isOnSwiperAnimation: boolean;
64  @State imageTop: number = 0;
65  @StorageLink('isHorizontal') isHorizontal: boolean
66    = ScreenManager.getInstance().isHorizontal();
67  @State isPhotoScaled: boolean = false;
68  @State justifyWidth: boolean = true;
69  @State imageWidth: string = Constants.PERCENT_100;
70  @State imageHeight: string = Constants.PERCENT_100;
71  @State isEdgeTop: boolean = true;
72  @Link isRunningAnimation: boolean;
73  @State geometryTransitionId: string = 'default_id';
74  @StorageLink('geometryTransitionBrowserId') @Watch('onGeometryIdChange') geometryTransitionBrowserId: string = '';
75  private lastUpdateTransition: number = -1;
76  private eventPipeline: EventPipeline;
77  private animationOption: AnimationParam = null;
78  private animationEndMatrix: Matrix4.Matrix4Transit = null;
79  private isHandlingTap: boolean = false;
80  private timerCounter: number;
81  private imgScale: number = 1;
82  private firstLoad: boolean = true;
83  private preItem = { height: 0, width: 0 };
84  private albumUri: string = '';
85  private imagePropertyComponentHeight: number = 578;
86  private propertyHeightHorizontal: number = 300;
87  private lastTouchDownY: number = 0;
88  private lastTouchDownX: number = 0;
89  private isInSelectedMode: boolean = false;
90  private geometryTransitionEnable: boolean = false;
91  private windowLayoutWidth: number = ScreenManager.getInstance().getWinLayoutWidth();
92  private windowLayoutHeight: number = ScreenManager.getInstance().getWinLayoutHeight();
93  private windowColumns: number = ScreenManager.getInstance().getScreenColumns();
94  private isFromFACard: boolean = false;
95  private dataSource: PhotoDataSource;
96  private isDefaultFA: boolean = false;
97  onWindowSizeChangeCallBack = () => this.onWindowSizeChanged();
98
99  onGeometryIdChange() {
100    this.geometryTransitionId = (this.mPosition === this.currentIndex) ? this.geometryTransitionBrowserId : 'default_id';
101    Log.debug(TAG, 'geometryTransitionId = ' + this.geometryTransitionId + ', this.mPosition = ' + this.mPosition +
102    ', this.currentIndex = ' + this.currentIndex);
103  }
104
105  aboutToAppear(): void {
106    Log.info(TAG, `aboutToAppear ${this.item.uri}`);
107    this.eventPipeline = new EventPipeline(this.broadCast, this.item, this);
108    this.matrix = Matrix4.identity().copy();
109    this.isPhotoScaled = false;
110    this.firstLoad = true;
111    this.eventPipelineSizeChange = this.eventPipelineSizeChange.bind(this)
112    if (this.dataSource) {
113      this.broadCast.on(BroadCastConstants.ON_DATA_RELOADED, (addCount) => {
114        let item: MediaItem = this.dataSource.getItemByUri(this.item.uri);
115        if (item) {
116          this.item = item;
117          Log.debug(TAG, `Item[${this.item.uri}] is changed`);
118        }
119      });
120    }
121    ScreenManager.getInstance().on(ScreenManager.ON_WIN_SIZE_CHANGED, this.onWindowSizeChangeCallBack);
122    this.broadCast.on(PhotoConstants.RESET_DEFAULT_SCALE + this.item.uri, () => {
123      this.resetScaleAnimation(Matrix4.identity().copy());
124    });
125    this.broadCast.on(PhotoConstants.SAVE_SCALE, () => {
126      if (this.currentIndex == this.mPosition) {
127        AppStorage.SetOrCreate(PhotoConstants.MATRIX, this.matrix);
128      }
129    });
130    this.onTransitionChange();
131    this.onViewDataChanged();
132    this.updateListCardWidth();
133    this.calculateImagePos();
134    this.onGeometryIdChange();
135
136    let matrix: Matrix4.Matrix4Transit = AppStorage.Get(PhotoConstants.MATRIX);
137    if (this.currentIndex == this.mPosition && matrix) {
138      this.matrix = matrix;
139      this.updatePhotoScaledStatus();
140    }
141    Log.info(TAG, `aboutToAppear ${this.item.uri} end`);
142  }
143
144  onWindowSizeChanged(): void {
145    let winLayoutW = ScreenManager.getInstance().getWinLayoutWidth();
146    let winLayoutH = ScreenManager.getInstance().getWinLayoutHeight();
147    if (this.windowLayoutWidth !== winLayoutW || this.windowLayoutHeight !== winLayoutH) {
148      Log.debug(TAG, `size change. oldWH: [${this.windowLayoutWidth}, ${this.windowLayoutHeight}]`
149      + `, newWH: [${winLayoutW}, ${winLayoutH}]`);
150      this.windowLayoutWidth = winLayoutW;
151      this.windowLayoutHeight = winLayoutH;
152      this.windowColumns = ScreenManager.getInstance().getScreenColumns();
153      // 跟随屏幕旋转动效同时对Image的宽高重新赋值
154      animateTo({
155        duration: Constants.SCREEN_ROTATE_DURATION,
156        curve: PhotoConstants.PHOTO_TRANSITION_CURVE,
157        onFinish: () => {
158          this.eventPipeline.onAnimationEnd(this.animationEndMatrix);
159          this.animationOption = null;
160          this.animationEndMatrix = null;
161        }
162      }, () => {
163        this.eventPipelineSizeChange();
164        this.matrix = this.animationEndMatrix;
165        this.calculateImagePos();
166        this.updateListCardWidth();
167      });
168    }
169  }
170
171  eventPipelineSizeChange(): void {
172    this.eventPipeline.onComponentSizeChanged(vp2px(this.windowLayoutWidth), vp2px(this.windowLayoutHeight));
173  }
174
175  aboutToDisappear(): void {
176    Log.info(TAG, `aboutToDisappear ${this.item.uri}`);
177    if (this.currentIndex == this.mPosition) {
178      AppStorage.Delete(PhotoConstants.MATRIX);
179    }
180    if (this.dataSource) {
181      this.broadCast.off(BroadCastConstants.ON_DATA_RELOADED, null);
182    }
183    ScreenManager.getInstance().off(ScreenManager.ON_WIN_SIZE_CHANGED, this.onWindowSizeChangeCallBack);
184    this.onWindowSizeChangeCallBack = null;
185    this.broadCast.off(PhotoConstants.RESET_DEFAULT_SCALE + this.item.uri, null);
186    this.broadCast.off(PhotoConstants.SAVE_SCALE, null);
187    this.lcdPixelMapUpdate = false;
188    Log.info(TAG, `aboutToDisappear ${this.item.uri} end`);
189  }
190
191  onViewDataChanged(): void {
192    if (this.eventPipeline == null || this.item == null) {
193      return;
194    }
195    if (!this.usePixmap && !this.isDefaultFA) {
196      this.ratio = ImageUtil.calcRatio(this.item);
197    }
198
199    if ((this.preItem.height == this.item.imgHeight &&
200    this.preItem.width == this.item.imgWidth) && !this.firstLoad) {
201      this.preItem.width = this.item.imgWidth;
202      this.preItem.height = this.item.imgHeight;
203      this.eventPipeline && this.eventPipeline.onDataChanged(this.item)
204      return;
205    }
206
207    this.matrix = Matrix4.identity().copy();
208    this.updatePhotoScaledStatus();
209    this.eventPipeline && this.eventPipeline.setDefaultScale(1);
210
211    this.eventPipeline && this.eventPipeline.onDataChanged(this.item);
212    this.firstLoad = false;
213    this.preItem.width = this.item.imgWidth;
214    this.preItem.height = this.item.imgHeight;
215    Log.info(TAG, 'onViewDataChanged, index: ' + this.mPosition + ', usePixmap: ' + this.usePixmap
216    + ', ratio: ' + this.ratio + ', thumbnail: ' + JSON.stringify(this.thumbnail));
217  }
218
219  onTouchEventRespond(matrix: Matrix4.Matrix4Transit): void {
220    Log.info(TAG, `on touch event respond ${this.item.uri}`);
221    this.matrix = matrix;
222    this.updatePhotoScaledStatus();
223  }
224
225  onDirectionChangeRespond(direction: PanDirection): void {
226    Log.info(TAG, `item: ${this.item.uri}, direction: ${direction}`);
227    if (this.mDirection === direction) {
228      return;
229    }
230    this.mDirection = direction;
231  }
232
233  resetScaleAnimation(matrix: Matrix4.Matrix4Transit): void {
234    if (this.eventPipeline.isDefaultScale()) {
235      return;
236    }
237    this.eventPipeline.startAnimation(matrix);
238    animateTo({
239      duration: this.animationOption.duration,
240      curve: this.animationOption.curve,
241      onFinish: () => {
242        this.eventPipeline.onAnimationEnd(this.animationEndMatrix);
243        this.animationOption = null;
244        this.animationEndMatrix = null;
245      }
246    }, () => {
247      this.matrix = this.animationEndMatrix;
248      this.updatePhotoScaledStatus();
249    })
250  }
251
252  onAnimationEventRespond(animationOption: AnimationParam, animationEndMatrix: Matrix4.Matrix4Transit): void {
253    Log.info(TAG, `item: ${this.item.uri}, animationOption: ${JSON.stringify(animationOption)}`);
254    this.animationOption = animationOption;
255    this.animationEndMatrix = animationEndMatrix;
256  }
257
258  updatePhotoScaledStatus() {
259    let matrix: any = this.matrix;
260    if (!!matrix) {
261      let mat = matrix.copy().matrix4x4;
262      let xScale = mat[Constants.NUMBER_0];
263      let yScale = mat[Constants.NUMBER_5];
264      Log.debug(TAG, `photo in PhotoItem has Scaled x scale: ${xScale}, y scale: ${yScale}, mat: ${mat}`);
265      this.isPhotoScaled = (xScale != 1 || yScale != 1);
266    } else {
267      this.isPhotoScaled = false;
268    }
269  }
270
271  @Builder buildImage() {
272    Image(this.thumbnail)
273      .key('browserFocus_' + this.thumbnail)
274      .aspectRatio(this.ratio)
275      .focusable(true)
276      .transform(this.matrix)
277      .objectFit(ImageFit.Cover)
278      .autoResize(false)
279      .onComplete(() => {
280        Log.info(TAG, `onComplete finish index: ${this.mPosition}, uri: ${this.item.uri}`);
281        this.showError = false;
282        this.isLoading = false;
283        if (this.updateTransition == this.mPosition) {
284          this.broadCast.emit(PhotoConstants.PHOTO_SHOW_STATE, [true]);
285        }
286      })
287      .onError(() => {
288        Log.info(TAG, `image show error`);
289        this.showError = true;
290        this.isLoading = false;
291        if (this.updateTransition == this.mPosition) {
292          this.broadCast.emit(PhotoConstants.PHOTO_SHOW_STATE, [false]);
293        }
294      })
295      .onKeyEvent((event: KeyEvent) => {
296        this.handleKeyEvent(event);
297      })
298      .width(this.imageWidth)
299      .height(this.imageHeight)
300      .scale({
301        x: this.geometryScale,
302        y: this.geometryScale
303      })
304      .offset({
305        x: this.geometryOffsetX,
306        y: this.geometryOffsetY
307      })
308      .geometryTransition(this.geometryTransitionId)
309        // @ts-ignore
310      .transition(TransitionEffect.asymmetric(TransitionEffect.opacity(0.99),
311        // @ts-ignore
312        TransitionEffect.scale({
313          x: 1 / this.geometryScale,
314          y: 1 / this.geometryScale,
315          centerX: '50%',
316          centerY: '50%' })
317          // @ts-ignore
318          .combine(TransitionEffect.opacity(0.99))
319      ))
320      .onAppear(() => {
321        if (this.currentIndex == this.mPosition) {
322          let ret: Boolean = focusControl.requestFocus('browserFocus_' + this.thumbnail);
323          if (ret !== true) {
324            Log.error(TAG, `requestFocus faild, ret:${ret}`);
325          }
326        }
327      })
328  }
329
330  build() {
331    Stack() {
332      // 加载图标文字组件
333      if (this.isLoading && !this.isRunningAnimation) {
334        Column() {
335          LoadingPanel()
336        }
337        .width(Constants.PERCENT_100)
338        .height(Constants.PERCENT_100)
339      }
340
341      Column() {
342        Stack({ alignContent: Alignment.TopStart }) {
343          Column() {
344            // 图片主体组件
345            if (this.item.width != 0 && this.item.height != 0) {
346              this.buildImage()
347            } else {
348              Image(this.thumbnail)
349                .key('browserFocus_' + this.thumbnail)
350                .focusable(true)
351                .transform(this.matrix)
352                .objectFit(ImageFit.Cover)
353                .autoResize(false)
354                .onComplete(() => {
355                  Log.info(TAG, `onComplete finish index: ${this.mPosition}, uri: ${this.item.uri}`);
356                  this.showError = false;
357                  this.isLoading = false;
358                  if (this.updateTransition == this.mPosition) {
359                    this.broadCast.emit(PhotoConstants.PHOTO_SHOW_STATE, [true]);
360                  }
361                })
362                .onError(() => {
363                  Log.info(TAG, `image show error`);
364                  this.showError = true;
365                  this.isLoading = false;
366                  if (this.updateTransition == this.mPosition) {
367                    this.broadCast.emit(PhotoConstants.PHOTO_SHOW_STATE, [false]);
368                  }
369                })
370                .onKeyEvent((event: KeyEvent) => {
371                  this.handleKeyEvent(event);
372                })
373                .onAppear(() => {
374                  if (this.currentIndex == this.mPosition) {
375                    let ret: Boolean = focusControl.requestFocus('browserFocus_' + this.thumbnail);
376                    if (ret !== true) {
377                      Log.error(TAG, `requestFocus faild, ret:${ret}`);
378                    }
379                  }
380                })
381            }
382          }
383          .width(Constants.PERCENT_100)
384          .height(Constants.PERCENT_100)
385          .alignItems(HorizontalAlign.Center)
386          .justifyContent(FlexAlign.Center)
387        }
388      }
389      .hitTestBehavior(HitTestMode.Default)
390      .width(Constants.PERCENT_100)
391      .height(Constants.PERCENT_100)
392      // 绑定手势
393      .parallelGesture(
394        GestureGroup(GestureMode.Parallel,
395          // 捏合手势
396          PinchGesture({
397            fingers: 2,
398            distance: 1
399          })
400            .onActionStart((event: GestureEvent) => {
401              Log.info(TAG, 'PinchGesture onActionStart');
402              Log.info(TAG, `PinchGesture onActionStart scale:\
403                          ${event.scale}, cx: ${event.pinchCenterX}, cy: ${event.pinchCenterY}`);
404              if (this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_IMAGE
405              || this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
406                this.eventPipeline.onScaleStart(event.scale, event.pinchCenterX, event.pinchCenterY);
407              }
408            })
409            .onActionUpdate((event) => {
410              Log.info(TAG, `PinchGesture onActionUpdate scale: ${event.scale}`);
411              if (this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_IMAGE
412              || this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
413                this.eventPipeline.onScale(event.scale);
414              }
415            })
416            .onActionEnd(() => {
417              Log.info(TAG, 'PinchGesture onActionEnd');
418              if (this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_IMAGE
419              || this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
420                this.eventPipeline.onScaleEnd();
421                if (this.animationOption != null) {
422                  animateTo({
423                    duration: this.animationOption.duration,
424                    curve: this.animationOption.curve,
425                    onFinish: () => {
426                      this.eventPipeline.onAnimationEnd(this.animationEndMatrix);
427                      this.animationOption = null;
428                      this.animationEndMatrix = null;
429                    }
430                  }, () => {
431                    this.matrix = this.animationEndMatrix;
432                  })
433                  if (!!this.verifyPhotoScaled) {
434                    this.verifyPhotoScaled(this.matrix)
435                  }
436                  this.updatePhotoScaledStatus();
437                }
438              }
439            }),
440          // 平移手势
441          PanGesture({
442            direction: this.mDirection
443          })
444            .onActionStart((event: GestureEvent) => {
445              Log.info(TAG, `PanGesture start offsetX:\
446                                  ${vp2px(event.offsetX)}, offsetY: ${vp2px(event.offsetY)}`);
447              if (this.isNeedShieldPullUpEvent(event)) {
448                Log.info(TAG, `shield pullup event, is album recycle = ${this.albumUri == UserFileManagerAccess.getInstance()
449                  .getSystemAlbumUri(UserFileManagerAccess.TRASH_ALBUM_SUB_TYPE)},\
450                                    is photo scaled = ${this.isPhotoScaled}, is pull Up = ${event.offsetY < 0}`);
451                return;
452              }
453              this.eventPipeline.onMoveStart(vp2px(event.offsetX), vp2px(event.offsetY));
454            })
455            .onActionUpdate((event: GestureEvent) => {
456              Log.info(TAG, `PanGesture update offsetX:\
457                                  ${vp2px(event.offsetX)}, offsetY: ${vp2px(event.offsetY)}`);
458              if (this.isNeedShieldPullUpEvent(event)) {
459                return;
460              }
461              this.eventPipeline.onMove(vp2px(event.offsetX), vp2px(event.offsetY));
462              this.isPullDownAndDragPhoto = this.eventPipeline.canPullDownAndDragPhoto();
463              if (this.isPullDownAndDragPhoto && this.geometryTransitionEnable &&
464              !this.isOnSwiperAnimation && !this.isFromFACard) {
465                this.doDragPhoto(event.offsetX, event.offsetY);
466              }
467            })
468            .onActionEnd((event: GestureEvent) => {
469              Log.info(TAG, `PanGesture end offsetX:\
470                                  ${vp2px(event.offsetX)}, offsetY: ${vp2px(event.offsetY)} \
471                                  this.isOnSwiperAnimation ${this.isOnSwiperAnimation}`);
472              if (this.isOnSwiperAnimation || this.isNeedShieldPullUpEvent(event)) {
473                return;
474              }
475              this.eventPipeline.onMoveEnd(vp2px(event.offsetX), vp2px(event.offsetY));
476              this.isPullDownAndDragPhoto = this.eventPipeline.canPullDownAndDragPhoto();
477              if (this.animationOption != null) {
478                animateTo({
479                  duration: this.animationOption.duration,
480                  curve: this.animationOption.curve,
481                  onFinish: () => {
482                    this.eventPipeline.onAnimationEnd(this.animationEndMatrix);
483                    this.animationOption = null;
484                    this.animationEndMatrix = null;
485                  }
486                }, () => {
487                  this.matrix = this.animationEndMatrix;
488                })
489                if (!!this.verifyPhotoScaled) {
490                  this.verifyPhotoScaled(this.matrix)
491                }
492                this.updatePhotoScaledStatus();
493              }
494            }),
495          // 点击手势
496          TapGesture({
497            count: 1
498          })
499            .onAction((event: GestureEvent) => {
500              if (this.isHandlingTap) {
501                if (this.timerCounter != null) {
502                  clearTimeout(this.timerCounter)
503                  this.timerCounter = null;
504                  this.isHandlingTap = false;
505                }
506              } else {
507                this.isHandlingTap = true;
508                this.timerCounter = setTimeout(() => {
509                  this.broadCast.emit(PhotoConstants.TOGGLE_BAR, [null]);
510                  this.isHandlingTap = false;
511                }, Constants.DOUBLE_CLICK_GAP)
512                return;
513              }
514              Log.info(TAG, `onDoubleTap event: ${JSON.stringify(event)}`);
515              this.eventPipeline.onDoubleTap(event.fingerList[0].localX, event.fingerList[0].localY);
516              if (this.animationOption != null) {
517                Log.info(TAG, 'TapGesture animateTo start');
518                animateTo({
519                  duration: this.animationOption.duration,
520                  curve: this.animationOption.curve,
521                  onFinish: () => {
522                    this.eventPipeline.onAnimationEnd(this.animationEndMatrix);
523                    this.animationOption = null;
524                    this.animationEndMatrix = null;
525                  }
526                }, () => {
527                  this.matrix = this.animationEndMatrix;
528                })
529                if (!!this.verifyPhotoScaled) {
530                  this.verifyPhotoScaled(this.matrix)
531                }
532                this.updatePhotoScaledStatus();
533              }
534            }),
535        )
536      )
537      .clip(true)
538      .onTouch((event) => {
539        this.dealTouchEvent(event);
540        this.eventPipeline.onTouch(event);
541      })
542      // TODO Remind users when pictures of other devices cannot be show
543      if ((this.showError || this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) &&
544      this.pageFrom == Constants.ENTRY_FROM.DISTRIBUTED) {
545        Row() {
546          Text((this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO) ?
547          $r('app.string.no_distributed_photo_show_video') :
548          $r('app.string.no_distributed_photo_show_image'))
549            .fontSize($r('sys.float.ohos_id_text_size_body2'))
550            .fontFamily($r('app.string.id_text_font_family_regular'))
551            .fontColor($r('sys.color.ohos_id_color_text_tertiary'))
552        }
553        .margin({
554          top: (this.item.mediaType ==
555          UserFileManagerAccess.MEDIA_TYPE_VIDEO) ? $r('app.float.input_text_notify_margin') : 0
556        })
557      }
558      // 播放视频按键
559      if (this.isVideoPlayBtnShow() && !this.isPullDownAndDragPhoto && !this.isRunningAnimation) {
560        Row() {
561          Image($r('app.media.ic_video_play_btn_png'))
562            .key('VideoPlayButton')
563            .objectFit(ImageFit.Contain)
564            .width($r('app.float.icon_video_size'))
565            .height($r('app.float.icon_video_size'))
566            .onClick(() => {
567              Log.info(TAG, 'video item: ' + JSON.stringify(this.item))
568              Log.info(TAG, 'video thumbail: ' + JSON.stringify(this.thumbnail))
569              if (this.item != undefined) {
570                router.pushUrl({
571                  url: 'pages/VideoBrowser',
572                  params: {
573                    uri: this.item.uri,
574                    dateTaken: this.item.getDataTaken(),
575                    previewUri: this.thumbnail
576                  }
577                })
578                let msg = {
579                  'PhotoButton': BigDataConstants.PHOTO_BUTTON_VIDEO,
580                  'From': BigDataConstants.LOCAL_MEDIA,
581                }
582                ReportToBigDataUtil.report(BigDataConstants.CLICK_PHOTO_BUTTON_ID, msg);
583              }
584            })
585        }
586      }
587    }
588    .width(Constants.PERCENT_100)
589    .height(Constants.PERCENT_100)
590  }
591
592  isVideoPlayBtnShow(): boolean {
593    return (this.item != undefined) && (this.item.mediaType == UserFileManagerAccess.MEDIA_TYPE_VIDEO);
594  }
595
596  doDragPhoto(offsetX: number, offsetY: number) {
597    animateTo({
598      curve: PhotoConstants.RESPONSIVE_SPRING_MOTION_CURVE
599    }, () => {
600      let distanceY = Math.abs(offsetY);
601      this.geometryOffsetX = offsetX;
602      this.geometryOffsetY = offsetY;
603
604      // Calculate image size by distance Y, min size is 50%
605      let calcGeometryScale = Constants.NUMBER_1 - distanceY * PhotoConstants.DRAG_SCALE;
606      this.geometryScale = calcGeometryScale > PhotoConstants.MIN_DRAG_SCALE ?
607        calcGeometryScale : PhotoConstants.MIN_DRAG_SCALE;
608
609      // Calculate image opacity by distance Y, min opacity is 0
610      let calcTouchOpacity = Constants.NUMBER_1 - distanceY * PhotoConstants.DRAG_OPACITY;
611      let touchOpacity = calcTouchOpacity > Constants.NUMBER_0 ? calcTouchOpacity : Constants.NUMBER_0;
612      AppStorage.SetOrCreate<number>('geometryOpacity', touchOpacity);
613    })
614  }
615
616  private calculateImagePos(): void {
617    let screenWidth = vp2px(this.windowLayoutWidth);
618    let screenHeight = vp2px(this.windowLayoutHeight);
619    this.justifyWidth = this.needJustifyWidth();
620    this.imageWidth = this.justifyWidth ? Constants.PERCENT_100 : undefined;
621    this.imageHeight = !this.justifyWidth ? Constants.PERCENT_100 : undefined;
622    if (!this.justifyWidth) {
623      this.imageTop = 0;
624    } else {
625      let imgHeight = screenWidth / this.ratio;
626      this.imageTop = screenHeight / 2 - imgHeight / 2;
627      Log.debug(TAG, `calculate image size: height ${imgHeight}, screen height ${screenHeight}, ratio ${this.ratio}`);
628    }
629    Log.debug(TAG, `calculate image size: top ${this.imageTop}`);
630  }
631
632  private updateListCardWidth(): void {
633    if (this.windowColumns == ColumnSize.COLUMN_FOUR) {
634      this.listCardWidth = ScreenManager.getInstance().getColumnsWidth(ColumnSize.COLUMN_FOUR);
635    } else if (this.windowColumns == ColumnSize.COLUMN_EIGHT) {
636      this.listCardWidth = ScreenManager.getInstance().getColumnsWidth(ColumnSize.COLUMN_SIX);
637    } else if (this.windowColumns == ColumnSize.COLUMN_TWELVE) {
638      this.listCardWidth = ScreenManager.getInstance().getColumnsWidth(ColumnSize.COLUMN_EIGHT);
639    } else {
640      Log.error(TAG, `screenColumns is: ${this.windowColumns}`);
641    }
642  }
643
644  private onTransitionChange() {
645    Log.info(TAG, `onTransitionChange , ${this.updateTransition} ${this.position}`);
646    if (this.lastUpdateTransition != this.updateTransition) {
647      this.lastUpdateTransition = this.updateTransition;
648      if (this.updateTransition == this.mPosition) {
649        this.broadCast.emit(PhotoConstants.PHOTO_SHOW_STATE, [!this.showError]);
650      } else {
651
652      }
653      // reset matrix
654      if (this.imgScale != 1) {
655        this.matrix = Matrix4.identity().scale({
656          x: this.imgScale,
657          y: this.imgScale
658        }).copy();
659        this.eventPipeline && this.eventPipeline.setDefaultScale(this.imgScale);
660      } else {
661        this.matrix = Matrix4.identity().copy();
662        // 重置大小
663        this.eventPipeline && this.eventPipeline.reset();
664      }
665      this.updatePhotoScaledStatus();
666      Log.info(TAG, `onTransitionChange end`);
667    }
668  }
669
670  private needJustifyWidth(): boolean {
671    let maxWidth = vp2px(this.windowLayoutWidth);
672    let maxHeight = vp2px(this.windowLayoutHeight);
673    let justifyWidth: boolean = this.ratio >= (maxWidth / maxHeight);
674    Log.info(TAG, `maxWidth:${maxWidth}, maxHeight:${maxHeight}, ratio:${this.ratio}, justifyWidth:${justifyWidth}`);
675    return justifyWidth;
676  }
677
678  private dealTouchEvent(event: TouchEvent): void {
679    if (!this.eventPipeline.canTouch() || this.isOnSwiperAnimation || event.touches.length > 1 || this.isPhotoScaled || this.isInSelectedMode || this.isDefaultFA) {
680      return;
681    }
682    if (event.type == TouchType.Move) {
683      let yOffset = event.touches[0].screenY - this.lastTouchDownY;
684      let xOffset = event.touches[0].screenX - this.lastTouchDownX;
685      this.lastTouchDownY = event.touches[0].screenY;
686      this.lastTouchDownX = event.touches[0].screenX;
687      if (yOffset == undefined || xOffset == undefined) {
688        Log.info(TAG, 'dealTouchEvent screenY or screenX undefined');
689        return;
690      }
691    } else if (event.type == TouchType.Down) {
692      this.lastTouchDownY = event.touches[0].screenY;
693      this.lastTouchDownX = event.touches[0].screenX;
694    } else if (event.type == TouchType.Up) {
695    }
696  }
697
698  private handleKeyEvent(event: KeyEvent): void {
699    Log.info(TAG, `type=${event.type}, keyCode=${event.keyCode}`);
700    if (KeyType.Up == event.type) {
701      switch (event.keyCode) {
702        case MultimodalInputManager.KEY_CODE_KEYBOARD_ESC:
703          let msg = {
704            'From': BigDataConstants.BY_KEYBOARD,
705          }
706          ReportToBigDataUtil.report(BigDataConstants.ESC_PHOTO_BROWSER_WAY, msg);
707          this.broadCast.emit(PhotoConstants.PULL_DOWN_END, []);
708          break;
709        case MultimodalInputManager.KEY_CODE_KEYBOARD_ENTER:
710          if (this.item != undefined && this.item.mediaType === UserFileManagerAccess.MEDIA_TYPE_VIDEO) {
711            router.pushUrl({
712              url: 'pages/VideoBrowser',
713              params: {
714                uri: this.item.uri,
715                dateTaken: this.item.getDataTaken(),
716                previewUri: this.thumbnail
717              }
718            })
719            let msg = {
720              'PhotoButton': BigDataConstants.PHOTO_BUTTON_VIDEO,
721              'From': BigDataConstants.LOCAL_MEDIA,
722            }
723          }
724          break;
725        default:
726          Log.info(TAG, `on key event Up, default`);
727          break;
728      }
729    }
730  }
731
732  private isNeedShieldPullUpEvent(event: GestureEvent): boolean {
733    return event.offsetY < 0 &&
734    !this.isPhotoScaled;
735  }
736}
737