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