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