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