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 { Action } from '../redux/actions/Action';
17import { CameraId } from '../setting/settingitem/CameraId';
18import { CameraPlatformCapability } from '../camera/CameraPlatformCapability';
19import { Log } from '../utils/Log';
20import { CameraStatus } from '../utils/Constants';
21import { BaseFunction } from './BaseFunction';
22import { FunctionCallBack } from '../camera/CameraService';
23import ReportUtil from '../utils/ReportUtil';
24import { GlobalContext } from '../utils/GlobalContext';
25
26export class CameraBasicFunction extends BaseFunction {
27  private TAG = '[CameraBasicFunction]:';
28  private mCameraId: string = CameraId.BACK;
29  private mSurfaceId: string = '';
30  private mCurrentMode: string = '';
31  private mSessionList: string[] = [];
32  private isSessionReleasing: boolean = false
33  private initDataCache: any = null
34  public startIdentification: boolean = false
35
36  private functionBackImpl: FunctionCallBack = {
37    onCapturePhotoOutput: (): void => {
38      Log.info(`${this.TAG} functionBackImpl onCapturePhotoOutput`)
39      this.mWorkerManager.postMessage(Action.capturePhotoOutput())
40    },
41    onCaptureSuccess: (thumbnail: any, resourceUri: any): void => {
42      Log.info(`${this.TAG} functionBackImpl onCaptureSuccess ${thumbnail}`)
43      this.mWorkerManager.postMessage(Action.updateThumbnail(thumbnail, resourceUri))
44    },
45    onCaptureFailure: (): void => {
46      Log.info(`${this.TAG} functionBackImpl onCaptureFailure`)
47      this.mWorkerManager.postMessage(Action.captureError())
48    },
49    onRecordSuccess: (thumbnail: any): void => {
50      Log.info(`${this.TAG} functionBackImpl onRecordSuccess ${thumbnail}`)
51      this.mWorkerManager.postMessage(Action.recordDone(thumbnail))
52    },
53    onRecordFailure: (): void => {
54      Log.info(`${this.TAG} functionBackImpl onRecordFailure`)
55      this.mWorkerManager.postMessage(Action.recordError())
56    },
57    thumbnail: (thumbnail: any): void => {
58      Log.info(`${this.TAG} functionBackImpl thumbnail ${thumbnail}`)
59      this.mWorkerManager.postMessage(Action.loadThumbnail(thumbnail))
60    }
61  }
62
63  public async initCamera(data, callType?: string) {
64    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_BEGIN_INIT);
65    Log.info(`${this.TAG} initCamera this.startIdentification:${JSON.stringify(this.startIdentification)} `);
66    if (this.startIdentification) {
67      const platformCapability = CameraPlatformCapability.getInstance();
68      this.mWorkerManager.postMessage(Action.initCameraDone(platformCapability));
69      return;
70    }
71    if (callType) this.startIdentification = true
72    Log.start(`${this.TAG} initCamera`)
73    this.mSessionList.push('CREATE')
74    let curStorageCameraId = AppStorage.Get<string>('storageCameraId')
75    if (curStorageCameraId) {
76      data.cameraId = curStorageCameraId
77    }
78    Log.info(`${this.TAG} initData:${JSON.stringify(data)} `)
79    this.initDataCache = data
80    if (GlobalContext.get().getT<boolean>('isSessionCreating') || this.isSessionReleasing) {
81      Log.info(`${this.TAG} initCamera isSessionCreating or isSessionReleasing return`)
82      return
83    }
84    this.mCameraId = data.cameraId
85    this.mCurrentMode = data.mode
86    let mCameraCount = await this.mCameraService.initCamera(this.mCameraId)
87    const platformCapability = CameraPlatformCapability.getInstance()
88    await platformCapability.init(mCameraCount)
89    this.mWorkerManager.postMessage(Action.initCameraDone(platformCapability))
90    this.mCameraService.getThumbnail(this.functionBackImpl)
91    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_INIT_FINISHED);
92    this.mWorkerManager.postMessage(Action.updateCameraStatus())
93    Log.end(`${this.TAG} initCamera`)
94  }
95
96  private async imageSize(data) {
97    Log.info(`${this.TAG} imageSize ${JSON.stringify(data)}  E`)
98    this.mCameraService.mImageSize.imageWidth = data.imageSize.width
99    this.mCameraService.mImageSize.imageHeight = data.imageSize.height
100    Log.info(`${this.TAG} imageSize X`)
101  }
102
103  private async videoSize(data) {
104    Log.info(`${this.TAG} videoSize ${JSON.stringify(data)}  E`)
105    this.mCameraService.mVideoFrameSize.frameWidth = data.videoSize.width
106    this.mCameraService.mVideoFrameSize.frameHeight = data.videoSize.height
107    Log.info(`${this.TAG} videoSize X`)
108  }
109
110  private async onSurfacePrepare(data) {
111    Log.info(`${this.TAG} onSurfacePrepare ${JSON.stringify(data)}  E`)
112    this.mSurfaceId = data.surfaceId
113    Log.info(`${this.TAG} onSurfacePrepare X`)
114  }
115
116  private async startPreview(data?) {
117    Log.start(`${this.TAG} startPreview`)
118    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_BEGIN_PREVIEW);
119    if (!this.mSurfaceId) {
120      Log.info(`${this.TAG} startPreview error mSurfaceId is null`)
121      this.enableUi()
122      return
123    }
124    await this.mCameraService.createPreviewOutput(this.mSurfaceId, this.mCurrentMode)
125    if (await this.isVideoMode()) {
126      //      await this.mCameraService.createVideoOutput(this.functionBackImpl)
127    } else {
128      await this.mCameraService.createPhotoOutput(this.functionBackImpl)
129    }
130    await this.mCameraService.createSession(this.mSurfaceId, await this.isVideoMode())
131    if ([...this.mSessionList].pop() === 'RELEASE') {
132      await this.close()
133    }
134    if (data && data?.zoomRatio && data.zoomRatio !== 1) {
135      await this.mCameraService.setZoomRatio(data.zoomRatio)
136    }
137    this.mSessionList = []
138    this.enableUi()
139    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_PREVIEW_FINISHED);
140    this.mWorkerManager.postMessage(Action.updateCameraStatus())
141    Log.end(`${this.TAG} startPreview`)
142  }
143
144  private async reStartPreview(data) {
145    Log.start(`${this.TAG} reStartPreview`)
146    if (!this.mSurfaceId) {
147      Log.info(`${this.TAG} reStartPreview error mSurfaceId is null`)
148      this.enableUi()
149      return
150    }
151    this.mCameraService.setCameraId(this.mCameraId)
152    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_RELEASING);
153    await this.mCameraService.releaseCamera()
154    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_RELEASE_FINISHED);
155    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_BEGIN_PREVIEW);
156    await this.mCameraService.createCameraInput(this.mCameraId)
157    await this.mCameraService.createPreviewOutput(this.mSurfaceId, this.mCurrentMode)
158    if (await this.isVideoMode()) {
159      //      await this.mCameraService.createVideoOutput(this.functionBackImpl)
160    } else {
161      await this.mCameraService.createPhotoOutput(this.functionBackImpl)
162    }
163    await this.mCameraService.createSession(this.mSurfaceId, await this.isVideoMode())
164    if ([...this.mSessionList].pop() === 'RELEASE') {
165      await this.close()
166    }
167    if (data && data?.zoomRatio && data.zoomRatio !== 1) {
168      await this.mCameraService.setZoomRatio(data.zoomRatio)
169    }
170    this.mSessionList = []
171    this.enableUi()
172    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_PREVIEW_FINISHED);
173    this.mWorkerManager.postMessage(Action.updateCameraStatus())
174    Log.end(`${this.TAG} reStartPreview`)
175  }
176
177  private async changeMode(data) {
178    Log.start(`${this.TAG} changeMode`)
179    ReportUtil.write(ReportUtil.SWITCH_MODE)
180    this.mCurrentMode = data.mode
181    this.mCameraId = this.mCameraId.split('_').pop() as string;
182    Log.info(`${this.TAG} this.mCurrentMode = ${this.mCurrentMode}`)
183    await this.mCameraService.releaseCamera()
184    await this.mCameraService.createCameraInput(this.mCameraId, 'modeChange')
185    await this.mCameraService.createPreviewOutput(this.mSurfaceId, this.mCurrentMode)
186    if (await this.isVideoMode()) {
187      //      await this.mCameraService.createVideoOutput(this.functionBackImpl)
188    } else {
189      await this.mCameraService.createPhotoOutput(this.functionBackImpl)
190    }
191    await this.mCameraService.createSession(this.mSurfaceId, await this.isVideoMode())
192    this.mWorkerManager.postMessage(Action.onModeChanged(this.mCurrentMode))
193    this.mWorkerManager.postMessage(Action.swipeModeChangeDone(false))
194    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_PREVIEW_FINISHED);
195    this.mWorkerManager.postMessage(Action.updateCameraStatus())
196    this.enableUi()
197    Log.end(`${this.TAG} changeMode`)
198  }
199
200  private async switchCamera(data) {
201    Log.start(`${this.TAG} switchCamera`)
202    ReportUtil.write(ReportUtil.SWITCH_CAMERA)
203    this.mCameraId = data.cameraId
204    this.mCameraService.setCameraId(this.mCameraId)
205    await this.mCameraService.releaseCamera()
206    await this.mCameraService.createCameraInput(this.mCameraId)
207    if (data?.curMode && data.curMode !== undefined && data.curMode !== this.mCurrentMode) {
208      this.mCurrentMode = data.curMode
209    }
210    await this.mCameraService.createPreviewOutput(this.mSurfaceId, this.mCurrentMode)
211    if (await this.isVideoMode()) {
212      //      await this.mCameraService.createVideoOutput(this.functionBackImpl)
213    } else {
214      await this.mCameraService.createPhotoOutput(this.functionBackImpl)
215    }
216    await this.mCameraService.createSession(this.mSurfaceId, await this.isVideoMode())
217    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_PREVIEW_FINISHED);
218    this.mWorkerManager.postMessage(Action.updateCameraStatus())
219    if (new Date().getTime() - GlobalContext.get().getT<number>('switchCameraTime') > 2000) {
220      ReportUtil.write(ReportUtil.SWITCH_TIMEOUT)
221    }
222    this.enableUi()
223    Log.end(`${this.TAG} switchCamera`)
224  }
225
226  private async close() {
227    Log.start(`${this.TAG} close`)
228    this.mSessionList.push('RELEASE')
229    if (GlobalContext.get().getT<boolean>('isSessionCreating') || this.isSessionReleasing) {
230      Log.info(`${this.TAG} isSessionCreating or isSessionReleasing return`)
231      return
232    }
233    this.isSessionReleasing = true
234    await this.mCameraService.releaseCamera()
235    GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_RELEASE_FINISHED);
236    this.mWorkerManager.postMessage(Action.updateCameraStatus())
237    this.startIdentification = false
238    this.isSessionReleasing = false
239    if ([...this.mSessionList].pop() === 'CREATE') {
240      await this.initCamera(this.initDataCache)
241      GlobalContext.get().setObject('cameraStatus', CameraStatus.CAMERA_INIT_FINISHED);
242      this.mWorkerManager.postMessage(Action.updateCameraStatus())
243    }
244    this.mSessionList = []
245    Log.end(`${this.TAG} close`)
246  }
247
248  private async isVideoMode(): Promise<boolean> {
249    Log.info(`${this.TAG} isVideoMode ${this.mCurrentMode} ${this.mCurrentMode === 'VIDEO'}`)
250    return this.mCurrentMode === 'VIDEO'
251  }
252
253  private async reloadThumbnail(data) {
254    Log.info(`${this.TAG} loadThumbnail E`)
255    this.mCameraService.getThumbnail(this.functionBackImpl)
256    Log.info(`${this.TAG} loadThumbnail X`)
257  }
258
259  static getInstance() {
260    if (!globalThis?.cameraBasicMethod) {
261      globalThis.cameraBasicMethod = new CameraBasicFunction()
262    }
263    return globalThis.cameraBasicMethod
264  }
265
266  load(): void {
267    Log.info(`${this.TAG} load E`)
268    this.mEventBus.on(Action.ACTION_INIT, this.initCamera.bind(this))
269    this.mEventBus.on(Action.ACTION_CHANGE_IMAGE_SIZE, this.imageSize.bind(this))
270    this.mEventBus.on(Action.ACTION_CHANGE_VIDEO_SIZE, this.videoSize.bind(this))
271    this.mEventBus.on(Action.ACTION_PREPARE_SURFACE, this.onSurfacePrepare.bind(this))
272    this.mEventBus.on(Action.ACTION_START_PREVIEW, this.startPreview.bind(this))
273    this.mEventBus.on(Action.ACTION_RESTART_PREVIEW, this.reStartPreview.bind(this))
274    this.mEventBus.on(Action.ACTION_CHANGE_MODE, this.changeMode.bind(this))
275    this.mEventBus.on(Action.ACTION_SWITCH_CAMERA, this.switchCamera.bind(this))
276    this.mEventBus.on(Action.ACTION_CLOSE_CAMERA, this.close.bind(this))
277    this.mEventBus.on(Action.ACTION_RELOAD_THUMBNAIL, this.reloadThumbnail.bind(this))
278    Log.info(`${this.TAG} load X`)
279  }
280
281  unload(): void {
282    Log.info(`${this.TAG} unload E`)
283    this.mEventBus.off(Action.ACTION_INIT, this.initCamera.bind(this))
284    this.mEventBus.off(Action.ACTION_CHANGE_IMAGE_SIZE, this.imageSize.bind(this))
285    this.mEventBus.off(Action.ACTION_CHANGE_VIDEO_SIZE, this.videoSize.bind(this))
286    this.mEventBus.off(Action.ACTION_PREPARE_SURFACE, this.onSurfacePrepare.bind(this))
287    this.mEventBus.off(Action.ACTION_START_PREVIEW, this.startPreview.bind(this))
288    this.mEventBus.off(Action.ACTION_RESTART_PREVIEW, this.reStartPreview.bind(this))
289    this.mEventBus.off(Action.ACTION_CHANGE_MODE, this.changeMode.bind(this))
290    this.mEventBus.off(Action.ACTION_SWITCH_CAMERA, this.switchCamera.bind(this))
291    this.mEventBus.off(Action.ACTION_CLOSE_CAMERA, this.close.bind(this))
292    this.mEventBus.off(Action.ACTION_RELOAD_THUMBNAIL, this.reloadThumbnail.bind(this))
293    Log.info(`${this.TAG} unload X`)
294  }
295}