• 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, getStore, OhCombinedState } from '../../redux/store';
20import { Action } from '../../redux/actions/Action';
21import { EventBus } from '../../worker/eventbus/EventBus';
22import { EventBusManager } from '../../worker/eventbus/EventBusManager';
23import { SettingManager } from '../../setting/SettingManager';
24import Timer from '../../setting/settingitem/Timer';
25import { GlobalContext } from '../../utils/GlobalContext';
26import { ComponentIdKeys } from '../../utils/ComponentIdKeys';
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  public setDispatch(dispatch: Dispatch) {
45    this.mDispatch = dispatch;
46  }
47
48  public updateSmallVideoTimerVisible(visible: boolean): void {
49    this.mDispatch(Action.updateSmallVideoTimerVisible(visible));
50  }
51
52  public updateShutterIcon(icon: Resource): void {
53    this.mDispatch(Action.updateShutterIcon(icon));
54  }
55
56  public capture(): void {
57    this.mDispatch(Action.updateShowFlashBlackFlag(true));
58    this.mDispatch(Action.capture());
59  }
60
61  public startRecording(): void {
62    this.mDispatch(Action.startRecording());
63    this.mDispatch(Action.updateVideoState('startTakeVideo'));
64    this.mDispatch(Action.updateBigVideoTimerVisible(true));
65    this.mDispatch(Action.updateScreenStatus(true));
66  }
67
68  public pauseRecording(): void {
69    this.mDispatch(Action.pauseRecording());
70    this.mDispatch(Action.updateVideoState('pauseTakeVideo'));
71  }
72
73  public resumeRecording(): void {
74    this.mDispatch(Action.resumeRecording());
75    this.mDispatch(Action.updateVideoState('startTakeVideo'));
76  }
77
78  public stopRecording(): void {
79    this.mDispatch(Action.stopRecording());
80    this.mDispatch(Action.updateVideoState('beforeTakeVideo'));
81    this.mDispatch(Action.updateBigVideoTimerVisible(false));
82    this.mDispatch(Action.updateSmallVideoTimerVisible(false));
83    this.mDispatch(Action.updateScreenStatus(false));
84  }
85
86  public changeTimeLapse(isShowtimeLapse: boolean): void {
87    this.mDispatch(Action.changeTimeLapse(isShowtimeLapse));
88  }
89
90  private mDispatch: Dispatch = (data) => data;
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  type: ButtonType = ButtonType.Capsule;
105  stateEffect: boolean = false;
106  @State state: StateStruct = new StateStruct();
107  @State captureBtnScale: number = 1;
108  private TAG: string = '[ShutterButton]:';
109  private appEventBus: EventBus = EventBusManager.getInstance().getEventBus();
110  private settingManager = SettingManager.getInstance();
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.info(`${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.info(`${this.TAG} aboutToDisappear X`);
145  }
146
147  build() {
148    if (this.state.videoState === 'beforeTakeVideo') {
149      Stack({ alignContent: Alignment.Center }) {
150        if (this.state.mode === 'VIDEO') {
151          Image(this.state.shutterIcon)
152            .key(ComponentIdKeys.SHUTTER_VIDEO_1)
153            .width(76)
154            .aspectRatio(1)
155            .enabled(this.state.uiEnable)
156            .onTouch((event?: TouchEvent) => {
157              if (!event) {
158                return;
159              }
160              if (event.type === TouchType.Up) {
161                let timerLapse = this.settingManager.getTimeLapse()
162                Log.log(`${this.TAG} ShutterButton startRecording getValue= ${JSON.stringify(timerLapse)}`)
163                if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) {
164                  Log.log('ShutterButton startRecording changeTimeLapse called')
165                  this.mAction.changeTimeLapse(true)
166                } else {
167                  Log.log('ShutterButton startRecording changeTimeLapse not called')
168                  this.mAction.startRecording()
169                }
170              }
171            })
172        } else {
173          Image($r('app.media.ic_circled')).fillColor(Color.White)
174          Image(this.state.shutterIcon)
175            .width(54)
176            .aspectRatio(1)
177            .fillColor(Color.White)
178            .key(ComponentIdKeys.SHUTTER_PHOTO_1)
179            .scale({ x: this.captureBtnScale, y: this.captureBtnScale, z: this.captureBtnScale })
180            .enabled(this.state.uiEnable)
181            .onTouch((event?: TouchEvent) => {
182              if (!event) {
183                return;
184              }
185              if (event.type === TouchType.Down) {
186                animateTo(
187                  { duration: 125, curve: Curve.Sharp, delay: 0 },
188                  () => {
189                    this.captureBtnScale = 0.85
190                  })
191              } else if (event.type === TouchType.Up) {
192                animateTo(
193                  {
194                    duration: 125,
195                    curve: Curve.Sharp,
196                    delay: 0,
197                    onFinish: () => {
198                      this.captureBtnScale = 1
199                    }
200                  },
201                  () => {
202                    this.captureBtnScale = 1
203                  })
204                let timerLapse = this.settingManager.getTimeLapse()
205                Log.log(`${this.TAG} ShutterButton start capture getValue= ${JSON.stringify(timerLapse)}`)
206                if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) {
207                  Log.log('ShutterButton startRecording changeTimeLapse called')
208                  this.mAction.changeTimeLapse(true)
209                } else {
210                  Log.log('ShutterButton capture changeTimeLapse not called')
211                  this.mAction.capture()
212                }
213              }
214            })
215        }
216      }.width(76).aspectRatio(1).margin({ left: 48, right: 48 })
217    } else {
218      Column() {
219        Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
220          Column() {
221            Image($r('app.media.ic_video_end'))
222              .key(ComponentIdKeys.SHUTTER_VIDEO_END_1)
223              .width(20)
224              .aspectRatio(1)
225              .fillColor(Color.White)
226              .enabled(this.state.uiEnable)
227          }
228          .width(40)
229          .padding({ left: 10, right: 10 })
230          .margin({ right: 6 })
231          .enabled(this.state.uiEnable)
232          .onClick(() => {
233            this.mAction.stopRecording()
234          })
235
236          Column() {
237            if (this.state.videoState === 'startTakeVideo') {
238              Image($r('app.media.ic_video_recording'))
239                .width(20).aspectRatio(1).fillColor(Color.White)
240                .enabled(this.state.uiEnable)
241            } else if (this.state.videoState === 'pauseTakeVideo') {
242              Image($r('app.media.ic_video_pause')).width(20).aspectRatio(1).fillColor(Color.Red)
243                .enabled(this.state.uiEnable)
244            }
245          }
246          .width(40)
247          .padding({ left: 10, right: 10 })
248          .margin({ left: 6 })
249          .enabled(this.state.uiEnable)
250          .onClick(() => {
251            this.state.videoState === 'startTakeVideo' ? this.mAction.pauseRecording() : this.mAction.resumeRecording()
252          })
253        }.width('100%').height('100%')
254      }
255      .width(120)
256      .height(56)
257      .borderRadius(28)
258      .border({ width: 1, color: Color.White, style: BorderStyle.Solid })
259      .margin({ left: 24, right: 24 })
260    }
261  }
262
263  private async onThumbnailUpdate(data: UpdateThumbnailStruct): Promise<void> {
264    Log.info(`${this.TAG} onThumbnailUpdate data: ${JSON.stringify(data)} E`)
265    Log.info(`${this.TAG} onThumbnailUpdate resourceUri= ${JSON.stringify(this.state.resourceUri)} E`)
266    Log.info(`${this.TAG} onThumbnailUpdate isThirdPartyCall= ${this.state.isThirdPartyCall} E`)
267    Log.info(`${this.TAG} onThumbnailUpdate videoUri= ${this.state.videoUri} E`)
268    if (this.state.isThirdPartyCall) {
269      Log.info(`${this.TAG} onThumbnailUpdate start router to ThirdPreviewView`)
270      router.push({
271        uri: "pages/ThirdPreviewView",
272        params: {
273          width: this.state.xComponentWidth,
274          height: this.state.xComponentHeight,
275          mode: this.state.mode,
276          uri: this.state.resourceUri,
277          videoUri: this.state.videoUri,
278          callBundleName: this.getCallBundleName(),
279        }
280      })
281    }
282    Log.info(`${this.TAG} onThumbnailUpdate this.state.thumbnail: ${JSON.stringify(this.state.thumbnail)} X`)
283  }
284
285  private getCallBundleName(): string {
286    let parameters = GlobalContext.get().getCameraAbilityWant().parameters;
287    if (!parameters) {
288      return '';
289    }
290    return parameters?.callBundleName as string;
291  }
292
293  private async changeShutterIcon(data: ModeStruct): Promise<void> {
294    Log.info(`${this.TAG} resetShutterIcon E`);
295    this.refreshIcon(data.mode)
296    Log.info(`${this.TAG} resetShutterIcon X`);
297  }
298
299  private async refreshIcon(mode: string): Promise<void> {
300    Log.info(`${this.TAG} refreshIcon E`);
301    if (mode === 'PHOTO') {
302      this.mAction.updateShutterIcon($r('app.media.ic_circled_filled'))
303    } else if (mode === 'VIDEO') {
304      this.mAction.updateShutterIcon($r('app.media.take_video_normal'))
305    } else {
306      this.mAction.updateShutterIcon($r('app.media.ic_circled_filled'))
307    }
308    Log.info(`${this.TAG} refreshIcon X`);
309  }
310}