1/*
2 * Copyright (c) 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 '@system.router';
17import display from '@ohos.display';
18import deviceInfo from '@ohos.deviceInfo'
19import { BigVideoTimer } from './BigVideoTimer'
20import { Action, UiStateMode } from '@ohos/common/src/main/ets/default/redux/actions/Action'
21import { AssistiveGridView } from '@ohos/common/src/main/ets/default/featurecommon/assistivegridview/AssistiveGridView'
22import { BigText } from '@ohos/common/src/main/ets/default/featurecommon/bigtext/BigText'
23import { CameraNeedStatus, CameraStatus } from '@ohos/common/src/main/ets/default/utils/Constants'
24import { CameraId } from '@ohos/common/src/main/ets/default/setting/settingitem/CameraId'
25import { EventBus } from '@ohos/common/src/main/ets/default/worker/eventbus/EventBus'
26import { EventBusManager } from '@ohos/common/src/main/ets/default/worker/eventbus/EventBusManager'
27import { Dispatch, OhCombinedState } from '@ohos/common/src/main/ets/default/redux/store'
28import { getStore } from '@ohos/common/src/main/ets/default/redux/store'
29import { Log } from '@ohos/common/src/main/ets/default/utils/Log'
30import { GeoLocation } from '@ohos/common/src/main/ets/default/featurecommon/geolocation/GeoLocation'
31import { ScreenLockManager } from '@ohos/common/src/main/ets/default/featurecommon/screenlock/ScreenLockManager'
32import { SettingManager } from '@ohos/common/src/main/ets/default/setting/SettingManager'
33import { ShowFlashBlack } from '@ohos/common/src/main/ets/default/featurecommon/animate/ShowFlashBlack'
34import { TimeLapseView } from '@ohos/common/src/main/ets/default/featurecommon/timelapseview/TimeLapseView'
35import { ZoomText } from '@ohos/common/src/main/ets/default/featurecommon/zoomview/ZoomText'
36import { SmallVideoTimer } from './SmallVideoTimer'
37import promptAction from '@ohos.promptAction';
38import wantConstant from '@ohos.ability.wantConstant';
39import { GlobalContext } from '@ohos/common/src/main/ets/default/utils/GlobalContext';
40import { CameraPlatformCapability } from '@ohos/common/src/main/ets/default/camera/CameraPlatformCapability';
41import { BusinessError } from '@ohos.base';
42
43const DEFAULT_FUNCTION: Function = () => {
44};
45
46class SizeStruct {
47  width: number = 0;
48  height: number = 0;
49}
50
51class StateStruct {
52  mode: string = '';
53  curMode: string = '';
54  isShowPreview: boolean = true;
55  surfaceId: number = 0;
56  xComponentWidth: number = 0;
57  xComponentHeight: number = 0;
58  cameraPosition: CameraId = CameraId.BACK;
59  curCameraPosition: CameraId = CameraId.BACK;
60  minZoomRatio: number = 0;
61  maxZoomRatio: number = 0;
62  zoomRatio: number = 0;
63  platformCapability: CameraPlatformCapability | undefined = undefined;
64  videoState: string = '';
65  footBarHeight: number = 0;
66  isShowZoomText: boolean = false;
67  showZoomLabelValue: boolean = false;
68  modeIndex: number = 0;
69  isThirdPartyCall: boolean = false;
70  isShowBigText: boolean = false;
71  isShowtimeLapse: boolean = false;
72  isBigVideoTimerVisible: boolean = false;
73  isSmallVideoTimerVisible: boolean = false;
74  isAssGridViewShow: string = '';
75  isShowFlashBlack: boolean = false;
76  modeChangeDone: boolean = false;
77  isShowPageView: boolean = false;
78  showBlur: boolean = false;
79  isFaCall: boolean = false;
80  baseZoom: number = 0;
81}
82
83class PreviewAreaDispatcher {
84  private mDispatch: Dispatch = (data) => data;
85
86  public setDispatch(dispatch: Dispatch) {
87    this.mDispatch = dispatch;
88  }
89
90  public initCamera(cameraId: CameraId, mode: string): void {
91    this.mDispatch(Action.initCamera(cameraId, mode));
92  }
93
94  public initZoomRatio(min: number, max: number): void {
95    this.mDispatch(Action.initZoomRatio(min, max));
96  }
97
98  public changeZoomRatio(zoomRatio: number): void {
99    this.mDispatch(Action.changeZoomRatio(zoomRatio));
100  }
101
102  public updateZoomPercentage(zoomPercentage: number): void {
103    this.mDispatch(Action.updateZoomPercentage(zoomPercentage));
104  }
105
106  public prepareSurface(surfaceId: number): void {
107    this.mDispatch(Action.prepareSurface(surfaceId));
108  }
109
110  public startPreview(zoomRatio: number): void {
111    this.mDispatch(Action.startPreview(zoomRatio));
112  }
113
114  public switchCamera(cameraId: CameraId, mode: string): void {
115    this.mDispatch(Action.switchCamera(cameraId, mode));
116  }
117
118  public updateCameraPosition(cameraPosition: string): void {
119    this.mDispatch(Action.updateCameraPosition(cameraPosition));
120  }
121
122  public startVideoFlag(isStartVideo: boolean): void {
123    this.mDispatch(Action.startVideoFlag(isStartVideo));
124  }
125
126  public changeXComponentSize(xComponentWidth: number, xComponentHeight: number): void {
127    this.mDispatch(Action.changeXComponentSize(xComponentWidth, xComponentHeight));
128  }
129
130  public updateShowPreviewFlag(isShowPreview: boolean): void {
131    this.mDispatch(Action.updateShowPreviewFlag(isShowPreview));
132  }
133
134  public updateIsShowZoomText(isShowZoomText: boolean): void {
135    this.mDispatch(Action.updateShowZoomTextFlag(isShowZoomText));
136  }
137
138  public updateIsPhotoZoomDetails(isPhotoZoomDetails: boolean): void {
139    this.mDispatch(Action.updatePhotoZoomDetailsFlag(isPhotoZoomDetails));
140  }
141
142  public updateBaseZoom(zoomRatio: number): void {
143    this.mDispatch(Action.updateBaseZoom(zoomRatio));
144  }
145
146  public changeImageSize(videoSize: ScreenSizeType): void {
147    this.mDispatch(Action.changeVideoSize(videoSize));
148  }
149
150  public changeVideoSize(videoSize: SizeStruct): void {
151    this.mDispatch(Action.changeVideoSize(videoSize));
152  }
153
154  public close(): void {
155    this.mDispatch(Action.close());
156  }
157
158  public stopRecording(): void {
159    this.mDispatch(Action.stopRecording());
160    this.mDispatch(Action.updateVideoState('beforeTakeVideo'));
161    this.mDispatch(Action.updateBigVideoTimerVisible(false));
162    this.mDispatch(Action.updateSmallVideoTimerVisible(false));
163    this.mDispatch(Action.updateScreenStatus(false));
164  }
165
166  public changeTimeLapse(isShowtimeLapse: boolean): void {
167    this.mDispatch(Action.changeTimeLapse(isShowtimeLapse));
168  }
169
170  public capture(): void {
171    this.mDispatch(Action.updateShowFlashBlackFlag(true));
172    this.mDispatch(Action.capture());
173  }
174
175  public startRecording(): void {
176    this.mDispatch(Action.startRecording());
177    this.mDispatch(Action.updateVideoState('startTakeVideo'));
178    this.mDispatch(Action.updateBigVideoTimerVisible(true));
179    this.mDispatch(Action.updateScreenStatus(true));
180  }
181
182  public assistiveGridView(isViewShow: number): void {
183    this.mDispatch(Action.assistiveGridView(isViewShow));
184  }
185
186  public swipeChangeMode(swipeModeIndex: number): void {
187    this.mDispatch(Action.swipeChangeMode(swipeModeIndex));
188  }
189
190  public thirdPartyCall(isThirdPartyCall: boolean, action: string): void {
191    this.mDispatch(Action.thirdPartyCall(isThirdPartyCall, action));
192  }
193
194  public faCall(isFaCall: boolean): void {
195    this.mDispatch(Action.faCall(isFaCall));
196  }
197
198  public initMode(mode: string): void {
199    this.mDispatch(Action.initMode(mode));
200  }
201
202  public updateModeIndex(index: number): void {
203    this.mDispatch(Action.updateModeIndex(index));
204  }
205
206  public updateMode(mode: string): void {
207    this.mDispatch(Action.updateMode(mode));
208  }
209
210  public changeToMode(mode: string): void {
211    this.mDispatch(Action.changeMode(mode));
212  }
213
214  public updateShowZoomLabelValue(flag: boolean): void {
215    this.mDispatch(Action.updateShowZoomLabelValue(flag));
216  }
217
218  public reloadThumbnail(): void {
219    this.mDispatch(Action.reloadThumbnail());
220  }
221
222  public changeCameraPosition(cameraPosition: string): void {
223    this.mDispatch(Action.setCameraPosition(cameraPosition));
224  }
225
226  public updateShowPinch(flag: boolean): void {
227    this.mDispatch(Action.updateShowPinch(flag));
228  }
229
230  public swipeModeChangeDone(actionOff: boolean): void {
231    this.mDispatch(Action.swipeModeChangeDone(actionOff));
232  }
233
234  public updateInitShowFlag(initShowFlag: boolean): void {
235    this.mDispatch(Action.updateInitShowFlag(initShowFlag));
236  }
237
238  public hideSettingView(): void {
239    this.mDispatch(Action.showSettingView(false));
240  }
241}
242
243GlobalContext.get().setXComponentController(new XComponentController())
244
245interface ScreenSizeType {
246  width: number
247  height: number
248}
249
250interface PlatformCapability {
251  platformCapability: CameraPlatformCapability
252}
253
254interface ModeData {
255  mode: string
256}
257
258interface KeepScreenData {
259  isKeepScreenOn: boolean
260}
261
262@Component
263export struct PreviewAreaLand {
264  private TAG: string = '[PreviewAreaLand]:';
265  appEventBus: EventBus = EventBusManager.getInstance().getEventBus();
266  @State state: StateStruct = new StateStruct();
267  @State btnSwitch: boolean = false;
268  @State btnSwitchSec: boolean = false;
269  @State pinchGestureTimerId: number = 0;
270  @State onTouchDownTimerId: number = 0;
271  @State scaleX: number = 1;
272  @State scaleXSec: number = 0.8;
273  @State rotateAngle: number = 0;
274  @State rotateAngleSec: number = 80;
275  @State btnOpacityFirst: number = 1;
276  @State btnOpacitySec: number = 0;
277  @State switchBackgroundOpacity: number = 1;
278  @State isSwitchBackground: boolean = false;
279  @State pageType: string = '';
280  @Link screenSize: ScreenSizeType;
281  @State isShowBlurSize: ScreenSizeType = { width: 0, height: 0 };
282  private mConnect: Function = () => {
283  };
284  private isShowPreview: boolean = false;
285  private settingManager = SettingManager.getInstance();
286  private pinchGestureTimer: number = 0;
287  private baseZoom: number = 0;
288  private modeArray: Array<string> = ['PHOTO', 'VIDEO'];
289  private mAction: PreviewAreaDispatcher = new PreviewAreaDispatcher();
290
291  private async onCameraInit(): Promise<void> {
292    Log.info(`${this.TAG} EventBus onCameraInit isShowPreview = ${this.isShowPreview}
293      platformCapability = ${this.state.platformCapability} E`);
294    if (this.state.platformCapability) {
295      this.settingManager.loadAllSetting().then(() => {
296        this.settingManager.setCameraId(this.state.cameraPosition);
297
298        let imageSize = this.settingManager.getImageSize();
299        this.mAction.changeImageSize(imageSize);
300
301        let videoSize = this.settingManager.getVideoSize();
302        this.mAction.changeVideoSize(videoSize);
303
304        let isAssGridViewShow: number = Number(this.settingManager.getAssistiveGrid());
305        this.mAction.assistiveGridView(isAssGridViewShow);
306
307        GeoLocation.getInstance().on();
308
309        this.resetScreenSize(this.screenSize);
310        this.mAction.updateShowPreviewFlag(true);
311      })
312    }
313    Log.info(`${this.TAG} onCameraInit isShowPreview = ${this.state.isShowPreview}  X`);
314  }
315
316  private async doCameraAction() {
317    Log.info(`${this.TAG} doCameraAction E`);
318    this.settingManager.setCameraId(this.state.cameraPosition);
319    Log.info(`${this.TAG} curMode:${this.state.curMode} mode:${this.state.mode}`);
320    if (this.state.curCameraPosition != this.state.cameraPosition) {
321      this.mAction.switchCamera(this.state.cameraPosition, this.state.mode);
322      this.mAction.updateCameraPosition(this.state.cameraPosition);
323    } else if (this.state.curMode != this.state.mode) {
324      this.mAction.changeToMode(this.state.mode);
325      this.mAction.updateMode(this.state.mode);
326    } else {
327      if (GlobalContext.get().getT<boolean>('keepCameraZoomRatio') && GlobalContext.get()
328        .getT<boolean>('keepCameraZoomRatio')) {
329        GlobalContext.get().setObject('keepCameraZoomRatio', false)
330        Log.info(`${this.TAG} keep zoomRatio: ` + this.state.zoomRatio);
331      } else {
332        this.mAction.changeZoomRatio(1);
333        Log.info(`${this.TAG} change zoomRatio: 1`);
334      }
335      this.mAction.startPreview(this.state.zoomRatio);
336    }
337    Log.info(`${this.TAG} doCameraAction X`);
338    this.mAction.updateInitShowFlag(true);
339  }
340
341  private async onModeChanged(data: ModeData) {
342    Log.info(`${this.TAG} onModeChanged E data.mode: ${data.mode}`);
343    this.mAction.changeZoomRatio(1);
344    this.mAction.updateShowPreviewFlag(true);
345    Log.info(`${this.TAG} onModeChanged X`);
346  }
347
348  private async onRecordError() {
349    Log.info(`${this.TAG} onRecordError invoke E`);
350    promptAction.showToast({
351      message: "录像异常",
352      duration: 2000,
353    })
354    if (this.state.videoState === 'startTakeVideo') {
355      this.mAction.stopRecording();
356    }
357    this.mAction.close();
358    Log.info(`${this.TAG} onRecordError invoke X`);
359  }
360
361  aboutToAppear(): void {
362    Log.info(`${this.TAG} aboutToAppear E ${JSON.stringify(router.getParams())}`);
363    let routerParams = router.getParams();
364    if (routerParams && routerParams.pageType) {
365      this.pageType = routerParams.pageType.toString();
366    }
367    getStore().subscribe((state: OhCombinedState) => {
368      let isShowBlur: boolean = !state.contextReducer.uiEnable && state.contextReducer.uiStateMode == UiStateMode.NONE;
369      this.state = {
370        mode: state.modeReducer.mode,
371        curMode: state.modeReducer.curMode,
372        isShowPreview: state.previewReducer.isShowPreview,
373        surfaceId: state.previewReducer.surfaceId,
374        xComponentWidth: state.previewReducer.xComponentWidth,
375        xComponentHeight: state.previewReducer.xComponentHeight,
376        cameraPosition: state.cameraReducer.cameraPosition,
377        curCameraPosition: state.cameraReducer.curCameraPosition,
378        minZoomRatio: state.zoomReducer.minZoomRatio,
379        maxZoomRatio: state.zoomReducer.maxZoomRatio,
380        zoomRatio: state.zoomReducer.zoomRatio,
381        platformCapability: state.cameraInitReducer.platformCapability,
382        videoState: state.recordReducer.videoState,
383        footBarHeight: state.contextReducer.footBarHeight,
384        isShowZoomText: state.zoomReducer.isShowZoomText,
385        showZoomLabelValue: state.zoomReducer.showZoomLabelValue,
386        modeIndex: state.modeReducer.modeIndex,
387        isThirdPartyCall: state.contextReducer.isThirdPartyCall,
388        isShowBigText: state.modeReducer.isShowBigText,
389        isShowtimeLapse: state.settingReducer.isShowtimeLapse,
390        isBigVideoTimerVisible: state.recordReducer.isBigVideoTimerVisible,
391        isSmallVideoTimerVisible: state.recordReducer.isSmallVideoTimerVisible,
392        isAssGridViewShow: state.settingReducer.isAssGridViewShow,
393        isShowFlashBlack: state.previewReducer.isShowFlashBlack,
394        modeChangeDone: state.modeReducer.modeChangeDone,
395        isShowPageView: state.settingReducer.isShowSettingView,
396        showBlur: isShowBlur,
397        isFaCall: state.contextReducer.isFaCall,
398        baseZoom: 0
399
400      };
401    }, (dispatch: Dispatch) => {
402      this.mAction.setDispatch(dispatch);
403    });
404    this.mConnect(this.state)
405    this.appEventBus.on(Action.ACTION_INIT_DONE, () => this.onCameraInit);
406    this.appEventBus.on(Action.ACTION_ON_MODE_CHANGED, () => this.onModeChanged);
407    this.appEventBus.on(Action.ACTION_KEEP_SCREEN_ON, () => this.onKeepScreen);
408    this.appEventBus.on('windowSize', () => this.windowSizeChange);
409    this.appEventBus.on(Action.ACTION_RECORD_ERROR, () => this.onRecordError);
410    this.appEventBus.on(ScreenLockManager.SCREEN_CHANGE_EVENT, () => this.onScreenChange);
411    this.appEventBus.on(Action.ACTION_UPDATE_CAMERA_STATUS, () => this.updateCameraStatus);
412    GlobalContext.get().setObject('updateCameraStatus', () => {
413      Log.info(`${this.TAG} globalThis.updateCameraStatus called`);
414      this.updateCameraStatus();
415    });
416    display.getDefaultDisplay().then((dis) => {
417      this.isShowBlurSize = { width: px2vp(dis.width), height: px2vp(dis.height) };
418    })
419    this.calledByOther();
420    this.mAction.initCamera(this.state.cameraPosition, this.state.mode);
421    this.mAction.initZoomRatio(1, 6); //TODO 需要动态取得实际变焦能力范围
422    Log.info(`${this.TAG} aboutToAppear X`);
423    Log.start(Log.X_COMPONENT_LIFE);
424  }
425
426  private releaseCamera(): void {
427    Log.info(`${this.TAG} globalThis.releaseCamera called`);
428    GlobalContext.get().setObject('cameraNeedStatus', CameraNeedStatus.CAMERA_NO_NEED_TO_DO);
429    if (this.state.videoState === 'startTakeVideo') {
430      this.mAction.stopRecording();
431    }
432    this.mAction.close();
433  }
434
435  private async onForegroundInit() {
436    Log.info(`${this.TAG} onForegroundInit E `);
437    this.pageType = '';
438    GlobalContext.get().setObject('cameraNeedStatus', CameraNeedStatus.CAMERA_NO_NEED_TO_DO);
439    this.calledByOther();
440    this.mAction.initCamera(this.state.cameraPosition, this.state.mode);
441    await new Promise<Function | string>((resolve: Function | string) => setTimeout(resolve, 40));
442    this.doCameraAction();
443    this.mAction.updateInitShowFlag(true);
444    if (!this.state.isThirdPartyCall) {
445      this.mAction.reloadThumbnail();
446    }
447    Log.info(`${this.TAG} onForegroundInit X`);
448  }
449
450  private calledByOther(): void {
451    Log.info(`${this.TAG} calledByOther invoke E`);
452    let from: string = "";
453    let action: string = "";
454
455    if (GlobalContext.get().getCameraAbilityWant()) {
456      Log.info(`${this.TAG} cameraAbilityWant: ${JSON.stringify(GlobalContext.get().getCameraAbilityWant())}`);
457      if (GlobalContext.get().getCameraAbilityWant()?.parameters?.from) {
458        from = GlobalContext.get().getCameraAbilityWant()?.parameters?.from as string;
459      }
460      let cameraAbilityWant = GlobalContext.get().getCameraAbilityWant();
461      if (cameraAbilityWant?.action) {
462        action = cameraAbilityWant?.action;
463      }
464    } else {
465      this.mAction.thirdPartyCall(false, "");
466      return;
467    }
468    Log.info(`${this.TAG} from: ${from}  uri: ${action}`);
469
470    if (from === "FA") {
471      Log.info(`from === "FA"`);
472      if (GlobalContext.get().getCameraAbilityWant()?.parameters?.action) {
473        action = GlobalContext.get().getCameraAbilityWant()?.parameters?.action as string;
474      }
475      this.mAction.faCall(true);
476      this.mAction.thirdPartyCall(false, "");
477      this.initStateMode(action);
478    } else if (action != "") {
479      this.mAction.faCall(false);
480      this.mAction.thirdPartyCall(true, GlobalContext.get().getCameraAbilityWant().action as string);
481      this.initStateMode(action);
482    } else {
483      this.mAction.faCall(false);
484      this.mAction.thirdPartyCall(false, "");
485    }
486    Log.info(`${this.TAG} calledByOther invoke X: ${this.state.mode}`);
487  }
488
489  private initStateMode(action: string): void {
490    switch (action) {
491      case wantConstant.Action.ACTION_IMAGE_CAPTURE:
492        this.mAction.initMode('PHOTO');
493        this.mAction.updateModeIndex(0);
494        break;
495      case wantConstant.Action.ACTION_VIDEO_CAPTURE:
496        this.mAction.initMode('VIDEO');
497        this.mAction.updateModeIndex(1);
498        break;
499      default:
500        Log.info(`${this.TAG} FA default`);
501        break;
502    }
503  }
504
505  private updateCameraStatus(): void {
506    Log.info(`${this.TAG} updateCameraStatus  cameraStatus: ${GlobalContext.get().getT<string>('cameraStatus')}
507      cameraNeedStatus: ${GlobalContext.get().getT<string>('cameraNeedStatus')}`);
508    if (this.canInit() && GlobalContext.get().getT<string>('cameraNeedStatus') == CameraNeedStatus.CAMERA_NEED_INIT) {
509      this.onForegroundInit();
510    }
511    if (this.canRelease() && GlobalContext.get()
512      .getT<string>('cameraNeedStatus') == CameraNeedStatus.CAMERA_NEED_RELEASE) {
513      this.releaseCamera();
514    }
515    Log.info(`${this.TAG} updateCameraStatus X`);
516  }
517
518  private canInit(): boolean {
519    //相机状态是首次加载或者session释放完成状态,才能进行初始化操作
520    return GlobalContext.get().getT<string>('cameraStatus') == CameraStatus.CAMERA_BEFORE_VALUE ||
521      GlobalContext.get().getT<string>('cameraStatus') == CameraStatus.CAMERA_RELEASE_FINISHED;
522  }
523
524  private canRelease(): boolean {
525    //相机状态是预览完成状态或录像完成状态才能进行释放操作
526    return GlobalContext.get().getT<string>('cameraStatus') == CameraStatus.CAMERA_PREVIEW_FINISHED ||
527      GlobalContext.get().getT<string>('cameraStatus') == CameraStatus.CAMERA_TAKE_VIDEO_FINISHED;
528  }
529
530  aboutToDisappear(): void {
531    Log.info(`${this.TAG} aboutToDisappear E`);
532    this.appEventBus.off(Action.ACTION_INIT_DONE, () => this.onCameraInit);
533    this.appEventBus.off(Action.ACTION_ON_MODE_CHANGED, () => this.onModeChanged);
534    this.appEventBus.off('windowSize', () => this.windowSizeChange);
535    this.appEventBus.off(Action.ACTION_UPDATE_CAMERA_STATUS, () => this.updateCameraStatus);
536    // this.mConnect?.destroy();
537    GeoLocation.getInstance().off();
538    GlobalContext.get().setObject('cameraNeedStatus', CameraNeedStatus.CAMERA_NEED_RELEASE)
539    this.updateCameraStatus();
540    Log.info(`${this.TAG} aboutToDisappear X`);
541  }
542
543  onPageHide(): void {
544    this.mAction.startVideoFlag(false);
545    Log.info(`${this.TAG} onPageHide`);
546  }
547
548  private switchAnimationSec(): void {
549    Log.info(`${this.TAG} switchAnimationSec called`);
550    animateTo({ duration: 200,
551      delay: 0,
552      curve: Curve.Sharp,
553    }, () => {
554      this.btnOpacitySec = 1;
555    })
556    animateTo({
557      duration: 350,
558      curve: Curve.FastOutSlowIn,
559      delay: 0,
560    }, () => {
561      Log.info(`${this.TAG} btnSwitchSec callback btnSwitchSec= ${this.btnSwitchSec}`);
562      this.scaleXSec = 1;
563    })
564    animateTo({
565      duration: 350,
566      curve: 'cubic-bezier(0.21, 0.27, 0.20, 1.00)',
567      delay: 0,
568      onFinish: () => {
569        Log.info(`${this.TAG} btnSwitchSec onFinish btnSwitchSec= ${this.btnSwitchSec}`);
570        this.btnSwitchSec = false;
571        this.btnOpacitySec = 0;
572        this.scaleXSec = 0.8;
573        this.rotateAngleSec = 80;
574        this.switchBackgroundAnimation();
575      }
576    }, () => {
577      Log.info(`${this.TAG} btnSwitchSec callback btnSwitchSec= ${this.btnSwitchSec}`);
578      this.rotateAngleSec = 0;
579    })
580  }
581
582  private switchBackgroundAnimation(): void {
583    animateTo({ duration: 350,
584      delay: 0,
585      curve: Curve.Sharp,
586      onFinish: () => {
587        this.isSwitchBackground = false;
588        this.switchBackgroundOpacity = 1;
589      }
590    }, () => {
591      this.switchBackgroundOpacity = 0;
592    })
593  }
594
595  private pinchGestureStart(event: GestureEvent): void {
596    Log.info(`${this.TAG} pinchGestureStart invoke E`);
597    if (this.state.mode != 'MULTI' && this.state.cameraPosition !== 'FRONT') {
598      clearTimeout(this.pinchGestureTimer);
599      this.mAction.updateIsShowZoomText(true);
600      this.mAction.updateShowPinch(true);
601      this.mAction.updateShowZoomLabelValue(false);
602      this.baseZoom = this.state.zoomRatio;
603    }
604    if (this.state.mode !== "MULTI" && this.state.cameraPosition !== 'FRONT') {
605      this.mAction.updateIsShowZoomText(true);
606      this.mAction.updateShowPinch(true);
607      this.mAction.updateIsPhotoZoomDetails(true);
608      this.mAction.updateBaseZoom(this.state.zoomRatio);
609      clearTimeout(this.pinchGestureTimerId);
610      clearTimeout(this.onTouchDownTimerId);
611    }
612    Log.info(`${this.TAG} pinchGestureStart invoke X`);
613  }
614
615  private pinchGestureUpdate(event: GestureEvent): void {
616    Log.info(`${this.TAG} pinchGestureUpdate invoke E`);
617    if (this.state.mode != 'MULTI' && this.state.cameraPosition !== 'FRONT') {
618      let zoomRatio = event.scale + this.baseZoom - 1;
619      if (zoomRatio > 6) {
620        zoomRatio = 6;
621      }
622      if (zoomRatio < 1) {
623        zoomRatio = 1;
624      }
625      this.mAction.changeZoomRatio(zoomRatio);
626    }
627    Log.info(`${this.TAG} pinchGestureUpdate invoke X`);
628  }
629
630  private pinchGestureEnd(event: GestureEvent): void {
631    Log.info(`${this.TAG} pinchGestureEnd invoke E`);
632    this.mAction.updateShowZoomLabelValue(true);
633    this.mAction.updateShowPinch(false);
634    if (this.state.mode != 'MULTI' && this.state.cameraPosition !== 'FRONT') {
635      this.pinchGestureTimer = setTimeout(() => {
636        this.mAction.updateIsShowZoomText(false);
637      }, 2000)
638    }
639    Log.info(`${this.TAG} pinchGestureEnd invoke X`);
640  }
641
642  private onPreviewClicked(): void {
643    Log.info(`${this.TAG} onPreviewClicked isShowtimeLapse= ${this.state.isShowtimeLapse}, state.mode= ${this.state.mode}`);
644    if (this.state.mode === 'PHOTO' && this.state.isShowtimeLapse) {
645      this.mAction.changeTimeLapse(false);
646      this.mAction.capture();
647    } else if (this.state.mode === 'VIDEO' && this.state.isShowtimeLapse) {
648      this.mAction.changeTimeLapse(false);
649      this.mAction.startRecording();
650    }
651    Log.info(`${this.TAG} onPreviewClicked invoke X`);
652  }
653
654  private swipeChangeMode(swipe: number): void {
655    Log.info(`${this.TAG} swipeChangeMode E`);
656    if (!this.state.modeChangeDone && this.state.modeIndex + swipe >= 0 && this.state.modeIndex + swipe <= this.modeArray.length - 1 && !this.state.isShowtimeLapse) {
657      this.mAction.swipeChangeMode(this.state.modeIndex + swipe);
658      this.mAction.swipeModeChangeDone(true);
659    }
660    Log.info(`${this.TAG} swipeChangeMode X`);
661  }
662
663  private onKeepScreen(data: KeepScreenData): void {
664    Log.info(`${this.TAG} onKeepScreen E`);
665    if (data) {
666      GlobalContext.get()
667        .getCameraWinClass()
668        .setKeepScreenOn(data.isKeepScreenOn)
669        .then((v) => {
670          Log.info('Succeeded in setting the screen to be always on. Data: ' + JSON.stringify(v));
671        })
672        .catch((err: BusinessError) => {
673          Log.error('Failed to set the screen to be always on. Cause: ' + JSON.stringify(err));
674        });
675    }
676    Log.info(`${this.TAG} onKeepScreen X`);
677  }
678
679  private windowSizeChange(data: ScreenSizeType): void {
680    if (this.screenSize != data) {
681      this.screenSize = data;
682      this.resetScreenSize(data);
683    }
684  }
685
686  private resetScreenSize(size: ScreenSizeType): void {
687    this.settingManager.setScreenHeight(size.height);
688    this.settingManager.setScreenWidth(size.width);
689    let xComponentSize = this.settingManager.getPreviewDisplaySize(this.state.mode);
690    Log.info(this.TAG + " PreviewArea xComponentSize = " + JSON.stringify(xComponentSize));
691    this.mAction.changeXComponentSize(xComponentSize.width, xComponentSize.height);
692  }
693
694  private async onScreenChange(isScreenOn: boolean) {
695    Log.info(`${this.TAG} onScreenChanged E isScreenOn: ${isScreenOn}`);
696    if (!isScreenOn) {
697      GlobalContext.get().setObject('keepCameraZoomRatio', true);
698      if (this.state.isShowPageView) {
699        this.mAction.hideSettingView();
700      }
701    }
702    Log.info(`${this.TAG} onScreenChanged X`);
703  }
704
705  build() {
706    Column() {
707      if (this.state.isShowPreview) {
708        Stack() {
709          XComponent({
710            id: '',
711            type: 'surface',
712            libraryname: '',
713            controller: GlobalContext.get().getXComponentController()
714          })
715            .onLoad(() => {
716              Log.end(Log.X_COMPONENT_LIFE);
717              Log.info(`${this.TAG} XComponent_onLoad`);
718              let surfaceId: number = GlobalContext.get().getXComponentController().getXComponentSurfaceId();
719              this.mAction.prepareSurface(surfaceId);
720              this.doCameraAction();
721            })
722            .width(this.state.xComponentWidth)
723            .height(this.state.xComponentHeight)
724            .animation({
725              duration: 100,
726              curve: Curve.Sharp,
727              delay: 0,
728              iterations: 1,
729              playMode: PlayMode.Normal
730            })
731
732          if (this.isSwitchBackground) {
733            Column() {
734            }
735            .width(this.state.xComponentWidth)
736            .height(this.state.xComponentHeight)
737            .backgroundColor('#000')
738            .opacity(this.switchBackgroundOpacity)
739          }
740
741          if (this.btnSwitch) {
742            Image($r('app.media.whitePicture'))
743              .width(this.state.xComponentWidth)
744              .height(this.state.xComponentHeight)
745              .syncLoad(false)
746              .scale({ x: this.scaleX, y: 1.0 })
747              .rotate({ x: 0, y: 10, z: 0, angle: this.rotateAngle })// .opacity(this.state.btnOpacity_first)
748              .onComplete(() => {
749              })
750          }
751
752          if (this.btnSwitchSec) {
753            Image($r('app.media.whitePicture'))
754              .width(this.state.xComponentWidth)
755              .height(this.state.xComponentHeight)
756              .syncLoad(false)
757              .scale({ x: this.scaleX, y: 1.0 })
758              .rotate({ x: 0, y: 10, z: 0, angle: this.rotateAngleSec })// .opacity(this.state.btnOpacity_sec)
759              .onComplete(() => {
760              })
761          }
762
763          if (this.state.isShowFlashBlack) {
764            ShowFlashBlack();
765          }
766
767          if (this.state.isShowZoomText && deviceInfo.deviceType !== "default" && !this.state.isShowtimeLapse) {
768            Stack({ alignContent: Alignment.Top }) {
769              Column() {
770                Column() {
771                  ZoomText({ state: $state })
772                }
773                .width('100%')
774                .height('100%')
775              }
776              .width('100%')
777              .height('100%')
778              .alignItems(HorizontalAlign.Start)
779            }
780            .width('100%')
781            .height('96%')
782          }
783
784          if (this.state.isAssGridViewShow === '1' && this.state.mode !== 'MORE') {
785            AssistiveGridView();
786          }
787
788          if (this.state.isSmallVideoTimerVisible) {
789            Column() {
790              SmallVideoTimer();
791            }
792            .width('100%')
793            .height('100%')
794            .alignItems(HorizontalAlign.Start)
795          }
796          if (this.state.isBigVideoTimerVisible) {
797            Column() {
798              BigVideoTimer();
799            }
800            .width('100%')
801            .height('100%')
802            .alignItems(HorizontalAlign.Start)
803          }
804          if (this.state.isShowtimeLapse) {
805            Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
806              Column() {
807                TimeLapseView();
808              }.width('100%').height(150)
809            }.width('100%').height('100%')
810          }
811          if (this.state.showBlur) {
812            Column()
813              .width(this.isShowBlurSize.width)
814              .height(this.isShowBlurSize.height)
815              .backgroundColor(Color.Black)
816              .border({ width: { top: 0.5, bottom: 0.5 }, color: Color.Black })
817          }
818
819          Stack() {
820            if (this.state.isShowBigText) {
821              Stack({ alignContent: Alignment.BottomStart }) {
822                Column() {
823                  Column() {
824                    BigText()
825                  }
826                  .width('100%')
827                  .height('100%')
828                }
829                .width('100%')
830                .height('96vp')
831                .alignItems(HorizontalAlign.Start)
832              }
833              .width('100%')
834              .height('100%')
835            }
836          }
837          .width(this.screenSize.width > this.screenSize.height ? this.screenSize.height : this.screenSize.width)
838          .height(this.screenSize.height > (this.screenSize.width * 9 / 16) ? (this.screenSize.width * 9 / 16) :
839          this.screenSize.height)
840        }
841        .width('100%')
842        .height('100%')
843      }
844    }
845    .width(this.state.xComponentWidth)
846    .height(this.state.xComponentHeight)
847    .margin({ top: 0 })
848    .backgroundColor('#000')
849    .gesture(
850      GestureGroup(
851        GestureMode.Exclusive,
852        TapGesture({ fingers: 1, count: 1 })
853          .onAction(() => this.onPreviewClicked),
854        PinchGesture({ fingers: 2, distance: 1 })
855          .onActionStart(() => this.pinchGestureStart)
856          .onActionUpdate(() => this.pinchGestureUpdate)
857          .onActionEnd(() => this.pinchGestureEnd),
858        PanGesture({ fingers: 1, direction: PanDirection.Up, distance: 10 })
859          .onActionEnd(() => {
860            if (!this.state.isThirdPartyCall && !this.state.isFaCall) {
861              this.swipeChangeMode(1);
862            }
863          }),
864        PanGesture({ fingers: 1, direction: PanDirection.Down, distance: 10 })
865          .onActionEnd(() => {
866            if (!this.state.isThirdPartyCall && !this.state.isFaCall) {
867              this.swipeChangeMode(-1);
868            }
869          })
870      )
871    )
872  }
873}