• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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'
17
18import { Action } from '../../redux/actions/Action'
19import { Log } from '../../utils/Log'
20import { EventBus } from '../../worker/eventbus/EventBus'
21import { EventBusManager } from '../../worker/eventbus/EventBusManager'
22import { OhCombinedState } from '../../redux/store'
23import { getStore } from '../../redux/store'
24import { SettingManager } from '../../setting/SettingManager'
25import Timer from '../../setting/settingitem/Timer'
26import { ComponentPosition } from '../../utils/ComponentPosition'
27import { GlobalContext } from '../../utils/GlobalContext'
28import { Dispatch } from '../../redux/core/redux/types/store'
29import image from '@ohos.multimedia.image'
30
31class StateStruct {
32  uiEnable: boolean = true;
33  shutterIcon: Resource = $r('app.media.ic_circled_filled');
34  captureBtnScale: number = 0;
35  videoState: string = 'beforeTakeVideo';
36  mode: string = '';
37  resourceUri: string = '';
38  videoUri: string = ''
39  thumbnail: Resource = $r('app.media.ic_camera_thumbnail_default_white');
40  isThirdPartyCall: boolean = false;
41  xComponentWidth: number = 0;
42  xComponentHeight: number = 0;
43}
44
45class ShutterButtonDispatcher {
46  private mDispatch: Dispatch = (data) => data;
47
48  public setDispatch(dispatch: Dispatch) {
49    this.mDispatch = dispatch;
50  }
51
52  public updateSmallVideoTimerVisible(visible: boolean): void {
53    this.mDispatch(Action.updateSmallVideoTimerVisible(visible));
54  }
55
56  public updateShutterIcon(icon: Resource): void {
57    this.mDispatch(Action.updateShutterIcon(icon));
58  }
59
60  public capture(): void {
61    this.mDispatch(Action.updateShowFlashBlackFlag(true));
62    this.mDispatch(Action.capture());
63  }
64
65  public startRecording(): void {
66    this.mDispatch(Action.startRecording());
67    this.mDispatch(Action.updateVideoState('startTakeVideo'));
68    this.mDispatch(Action.updateBigVideoTimerVisible(true));
69    this.mDispatch(Action.updateScreenStatus(true));
70  }
71
72  public pauseRecording(): void {
73    this.mDispatch(Action.pauseRecording());
74    this.mDispatch(Action.updateVideoState('pauseTakeVideo'));
75  }
76
77  public resumeRecording(): void {
78    this.mDispatch(Action.resumeRecording());
79    this.mDispatch(Action.updateVideoState('startTakeVideo'));
80  }
81
82  public stopRecording(): void {
83    this.mDispatch(Action.stopRecording());
84    this.mDispatch(Action.updateVideoState('beforeTakeVideo'));
85    this.mDispatch(Action.updateBigVideoTimerVisible(false));
86    this.mDispatch(Action.updateSmallVideoTimerVisible(false));
87    this.mDispatch(Action.updateScreenStatus(false));
88  }
89
90  public changeTimeLapse(isShowtimeLapse: boolean): void {
91    this.mDispatch(Action.changeTimeLapse(isShowtimeLapse));
92  }
93}
94
95class ScreenSizeType {
96  width: number = 0;
97  height: number = 0;
98}
99
100class ModeStruct {
101  mode: string = '';
102}
103
104class UpdateThumbnailStruct {
105  thumbnail: image.PixelMap | undefined = undefined;
106  resourceUri: string = '';
107}
108
109@Component
110export struct ShutterButtonLand {
111  private TAG: string = '[ShutterButtonLand]:';
112  private appEventBus: EventBus = EventBusManager.getInstance().getEventBus();
113  private settingManager = SettingManager.getInstance();
114  private lastTime = 0;
115  @Link screenSize: ScreenSizeType;
116  type: ButtonType = ButtonType.Capsule;
117  stateEffect: boolean = false;
118  @State state: StateStruct = new StateStruct();
119  @State captureBtnScale: number = 1;
120  private mAction: ShutterButtonDispatcher = new ShutterButtonDispatcher();
121
122  aboutToAppear(): void {
123    Log.debug(`${this.TAG} aboutToAppear E`)
124    getStore().subscribe((state: OhCombinedState) => {
125      this.state = {
126        uiEnable: state.ContextReducer.uiEnable,
127        shutterIcon: state.CameraReducer.shutterIcon,
128        captureBtnScale: state.CaptureReducer.captureBtnScale,
129        videoState: state.RecordReducer.videoState,
130        mode: state.ModeReducer.mode,
131        resourceUri: state.CameraInitReducer.resourceUri,
132        videoUri: state.CameraInitReducer.videoUri,
133        thumbnail: state.CameraInitReducer.thumbnail,
134        isThirdPartyCall: state.ContextReducer.isThirdPartyCall,
135        xComponentWidth: state.PreviewReducer.xComponentWidth,
136        xComponentHeight: state.PreviewReducer.xComponentHeight
137      };
138    }, (dispatch: Dispatch) => {
139      this.mAction.setDispatch(dispatch);
140    });
141    this.appEventBus.on(Action.ACTION_CHANGE_MODE, (data: ModeStruct) => this.changeShutterIcon(data));
142    this.appEventBus.on(Action.ACTION_UPDATE_THUMBNAIL, (data: UpdateThumbnailStruct) => this.onThumbnailUpdate(data));
143    this.appEventBus.on(Action.ACTION_INIT_MODE, (data: ModeStruct) => this.changeShutterIcon(data));
144    this.refreshIcon(this.state.mode)
145    Log.info(`${this.TAG} aboutToAppear X`)
146  }
147
148  aboutToDisappear(): void {
149    Log.debug(`${this.TAG} aboutToDisappear E`)
150    this.appEventBus.off(Action.ACTION_CHANGE_MODE, (data: ModeStruct) => this.changeShutterIcon(data))
151    this.appEventBus.off(Action.ACTION_UPDATE_THUMBNAIL, (data: UpdateThumbnailStruct) => this.onThumbnailUpdate(data))
152    this.appEventBus.off(Action.ACTION_INIT_MODE, (data: ModeStruct) => this.changeShutterIcon(data));
153    Log.debug(`${this.TAG} aboutToDisappear X`)
154  }
155
156  private async onThumbnailUpdate(data: UpdateThumbnailStruct): Promise<void> {
157    Log.info(`${this.TAG} onThumbnailUpdate data: ${JSON.stringify(data)} E`)
158    Log.debug(`${this.TAG} onThumbnailUpdate resourceUri= ${JSON.stringify(this.state.resourceUri)} E`)
159    Log.info(`${this.TAG} onThumbnailUpdate isThirdPartyCall= ${this.state.isThirdPartyCall} E`)
160    Log.debug(`${this.TAG} onThumbnailUpdate videoUri= ${this.state.videoUri} E`)
161    if (this.state.isThirdPartyCall) {
162      Log.info(`${this.TAG} onThumbnailUpdate start router to ThirdPreviewView`)
163      router.push({
164        uri: "pages/ThirdPreviewView",
165        params: {
166          width: this.state.xComponentWidth,
167          height: this.state.xComponentHeight,
168          mode: this.state.mode,
169          uri: this.state.resourceUri,
170          videoUri: this.state.videoUri,
171          callBundleName: GlobalContext.get().getCameraAbilityWant()?.parameters?.callBundleName
172        }
173      })
174    }
175    Log.info(`${this.TAG} onThumbnailUpdate this.state.thumbnail: ${JSON.stringify(this.state.thumbnail)} X`)
176  }
177
178  private async changeShutterIcon(data: ModeStruct): Promise<void> {
179    Log.debug(`${this.TAG} resetShutterIcon E`)
180    this.refreshIcon(data.mode)
181    Log.debug(`${this.TAG} resetShutterIcon X`)
182  }
183
184  private async refreshIcon(mode: string): Promise<void> {
185    Log.debug(`${this.TAG} refreshIcon E`)
186    if (mode === 'PHOTO') {
187      this.mAction.updateShutterIcon($r('app.media.ic_circled_filled'))
188    } else if (mode === 'VIDEO') {
189      this.mAction.updateShutterIcon($r('app.media.take_video_normal'))
190    } else {
191      this.mAction.updateShutterIcon($r('app.media.ic_circled_filled'))
192    }
193    Log.debug(`${this.TAG} refreshIcon X`)
194  }
195
196  build() {
197    if (this.state.videoState === 'beforeTakeVideo') {
198      Stack({ alignContent: Alignment.Center }) {
199        if (this.state.mode === 'VIDEO') {
200          Image(this.state.shutterIcon)
201            .width(76).aspectRatio(1).enabled(this.state.uiEnable)
202            .onTouch((event: TouchEvent) => {
203              if (event.type === TouchType.Up) {
204                let timerLapse = this.settingManager.getTimeLapse()
205                Log.log(`${this.TAG} startRecording getValue= ${JSON.stringify(timerLapse)}`)
206                if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) {
207                  this.mAction.changeTimeLapse(true)
208                } else {
209                  this.mAction.startRecording()
210                }
211              }
212            })
213        } else {
214          Image($r("app.media.ic_circled")).fillColor(Color.White).width(76).aspectRatio(1)
215          Image(this.state.shutterIcon)
216            .width(54)
217            .aspectRatio(1)
218            .fillColor(Color.White)
219            .scale({ x: this.captureBtnScale, y: this.captureBtnScale, z: this.captureBtnScale })
220            .enabled(this.state.uiEnable)
221            .onTouch((event: TouchEvent) => {
222              if (event.type === TouchType.Down) {
223                animateTo(
224                  { duration: 125, curve: Curve.Sharp, delay: 0 },
225                  () => {
226                    this.captureBtnScale = 0.85
227                  })
228              } else if (event.type === TouchType.Up) {
229                animateTo(
230                  { duration: 125, curve: Curve.Sharp, delay: 0,
231                    onFinish: () => {
232                      this.captureBtnScale = 1
233                    } },
234                  () => {
235                    this.captureBtnScale = 1
236                  })
237                let timerLapse = this.settingManager.getTimeLapse()
238                Log.log(`${this.TAG} startCapture getValue= ${JSON.stringify(timerLapse)}`)
239                if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) {
240                  this.mAction.changeTimeLapse(true)
241                } else {
242                  let waitTime = 450
243                  let curTime = Date.now();
244                  if (Math.abs(curTime - this.lastTime) >= waitTime) {
245                    Log.log(`${this.TAG} throttle invoke time = ${JSON.stringify(curTime - this.lastTime)}`)
246                    this.mAction.capture()
247                    this.lastTime = curTime;
248                  }
249                }
250              }
251            })
252        }
253      }.width(76).aspectRatio(1).margin({
254        top: ComponentPosition.getShutterButtonMargin(this.screenSize.width, this.screenSize.height, this.state.xComponentHeight),
255        bottom: ComponentPosition.getShutterButtonMargin(this.screenSize.width, this.screenSize.height, this.state.xComponentHeight)
256      })
257    } else {
258      Column() {
259        Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
260          if (this.state.videoState === 'startTakeVideo') {
261            Image($r('app.media.ic_video_recording'))
262              .width(20)
263              .aspectRatio(1)
264              .fillColor(Color.White)
265              .margin({ bottom: 16 })
266              .enabled(this.state.uiEnable)
267              .onClick(() => {
268                this.mAction.pauseRecording()
269              })
270          } else if (this.state.videoState === 'pauseTakeVideo') {
271            Image($r('app.media.ic_video_pause'))
272              .width(20)
273              .aspectRatio(1)
274              .fillColor(Color.Red)
275              .margin({ bottom: 16 })
276              .enabled(this.state.uiEnable)
277              .onClick(() => {
278                this.mAction.resumeRecording()
279              })
280          }
281          Image($r('app.media.ic_video_end'))
282            .width(20)
283            .aspectRatio(1)
284            .fillColor(Color.White)
285            .margin({ top: 16 })
286            .enabled(this.state.uiEnable)
287            .onClick(() => {
288              this.mAction.stopRecording()
289            })
290        }
291      }
292      .width(56)
293      .height(120)
294      .borderRadius(28)
295      .border({ width: 1, color: 0xffffff, style: BorderStyle.Solid })
296      .margin({ top: 26, bottom: 26 })
297    }
298  }
299}