1/* 2 * Copyright (c) 2022 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 getStore from '../../redux/store' 23import { SettingManager } from '../../setting/SettingManager' 24import Timer from '../../setting/settingitem/Timer' 25import ComponentPosition from '../../utils/ComponentPosition' 26 27let localState = (state) => { 28 return { 29 uiEnable: state.ContextReducer.uiEnable, 30 shutterIcon: state.CameraReducer.shutterIcon, 31 videoState: state.RecordReducer.videoState, 32 mode: state.ModeReducer.mode, 33 videoUri: state.CameraInitReducer.videoUri, 34 resourceUri: state.CameraInitReducer.resourceUri, 35 isThirdPartyCall: state.ContextReducer.isThirdPartyCall, 36 xComponentWidth: state.PreviewReducer.xComponentWidth, 37 xComponentHeight: state.PreviewReducer.xComponentHeight, 38 } 39} 40 41let localDispatcher = (dispatch) => { 42 return { 43 updateSmallVideoTimerVisible: (visible: boolean) => { 44 dispatch(Action.updateSmallVideoTimerVisible(visible)) 45 }, 46// updateCaptureBtnScale: (scale: number) => { 47// dispatch(Action.updateCaptureBtnScale(scale)) 48// }, 49 updateShutterIcon: (icon: Resource) => { 50 dispatch(Action.updateShutterIcon(icon)) 51 }, 52 capture: () => { 53 dispatch(Action.updateShowFlashBlackFlag(true)) 54 dispatch(Action.capture()) 55 }, 56 startRecording: () => { 57 dispatch(Action.startRecording()) 58 dispatch(Action.updateVideoState('startTakeVideo')) 59 dispatch(Action.updateBigVideoTimerVisible(true)) 60 dispatch(Action.updateScreenStatus(true)) 61 }, 62 pauseRecording: () => { 63 dispatch(Action.pauseRecording()) 64 dispatch(Action.updateVideoState('pauseTakeVideo')) 65 }, 66 resumeRecording: () => { 67 dispatch(Action.resumeRecording()) 68 dispatch(Action.updateVideoState('startTakeVideo')) 69 }, 70 stopRecording: () => { 71 dispatch(Action.stopRecording()) 72 dispatch(Action.updateVideoState('beforeTakeVideo')) 73 dispatch(Action.updateBigVideoTimerVisible(false)) 74 dispatch(Action.updateSmallVideoTimerVisible(false)) 75 dispatch(Action.updateScreenStatus(false)) 76 }, 77 changeTimeLapse: (isShowtimeLapse: boolean) => { 78 dispatch(Action.changeTimeLapse(isShowtimeLapse)) 79 } 80 } 81} 82 83@Component 84export struct ShutterButtonLand { 85 private TAG: string = '[ShutterButtonLand]:' 86 private appEventBus: EventBus = EventBusManager.getInstance().getEventBus() 87 private settingManager = SettingManager.getInstance() 88 private lastTime = 0 89 @Link screenSize: any 90 91 type: ButtonType 92 stateEffect: boolean 93 94 @State state: any = {} 95 @State captureBtnScale: number = 1 96 97 aboutToAppear(): void { 98 Log.debug(`${this.TAG} aboutToAppear E`) 99 getStore().connect(localState, localDispatcher)(this.state) 100 this.appEventBus.on(Action.ACTION_CHANGE_MODE, this.changeShutterIcon.bind(this)) 101 this.appEventBus.on(Action.ACTION_UPDATE_THUMBNAIL, this.onThumbnailUpdate.bind(this)) 102 this.appEventBus.on(Action.ACTION_INIT_MODE, this.changeShutterIcon.bind(this)) 103 this.refreshIcon(this.state.mode) 104 Log.debug(`${this.TAG} aboutToAppear X`) 105 } 106 107 aboutToDisappear(): void { 108 Log.debug(`${this.TAG} aboutToDisappear E`) 109 this.appEventBus.off(Action.ACTION_CHANGE_MODE, this.changeShutterIcon.bind(this)) 110 this.appEventBus.off(Action.ACTION_UPDATE_THUMBNAIL, this.onThumbnailUpdate.bind(this)) 111 this.appEventBus.off(Action.ACTION_INIT_MODE, this.changeShutterIcon.bind(this)) 112 Log.debug(`${this.TAG} aboutToDisappear X`) 113 } 114 115 private async onThumbnailUpdate(data) { 116 Log.info(`${this.TAG} onThumbnailUpdate data: ${JSON.stringify(data)} E`) 117 Log.info(`${this.TAG} onThumbnailUpdate resourceUri= ${JSON.stringify(this.state.resourceUri)} E`) 118 Log.info(`${this.TAG} onThumbnailUpdate isThirdPartyCall= ${this.state.isThirdPartyCall} E`) 119 Log.info(`${this.TAG} onThumbnailUpdate videoUri= ${this.state.videoUri} E`) 120 if (this.state.isThirdPartyCall) { 121 Log.info(`${this.TAG} onThumbnailUpdate start router to ThirdPreviewView`) 122 router.push({ 123 uri: "pages/ThirdPreviewView", 124 params: { 125 width: this.state.xComponentWidth, 126 height: this.state.xComponentHeight, 127 mode: this.state.mode, 128 uri: this.state.resourceUri, 129 videoUri: this.state.videoUri, 130 callBundleName:globalThis.cameraAbilityWant?.parameters?.callBundleName 131 } 132 }) 133 } 134 Log.info(`${this.TAG} onThumbnailUpdate this.state.thumbnail: ${JSON.stringify(this.state.thumbnail)} X`) 135 } 136 137 private async changeShutterIcon(data) { 138 Log.debug(`${this.TAG} resetShutterIcon E`) 139 this.refreshIcon(data.mode) 140 Log.debug(`${this.TAG} resetShutterIcon X`) 141 } 142 143 private async refreshIcon(mode: string) { 144 Log.debug(`${this.TAG} refreshIcon E`) 145 if (mode === 'PHOTO') { 146 this.state.updateShutterIcon($r('app.media.ic_circled_filled')) 147 } else if (mode === 'VIDEO') { 148 this.state.updateShutterIcon($r('app.media.take_video_normal')) 149 } else { 150 this.state.updateShutterIcon($r('app.media.ic_circled_filled')) 151 } 152 Log.debug(`${this.TAG} refreshIcon X`) 153 } 154 155 build() { 156 if (this.state.videoState === 'beforeTakeVideo') { 157 Stack({alignContent: Alignment.Center}) { 158 if (this.state.mode === 'VIDEO'){ 159 Image(this.state.shutterIcon) 160 .width(76).aspectRatio(1).enabled(this.state.uiEnable) 161 .onTouch((event: TouchEvent) => { 162 if (event.type === TouchType.Up) { 163 let timerLapse = this.settingManager.getTimeLapse() 164 Log.log(`${this.TAG} startRecording getValue= ${JSON.stringify(timerLapse)}`) 165 if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) { 166 this.state.changeTimeLapse(true) 167 } else { 168 this.state.startRecording() 169 } 170 } 171 }) 172 } else { 173 Image($r("app.media.ic_circled")).fillColor(Color.White).width(76).aspectRatio(1) 174 Image(this.state.shutterIcon).width(54).aspectRatio(1).fillColor(Color.White) 175 .scale({ x: this.captureBtnScale, y: this.captureBtnScale, z: this.captureBtnScale }) 176 .enabled(this.state.uiEnable) 177 .onTouch((event: TouchEvent) => { 178 if (event.type === TouchType.Down) { 179 animateTo( 180 { duration: 125, curve: Curve.Sharp, delay: 0 }, 181 () => { this.captureBtnScale = 0.85 }) 182 } else if (event.type === TouchType.Up) { 183 animateTo( 184 { duration: 125, curve: Curve.Sharp, delay: 0, 185 onFinish: () => { this.captureBtnScale = 1 }}, 186 () => { this.captureBtnScale = 1 }) 187 let timerLapse = this.settingManager.getTimeLapse() 188 Log.log(`${this.TAG} startCapture getValue= ${JSON.stringify(timerLapse)}`) 189 if (timerLapse && timerLapse.id !== Timer.RESOURCE_OFF.id) { 190 this.state.changeTimeLapse(true) 191 } else { 192 let waitTime = 450 193 let curTime = Date.now(); 194 if (Math.abs(curTime - this.lastTime) >= waitTime) { 195 Log.log(`${this.TAG} throttle invoke time = ${JSON.stringify(curTime - this.lastTime)}`) 196 this.state.capture() 197 this.lastTime = curTime; 198 } 199 } 200 } 201 }) 202 } 203 }.width(76).aspectRatio(1).margin({ 204 top: ComponentPosition.getShutterButtonMargin(this.screenSize.width, this.screenSize.height, this.state.xComponentHeight), 205 bottom: ComponentPosition.getShutterButtonMargin(this.screenSize.width, this.screenSize.height, this.state.xComponentHeight) }) 206 } else { 207 Column() { 208 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 209 if (this.state.videoState === 'startTakeVideo') { 210 Image($r('app.media.ic_video_recording')) 211 .width(20).aspectRatio(1).fillColor(Color.White) 212 .margin({ bottom: 16 }) 213 .enabled(this.state.uiEnable) 214 .onClick(() => { 215 this.state.pauseRecording() 216 }) 217 } else if (this.state.videoState === 'pauseTakeVideo') { 218 Image($r('app.media.ic_video_pause')).width(20).aspectRatio(1).fillColor(Color.Red) 219 .margin({ bottom: 16 }) 220 .enabled(this.state.uiEnable) 221 .onClick(() => { 222 this.state.resumeRecording() 223 }) 224 } 225 Image($r('app.media.ic_video_end')) 226 .width(20).aspectRatio(1).fillColor(Color.White) 227 .margin({ top: 16 }) 228 .enabled(this.state.uiEnable) 229 .onClick(() => { 230 this.state.stopRecording() 231 }) 232 } 233 } 234 .width(56) 235 .height(120) 236 .borderRadius(28) 237 .border({ width: 1, color: 0xffffff, style: BorderStyle.Solid }) 238 .margin({ top: 26, bottom: 26 }) 239 } 240 } 241}