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 display from '@ohos.display';
17import deviceInfo from '@ohos.deviceInfo';
18import { ModeMap } from '../common/ModeMap';
19import { Action } from '@ohos/common/src/main/ets/default/redux/actions/Action';
20import { CameraWorker } from '@ohos/common/src/main/ets/default/worker/CameraWorker';
21import { EventBus } from '@ohos/common/src/main/ets/default/worker/eventbus/EventBus';
22import { EventBusManager } from '@ohos/common/src/main/ets/default/worker/eventbus/EventBusManager';
23import { Dispatch, OhCombinedState } from '@ohos/common/src/main/ets/default/redux/store';
24import { getStore } from '@ohos/common/src/main/ets/default/redux/store';
25import { Log } from '@ohos/common/src/main/ets/default/utils/Log';
26import { MoreList } from '@ohos/common/src/main/ets/default/featurecommon/moreList/moreList';
27import {
28  PersistType,
29  PreferencesService
30} from '@ohos/common/src/main/ets/default/featurecommon/preferences/PreferencesService';
31import { RdbStoreManager } from '@ohos/common/src/main/ets/default/setting/storage/RdbStoreManager';
32import { TabBar } from '@ohos/common/src/main/ets/default/featurecommon/tabbar/TabBar';
33import { ZoomView } from '@ohos/common/src/main/ets/default/featurecommon/zoomview/ZoomView';
34import { Control } from './Control';
35import { FootBar } from './FootBar';
36import { PreviewArea } from './PreviewArea';
37import { SettingView } from './SettingView';
38import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
39import { CameraId } from '@ohos/common/src/main/ets/default/setting/settingitem/CameraId';
40import { BusinessError } from '@ohos.base';
41import ability from '@ohos.ability.ability';
42import { GlobalContext } from '@ohos/common/src/main/ets/default/utils/GlobalContext';
43
44CameraWorker.getInstance(new ModeMap());
45
46PersistentStorage.PersistProp('storageCameraId', '');
47
48class StateStruct {
49  footBarHeight: number = 0;
50  permissionFlag: boolean = false;
51  mode: string = '';
52  curMode: string = '';
53  isShowtimeLapse: boolean = false;
54  videoState: string = '';
55  isShowMoreList: boolean = false;
56  isThirdPartyCall: boolean = false;
57  showZoomLabelValue: boolean = true;
58  isShowPinch: boolean = false;
59  isShowPageView: boolean = false;
60  isInitiated: boolean = false;
61  curCameraPosition: CameraId = CameraId.BACK
62}
63
64class IndexDispatcher {
65  private mDispatch: Dispatch = (data) => data;
66
67  public setDispatch(dispatch: Dispatch) {
68    this.mDispatch = dispatch;
69  }
70
71  public initFootBarHeight(footBarHeight: number): void {
72    this.mDispatch(Action.initFootBarHeight(footBarHeight));
73  }
74
75  public setPermissionFlag(permissionFlag: boolean): void {
76    Log.info(`CameraApp setPermissionFlag: ${permissionFlag}`);
77    this.mDispatch(Action.setPermissionFlag(permissionFlag));
78  }
79
80  public initAction(action: string): void {
81    this.mDispatch(Action.initAction(action));
82  }
83
84  public initCameraPosition(cameraPosition: string): void {
85    this.mDispatch(Action.setCameraPosition(cameraPosition));
86  }
87
88  public initMode(mode: string): void {
89    this.mDispatch(Action.initMode(mode));
90  }
91
92  public changeTimeLapse(isShowtimeLapse: boolean): void {
93    this.mDispatch(Action.changeTimeLapse(isShowtimeLapse));
94  }
95
96  public stopRecording(): void {
97    this.mDispatch(Action.stopRecording());
98    this.mDispatch(Action.updateVideoState('beforeTakeVideo'));
99    this.mDispatch(Action.updateBigVideoTimerVisible(false));
100    this.mDispatch(Action.updateSmallVideoTimerVisible(false));
101  }
102
103  public resetRecordingTime(): void {
104    this.mDispatch(Action.updateRecordingTime(0));
105    this.mDispatch(Action.updateRecordingTimeDisplay('00:00'));
106  }
107
108  public hideSettingView(): void {
109    this.mDispatch(Action.showSettingView(false));
110  }
111
112  public updateModeIndex(index: number): void {
113    this.mDispatch(Action.updateModeIndex(index));
114  }
115
116  public faCall(isFaCall: boolean): void {
117    this.mDispatch(Action.faCall(isFaCall));
118  }
119}
120
121const ZOOM_HEIGHT = 140;
122
123@Entry
124@Component
125struct Index {
126  appEventBus: EventBus = EventBusManager.getInstance().getEventBus();
127  @State state: StateStruct = new StateStruct();
128  protected mPreferencesService: PreferencesService = PreferencesService.getInstance();
129  private TAG: string = '[Index]:';
130  private modeArray: Array<string> = ['PHOTO', 'VIDEO'];
131  private mAction: IndexDispatcher = new IndexDispatcher();
132
133  aboutToAppear(): void {
134    Log.info(`${this.TAG} aboutToAppear E`);
135    let dbStore: RdbStoreManager = RdbStoreManager.getInstance();
136    dbStore.initRdbConfig();
137    getStore().subscribe((state: OhCombinedState) => {
138      this.state = {
139        footBarHeight: state.contextReducer.footBarHeight,
140        permissionFlag: state.contextReducer.permissionFlag,
141        mode: state.modeReducer.mode,
142        curMode: state.modeReducer.curMode,
143        isShowtimeLapse: state.settingReducer.isShowtimeLapse,
144        videoState: state.recordReducer.videoState,
145        isShowMoreList: state.modeReducer.isShowMoreList,
146        isThirdPartyCall: state.contextReducer.isThirdPartyCall,
147        showZoomLabelValue: state.zoomReducer.showZoomLabelValue,
148        isShowPinch: state.zoomReducer.isShowPinch,
149        isShowPageView: state.settingReducer.isShowSettingView,
150        isInitiated: state.modeReducer.isInitiated,
151        curCameraPosition: state.cameraReducer.curCameraPosition,
152      };
153    }, (dispatch: Dispatch) => {
154      this.mAction.setDispatch(dispatch);
155    })
156    display.getDefaultDisplay().then((dis) => {
157      Log.info(`${this.TAG} dis data = ${JSON.stringify(dis)}`);
158      let footBarHeight: number = 0;
159      if (deviceInfo.deviceType === "default") {
160        footBarHeight = px2vp(dis.width) * (4 / 3) - 160;
161      } else {
162        footBarHeight = px2vp(dis.width) * (4 / 3) - 82;
163      }
164      this.mAction.initFootBarHeight(footBarHeight);
165    })
166
167    if (!this.state.permissionFlag) {
168      let permissionList: Array<Permissions> = [
169        "ohos.permission.MEDIA_LOCATION",
170        "ohos.permission.READ_IMAGEVIDEO",
171        "ohos.permission.WRITE_IMAGEVIDEO",
172        "ohos.permission.CAMERA",
173        "ohos.permission.MICROPHONE",
174        "ohos.permission.DISTRIBUTED_DATASYNC",
175        "ohos.permission.LOCATION",
176        "ohos.permission.LOCATION_IN_BACKGROUND",
177        "ohos.permission.APPROXIMATELY_LOCATION"
178      ];
179      Log.info(`${this.TAG} permissions need to require from user: ${JSON.stringify(permissionList)}`);
180      let atManager = abilityAccessCtrl.createAtManager();
181      try {
182        atManager.requestPermissionsFromUser(GlobalContext.get().getCameraAbilityContext(), permissionList).then((data) => {
183          Log.info(`${this.TAG} data permissions: ${JSON.stringify(data.permissions)}`);
184          Log.info(`${this.TAG} data authResult: ${JSON.stringify(data.authResults)}`);
185          let sum = 0
186          for (let i = 0; i < data.authResults.length; i++) {
187            sum += data.authResults[i];
188          }
189          if (sum >= 0) {
190            GlobalContext.get().setObject('permissionFlag', true);
191            this.mAction.setPermissionFlag(true);
192          } else {
193            GlobalContext.get().setObject('permissionFlag', false);
194            this.mAction.setPermissionFlag(false);
195          }
196          Log.info(`${this.TAG} request permissions result: ${GlobalContext.get().getT<boolean>('permissionFlag')}`);
197        }, (err: BusinessError) => {
198          Log.error(`${this.TAG} Failed to start ability err code: ${err.code}`);
199        })
200      } catch (error) {
201        Log.info(`${this.TAG} catch error: ${JSON.stringify(error)}`);
202      }
203    }
204
205    if (GlobalContext.get().getCameraFormParam() != undefined) {
206      this.mAction.initAction(GlobalContext.get().getCameraFormParam().action);
207      this.mAction.initMode(GlobalContext.get().getCameraFormParam().mode);
208      GlobalContext.get().setCameraFormParam(undefined);
209    }
210
211    GlobalContext.get().setObject('stopCameraRecording', () => {
212      this.stopCameraRecording();
213    })
214
215    if (!this.state.isInitiated) {
216      let initIndex: number = this.mPreferencesService.getModeValue(PersistType.FOR_AWHILE, 0);
217      Log.info(`${this.TAG} initModeIndex: ${initIndex}`);
218      this.mAction.initMode(this.modeArray[initIndex]);
219      this.mAction.updateModeIndex(initIndex);
220    }
221
222    Log.info(`${this.TAG} aboutToAppear X`);
223  }
224
225  onPageShow(): void {
226    Log.info(`${this.TAG} onPageShow this.permissionFlag: ${this.state.permissionFlag} permissionFlag: ${GlobalContext.get().getT<boolean>('permissionFlag')}`);
227    this.mAction.setPermissionFlag(GlobalContext.get().getT<boolean>('permissionFlag'));
228    let curCameraId = AppStorage.Get<string>('storageCameraId');
229    if (curCameraId) this.mAction.initCameraPosition(curCameraId);
230    this.mAction.resetRecordingTime();
231    Log.info(`${this.TAG} onPageShow X`);
232  }
233
234  aboutToDisappear(): void {
235    Log.info(`${this.TAG} aboutToDisappear E`);
236  }
237
238  onBackPress(): boolean {
239    Log.info(`${this.TAG} onBackPress E`);
240    if (this.state.isShowPageView) {
241      this.mAction.hideSettingView();
242      return true;
243    } else if (this.state.isShowtimeLapse) {
244      this.mAction.changeTimeLapse(false);
245      return true
246    } else if (this.state.isThirdPartyCall) {
247      let that = this;
248      that.terminateSelfWithResult();
249    } else {
250      if (this.state.videoState === 'startTakeVideo' || this.state.videoState === 'pauseTakeVideo') {
251        this.mAction.stopRecording();
252        return true;
253      }
254      Log.info(`${this.TAG} onBackPress X`);
255      return false;
256    }
257    return false;
258  }
259
260  onPageHide(): void {
261    Log.info(`${this.TAG} onPageHide E`);
262    this.stopCameraRecording();
263    Log.info(`${this.TAG} onPageHide X`);
264  }
265
266  public stopCameraRecording() {
267    Log.info(`${this.TAG} stopCameraRecording E`);
268    if (this.state.isShowtimeLapse) {
269      this.mAction.changeTimeLapse(false);
270    }
271    if (this.state.videoState === 'startTakeVideo' || this.state.videoState === 'pauseTakeVideo') {
272      this.mAction.stopRecording();
273    }
274    Log.info(`${this.TAG} stopCameraRecording X`);
275  }
276
277  terminateSelfWithResult() {
278    Log.info(`${this.TAG} terminateSelfWithResult start`);
279    let abilityResult: ability.AbilityResult = {
280      resultCode: -1,
281      want: {
282        parameters: {
283          resourceUri: '',
284          mode: this.state.mode
285        }
286      }
287    };
288    Log.info(`${this.TAG} terminateSelfWithResult start1`);
289    if (GlobalContext.get().getSession()) {
290      Log.info(`${this.TAG} terminateSelfWithResult start2`);
291      GlobalContext.get().getSession().terminateSelfWithResult(abilityResult, (error: BusinessError) => {
292        if (error) {
293          Log.error(`${this.TAG} Operation failed. Cause: ${error?.code}`);
294          return;
295        }
296        Log.info(`${this.TAG} Operation succeeded`);
297      });
298    } else {
299      GlobalContext.get().getCameraAbilityContext().terminateSelfWithResult(abilityResult, (error: BusinessError) => {
300        if (error) {
301          Log.error(`${this.TAG} Operation failed. Cause: ${error}`);
302          return;
303        }
304        Log.info(`${this.TAG} Operation succeeded`);
305      });
306    }
307  }
308
309  build() {
310    Stack({ alignContent: Alignment.Top }) {
311      Stack() {
312        PreviewArea()
313      }.width('100%').position({ y: 48 })
314
315      Stack() {
316        if (deviceInfo.deviceType !== "default" && this.state.videoState === "beforeTakeVideo" && !this.state.isShowtimeLapse && this.state.showZoomLabelValue) {
317          TabBar({ onBackClicked: () => this.onBackClicked() });
318        }
319      }.width('100%').height(48).position({ x: '0', y: '0' })
320
321      if (this.state.isShowMoreList && this.state.showZoomLabelValue) {
322        MoreList()
323      }
324      Stack({ alignContent: Alignment.Bottom }) {
325        Column() {
326          if (this.state.mode === "PHOTO" || this.state.mode === "VIDEO") {
327            if (deviceInfo.deviceType !== "default" && this.state.curCameraPosition !== 'FRONT') {
328              Column() {
329                ZoomView()
330              }
331              .visibility(!this.state.isShowtimeLapse && !this.state.isShowPinch ? Visibility.Visible : Visibility.Hidden)
332            }
333          }
334          if (this.state.videoState === "beforeTakeVideo" && this.state.showZoomLabelValue) {
335            Column() {
336              Control()
337            }
338            .visibility((!this.state.isShowtimeLapse && this.state.showZoomLabelValue) ? Visibility.Visible : Visibility.Hidden)
339            .width('100%')
340            .offset({
341              x: 156, y: 0
342            })
343          }
344          Column() {
345            FootBar()
346          }
347          .visibility((!this.state.isShowtimeLapse && this.state.showZoomLabelValue) ? Visibility.Visible : Visibility.Hidden)
348        }
349      }.width('100%').height(302 - ZOOM_HEIGHT).position({ y: this.state.footBarHeight + ZOOM_HEIGHT })
350      .visibility(!this.state.isShowtimeLapse ? Visibility.Visible : Visibility.Hidden)
351
352      if (this.state.isShowPageView) {
353        SettingView().width('100%').height('100%')
354      }
355    }.width('100%').height('100%').backgroundColor('#000')
356  }
357
358  private onBackClicked(): void {
359    Log.info(`${this.TAG} onBackClicked E`);
360    let that = this;
361    that.terminateSelfWithResult();
362  }
363}