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