/*
* Copyright (c) Huawei Device Co., Ltd. 2024-2025. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import camera from '@ohos.multimedia.camera';
import window from '@ohos.window';
import lazy { FunctionId } from '@ohos/common/src/main/ets/function/core/functionproperty/FunctionId';
import lazy { ModeType } from '@ohos/common/src/main/ets/mode/ModeType';
import lazy { reduxSubscribe, OhCombinedState, Dispatch, getStates } from '@ohos/common/src/main/ets/redux';
import lazy { Unsubscribe } from '@ohos/common/src/main/ets/redux/Store';
import lazy { PositionType } from '@ohos/common/src/main/ets/utils/types';
import lazy {
FocusExposureAreaDispatcher
} from '@ohos/common/src/main/ets/component/focusExposure/FocusExposureAreaDispatcher';
import lazy { HiLog } from '@ohos/common/src/main/ets/utils/HiLog';
import lazy { ZIndex } from '@ohos/common/src/main/ets/utils/ZIndex';
import lazy { EventBus } from '@ohos/common/src/main/ets/worker/eventbus/EventBus';
import lazy { EventBusManager } from '@ohos/common/src/main/ets/worker/eventbus/EventBusManager';
import lazy { BaseComponent } from '@ohos/common/src/main/ets/worker/BaseComponent';
import lazy { FocusExposureActionType } from '@ohos/common/src/main/ets/redux/actions/FocusExposureActionType';
import lazy { VibratorService } from '@ohos/common/src/main/ets/component/vibration/VibratorService';
import lazy { TipService } from '@ohos/common/src/main/ets/component/tip/TipService';
import lazy { FocusArea } from './FocusArea';
import lazy { CameraActionType } from '@ohos/common/src/main/ets/redux/actions/CameraActionType';
import lazy { ActionType } from '@ohos/common/src/main/ets/redux/actions/ActionType';
import lazy { ContextActionType } from '@ohos/common/src/main/ets/redux/actions/ContextActionType';
import lazy { UIOperationType } from '@ohos/common/src/main/ets/component/uicomponent/UIOperationType';
import lazy { FocusExposureService } from '@ohos/common/src/main/ets/component/focusExposure/FocusExposureService';
import lazy {
EventEffectiveInfo,
LockLevel,
FocusData,
ExposureData,
TipInfo
} from '@ohos/common/src/main/ets/component/focusExposure/FocusExposureHelper';
import lazy { FocusLock } from '@ohos/common/src/main/ets/component/focusExposure/FocusLock';
import lazy { RenderLocation } from '@ohos/common/src/main/ets/function/core/functionproperty/RenderLocation';
import lazy { RecordActionType } from '@ohos/common/src/main/ets/redux/actions/RecordActionType';
import lazy { RecordingState } from '@ohos/common/src/main/ets/function/recordcontrol/RecordAction';
import lazy { ThumbnailActionType } from '@ohos/common/src/main/ets/redux/actions/ThumbnailActionType';
import lazy { CommonConstants } from '@ohos/common/src/main/ets/statistics/CommonConstants';
import ResGetter from '@ohos/common/src/main/ets/utils/ResGetter';
import lazy { FeatureManager } from '@ohos/common/src/main/ets/function/core/FeatureManager';
import dataObserver from '@ohos/common/src/main/ets/utils/dataObserver';
import lazy { WindowEventData } from '@ohos/common/src/main/ets/utils/types';
import lazy { WindowActionType } from '@ohos/common/src/main/ets/redux/actions/WindowActionType';
import lazy { Event } from '@ohos/common/src/main/ets/component/focusExposure/FocusExposureHelper';
import lazy {
CollaborateControlActionType
} from '@ohos/common/src/main/ets/redux/actions/CollaborateControlActionType';
import lazy { WindowDirection } from '@ohos/common/src/main/ets/utils/WindowDirection';
import lazy { simpleStringify } from '@ohos/common/src/main/ets/utils/SimpleStringify';
import lazy {
SettingFuncDialogItemIndex
} from '@ohos/common/src/main/ets/component/settingview/SettingFuncDialogItemIndex';
import { ExposureArea } from '../../commonfunc/exposure/ExposureArea';
import lazy { ExposureFunction } from '../../commonfunc/exposure/ExposureFunction';
const TAG: 'FocusExposureArea' = 'FocusExposureArea';
class EnterImmersiveStruct {
public isImmersive?: boolean = false;
public mainTrigCompo?: UIOperationType = UIOperationType.NULL;
public isShowtimeLapse?: boolean = false;
public isShowTimedShot?: boolean = false;
}
interface PhotoBrowserStatus {
photoBrowserStatus: boolean
};
interface FocusState { focusState: camera.FocusState };
const EXPOSE_FOCUS_DURING: number = 3000;
interface CollaborateCoord {
x: number;
y: number;
}
@Component
export struct FocusExposureArea {
@Builder
childBuilder() {
}
// 使用父组件@Builder装饰的方法初始化子组件@BuilderParam
@BuilderParam childBuilderParam: () => void = this.childBuilder;
@LocalStorageLink('previewAnimatePosition') previewAnimatePosition: PositionType = { x: 0, y: 0 };
@State xComponentWidth: number = 0;
@State xComponentHeight: number = 0;
@State mode: ModeType = ModeType.NONE;
@State exposurePosition: PositionType = { x: 0, y: 0 };
@State focusOpacity: number = 1;
@State exposureOpacity: number = 1;
@State focusPosition: PositionType = { x: 0, y: 0 };
@State @Watch('resetFocusState') isShowSemiCollapsed: boolean = false;
@State isShowExposureRing: boolean = false;
@State @Watch('handleFocusVisibility') isShowFocus: boolean = false;
@State isShowExposure: boolean = false;
@State focusLockPosition: PositionType = { x: 0, y: 0 };
@State panGestureAction: TouchType = TouchType.Up;
@State panGestureEventOffset: number = 0;
@State cameraPosition: camera.CameraPosition = camera.CameraPosition.CAMERA_POSITION_UNSPECIFIED;
@State @Watch('resetFocusState') isHalfCollapsLEM: boolean = false;
@State @Watch('handleLockLevelChange') lockLevel: LockLevel = LockLevel.UNLOCK;
@State isSupportedFocusStateDetect: boolean = false;
@State touchPreviewAgain: string = '';
@State @Watch('resetFocusExposure') superMacroEnable: boolean = false;
@State @Watch('handleMorePageShow') isShowMorePage: boolean = false;
@State @Watch('resetFocusExposure') windowStatus: window.WindowStatusType = window.WindowStatusType.FULL_SCREEN;
@State uiEnable: boolean = true;
@State @Watch('handleZoomChange') zoomRatio: number = 1;
@State @Watch('changeExposureViewPosition') rotateAngle: number = 0;
@StorageLink('isLemCollaps') isLemCollaps: boolean = false;
@Prop isHdr: boolean = false
@StorageLink('isCustomFilterCardTouched') isCustomFilterCardTouched: boolean = false;
private cinemaFocusTimer: number = Number.NaN;
@LocalStorageLink('cinemaTracking') cinemaTracking: boolean = false;
private mSubscriber: Unsubscribe = {
destroy: () => {
}
};
private mAction: FocusExposureAreaDispatcher = new FocusExposureAreaDispatcher();
private mEventBus: EventBus = EventBusManager.getInstance().getEventBus();
private mBase: BaseComponent = new BaseComponent();
private focusProgressTimerId: number = Number.MIN_VALUE;
private mFocusExposureService: FocusExposureService = FocusExposureService.getInstance();
private tabSelector: FunctionId = FunctionId.NONE;
private isVdeCollapsed: boolean = false;
private focusMode: camera.FocusMode = camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO;
@State private isEditMorePage: boolean = false;
private iconId: number = -1;
private longPressTip: Resource = $r('app.string.exposure_focus_locked');
private isShowExtendBar: boolean = false;
private isImmersive: boolean = false;
private mainTrigCompo: UIOperationType = UIOperationType.NULL;
private isShowtimeLapse: boolean = false;
private lockLevelBeforePan: LockLevel = LockLevel.UNLOCK;
private progressOpacity: number = 1;
private exposureMode: camera.ExposureMode = camera.ExposureMode.EXPOSURE_MODE_AUTO;
private recordState: RecordingState = RecordingState.READY;
private isShowTimedShot: boolean = false;
aboutToAppear(): void {
this.mSubscriber = reduxSubscribe((state: OhCombinedState) => {
this.xComponentWidth = state.get<number>('previewReducer', 'xComponentWidth');
this.xComponentHeight = state.get<number>('previewReducer', 'xComponentHeight');
this.mode = state.get<ModeType>('modeReducer', 'mode');
this.cameraPosition = state.get<number>('cameraReducer', 'cameraPosition');
this.tabSelector = state.get<FunctionId>('tabBarReducer', 'tabBarSelector');
this.isVdeCollapsed = state.get<boolean>('collapsReducer', 'isVdeCollapsed');
this.isHalfCollapsLEM = state.get<boolean>('collapsReducer', 'isHalfCollapsLEM');
this.isShowSemiCollapsed = state.get<boolean>('collapsReducer', 'isShowSemiCollapsed');
this.isSupportedFocusStateDetect = state.get<boolean>('focusExposureReducer', 'isSupportedFocusStateDetect');
this.focusMode = state.get<camera.FocusMode>('focusExposureReducer', 'focusMode');
this.isShowExposure = state.get<boolean>('focusExposureReducer', 'isShowExposure');
this.isShowExposureRing = state.get<boolean>('focusExposureReducer', 'isShowExposureRing');
this.isShowExtendBar = state.get<boolean>('uiReducer', 'isShowExtendBar');
this.isShowFocus = state.get<boolean>('focusExposureReducer', 'isShowFocus');
this.lockLevel = state.get<LockLevel>('focusExposureReducer', 'lockLevel');
this.iconId = state.get<number>('uiReducer', 'iconId');
this.superMacroEnable = state.get<boolean>('superMacroReducer', 'superMacroEnable');
this.isShowMorePage = state.get<boolean>('modeReducer', 'isShowMorePage');
this.isEditMorePage = state.get<boolean>('modeReducer', 'isEditMorePage');
this.uiEnable = state.get<boolean>('contextReducer', 'uiEnable');
this.zoomRatio = state.get<number>('zoomReducer', 'zoomRatio');
this.isImmersive = state.get<boolean>('uiReducer', 'isImmersive');
this.mainTrigCompo = state.get<UIOperationType>('uiReducer', 'mainTrigCompo');
this.isShowtimeLapse = state.get<boolean>('settingReducer', 'isShowtimeLapse');
this.exposureMode = state.get<camera.ExposureMode>('focusExposureReducer', 'exposureMode');
this.recordState = state.get<RecordingState>('recordReducer', 'recordingState');
this.rotateAngle = state.get<number>('contextReducer', 'rotateAngle');
this.windowStatus = state.get<number>('windowReducer', 'windowStatus');
this.isShowTimedShot = state.get<boolean>('settingReducer', 'isShowTimedShot');
}, (dispatch: Dispatch): void => {
this.mAction.setDispatch(dispatch);
});
this.mEventBus.on([CameraActionType.CHANGE_MODE, CameraActionType.CHANGE_OUTPUT_TYPE,
CameraActionType.RESTART, CameraActionType.SWITCH_CAMERA_CHANGE_MODE, CameraActionType.SWITCH_CAMERA,
ContextActionType.UPDATE_SAVE_POWER_MODE_STATE, ContextActionType.ABILITY_ON_BACKGROUND],
this.resetFocusState.bind(this), this.mBase.hashCode());
this.mEventBus.on(ThumbnailActionType.PHOTOBROWSER_STATUS, this.handlePhotoBrowserOpen.bind(this),
this.mBase.hashCode());
this.mEventBus.on([RecordActionType.STOPPED, FocusExposureActionType.ACTION_RESET_FOCUS_EXPOSURE,
WindowActionType.REFRESH_UX, ActionType.ACTION_SHOW_PICKER],
this.resetFocusExposure.bind(this), this.mBase.hashCode());
this.mEventBus.on([ActionType.ACTION_CHANGE_TIME_LAPSE, ActionType.ACTION_IS_ENTER_IMMERSIVE],
this.handleEnterImmersiveForFocus.bind(this), this.mBase.hashCode());
this.mEventBus.on(FocusExposureActionType.ACTION_UPDATE_FOCUS_STATE,
this.handleFocusStateChange, this.mBase.hashCode());
this.mEventBus.on(ContextActionType.CHANGE_WINDOW_EVENT_TYPE, this.onChangeWindowEvent.bind(this),
this.mBase.hashCode());
this.mEventBus.on(CollaborateControlActionType.FOCUS, this.collaborateFocusExposure.bind(this),
this.mBase.hashCode());
this.mEventBus.on([ActionType.ACTION_CHANGE_TIMED_SHOT, ActionType.ACTION_IS_ENTER_IMMERSIVE],
this.handleEnterImmersiveForFocus.bind(this), this.mBase.hashCode());
dataObserver(this, 'mainTrigCompo', this.handleMainTrigCompoChange);
}
aboutToDisappear(): void {
HiLog.i(TAG, 'previewArea aboutToDisappear called.');
this.mAction.hideTip();
this.mEventBus.clear(this.mBase.hashCode());
this.mSubscriber.destroy();
this.resetFocusState();
}
private resetFocusState(data?: Record<string, object>): void {
HiLog.i(TAG, `resetFocusState E ${JSON.stringify(data)}`);
this.clearFocusProgressTimer();
if (this.isShowFocus) {
this.mAction.hideFocus();
}
}
private resetFocusMode(): void {
HiLog.i(TAG, 'resetFocusMode E');
let focusMode: camera.FocusMode = camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO;
this.mFocusExposureService.setFocusValue({ focusMode: focusMode, isParamSetting: false });
}
private resetFocusExposureMode(): void {
HiLog.i(TAG, 'resetFocusExposureMode E');
if (!this.isShowFocus) {
return;
}
this.resetFocusMode();
this.mFocusExposureService.setExposureValue({
exposureMode: camera.ExposureMode.EXPOSURE_MODE_AUTO,
exposureValue: 0
});
}
private resetFocusExposure(): void {
HiLog.i(TAG, 'resetFocusExposure E');
if (this.panGestureAction !== TouchType.Up) {
this.panGestureAction = TouchType.Up;
}
this.mAction.uiEnabled();
this.resetFocusExposureMode();
this.resetFocusState();
}
private handlePhotoBrowserOpen(data: PhotoBrowserStatus): void {
HiLog.i(TAG, 'handlePhotoBrowserOpen E.');
if (!data.photoBrowserStatus) {
return;
}
this.resetFocusState();
}
private resetSmartControlLocked(): void {
if (getStates().get<LockLevel>('focusExposureReducer', 'smartControlLocked')) {
this.mAction.resetSmartControlLocked();
}
}
private onChangeWindowEvent(data: WindowEventData) {
HiLog.i(TAG, `onChangeWindowEvent windowStageEventType: ${data.windowStageEventType}.`);
if ((data.windowStageEventType === window.WindowStageEventType.PAUSED ||
data.windowStageEventType === window.WindowStageEventType.INACTIVE) && this.panGestureAction !== TouchType.Up) {
this.panGestureAction = TouchType.Up;
this.mAction.uiEnabled();
let lockLevel: LockLevel = LockLevel.FOCUS_LOCK;
if (this.lockLevelBeforePan !== LockLevel.FOCUS_LOCK) {
lockLevel = LockLevel.TEMPORARY_LOCK;
}
this.mAction.updateLockLevel(lockLevel);
}
}
private handleZoomChange(): void {
if (!this.isShowFocus) {
return;
}
HiLog.i(TAG, 'handleZoomChange E.');
this.resetFocusExposure();
}
// 处理小太阳EV滑动手势
private handlePanGestureAction = (action: TouchType) => (event: GestureEvent) => {
if (AppStorage.get<boolean>('isNightShutting')) {
HiLog.i(TAG, `The image is being saved.`);
return;
}
if (this.isLemCollaps || this.isVdeCollapsed || !this.isShowExposure ||
this.panGestureAction === TouchType.Up && action === TouchType.Move) {
return;
}
HiLog.i(TAG, `handlePanGestureAction action: ${action}, rotateAngle: ${this.rotateAngle}.`);
if (action === TouchType.Down) {
this.mAction.uiDisabled();
this.resetSmartControlLocked();
HiLog.i(TAG, `handlePanGestureAction lockLevel: ${this.lockLevel}.`);
this.lockLevelBeforePan = this.lockLevel;
this.mAction.updateLockLevel(LockLevel.LOCK_PAUSE);
} else if (action === TouchType.Up) {
this.mAction.uiEnabled();
let lockLevel: LockLevel = LockLevel.FOCUS_LOCK;
if (this.lockLevelBeforePan !== LockLevel.FOCUS_LOCK) {
lockLevel = LockLevel.TEMPORARY_LOCK;
}
this.mAction.updateLockLevel(lockLevel);
}
this.panGestureAction = action;
this.panGestureEventOffset = this.mFocusExposureService.getExposureOffset(this.rotateAngle, event);
};
/**
* 遥控拍照场景对焦
*/
private collaborateFocusExposure(data: CollaborateCoord): void {
const direction = getStates().get<WindowDirection>('contextReducer', 'direction');
HiLog.d(TAG,
`data:${JSON.stringify(data)},preview:${JSON.stringify(this.previewAnimatePosition)}, xW:${this.xComponentWidth}, xH:${this.xComponentHeight},d:${direction}`);
let gx: number = 0;
let gy: number = 0;
switch (direction) {
case WindowDirection.LEFT:
gx = data.y;
gy = 1 - data.x;
break;
case WindowDirection.RIGHT:
gx = 1 - data.y;
gy = data.x;
break;
case WindowDirection.TOP:
gx = data.x;
gy = data.y;
break;
case WindowDirection.BOTTOM:
gx = 1 - data.y;
gy = 1 - data.x;
break;
}
const event: Event = {
fingerList: [{
globalX: this.xComponentWidth * gx,
globalY: this.previewAnimatePosition.y + this.xComponentHeight * gy
}],
fromWatchTouch: true
};
HiLog.d(TAG, `event: ${JSON.stringify(event)}`)
this.handleFocusExposure(event as GestureEvent);
}
// 处理touch - 短按/长按 数据下发
private handleFocusExposure(event: GestureEvent, isLongPress: boolean = false): boolean {
const eventInfo: EventEffectiveInfo = this.mFocusExposureService.getEventEffectiveInfo(event,
this.previewAnimatePosition, isLongPress);
if (!eventInfo.isEffective) {
HiLog.i(TAG, 'longPressShowExposureAndFocus, out of ranges');
return false;
}
this.handleShow(eventInfo, isLongPress);
return this.handleSetFocusPoint(isLongPress);
}
// AF/AE 显示控制
private handleShow(eventInfo: EventEffectiveInfo, isLongPress: boolean): void {
this.focusPosition = eventInfo.focusPosition;
this.exposurePosition = eventInfo.exposurePosition;
this.resetSmartControlLocked();
HiLog.i(TAG, `handleFocusExposureShow isLongPress: ${isLongPress}, isShowFocus: ${this.isShowFocus}.`);
let lockLevel: LockLevel = LockLevel.TEMPORARY_LOCK;
let isShowExposureRing: boolean = false;
let isShowExposure: boolean = false;
if (isLongPress) {
const isSupportFocusLockExposure: boolean = this.mFocusExposureService.isSupportFocusLockExposure();
const isSupportFocusExposureRing: boolean = this.mFocusExposureService.isSupportFocusExposureRing();
const isSupportOnlyFocus: boolean = this.mFocusExposureService.isSupportOnlyFocusLock();
if (isSupportFocusLockExposure) {
isShowExposure =
!(FeatureManager.getInstance().getFunction(FunctionId.EXPOSURE) as ExposureFunction).getExposureDisabled();
lockLevel = LockLevel.FOCUS_LOCK;
} else if (isSupportFocusExposureRing) {
isShowExposureRing = true;
lockLevel = LockLevel.FOCUS_EXPOSURE_LOCK;
} else if (isSupportOnlyFocus) {
lockLevel = LockLevel.FOCUS_LOCK;
}
} else if (!this.mFocusExposureService.isOnlyFocusSupported()) {
isShowExposure =
!(FeatureManager.getInstance().getFunction(FunctionId.EXPOSURE) as ExposureFunction).getExposureDisabled();
}
if (this.isShowExposureRing !== isShowExposureRing) {
this.mAction.updateShowExposureRing(isShowExposureRing);
}
if (this.isShowExposure !== isShowExposure) {
this.mAction.updateShowExposure(isShowExposure);
}
if (this.lockLevel === lockLevel) {
this.handleFocusProgress();
} else {
this.mAction.updateLockLevel(lockLevel);
}
// 对焦框显示
if (this.isShowFocus) {
this.handleFocusShow();
this.touchPreviewAgain = Date.now().toString(36);
} else {
this.mAction.updateShowFocus(true);
}
HiLog.i(TAG, `setFocusValue focusPosition: ${simpleStringify(this.focusPosition)}`);
}
private handleSetFocusPoint(isLongPress: boolean, eventInfo?: EventEffectiveInfo): boolean {
const focusPosition = eventInfo?.focusPosition ?? this.focusPosition;
const exposurePosition = eventInfo?.exposurePosition ?? this.exposurePosition;
this.mFocusExposureService.setFocusValue({
focusMode: camera.FocusMode.FOCUS_MODE_AUTO,
focusPoint: focusPosition
});
if (this.isShowExposure || this.isShowExposureRing) {
let exposureMode: camera.ExposureMode = camera.ExposureMode.EXPOSURE_MODE_AUTO;
HiLog.i(TAG, `handleFocusExposure exposureMode: ${this.exposureMode}`);
if ((isLongPress || this.exposureMode === camera.ExposureMode.EXPOSURE_MODE_LOCKED) && this.isShowExposure) {
exposureMode = camera.ExposureMode.EXPOSURE_MODE_LOCKED;
}
this.mFocusExposureService.setExposureValue({
exposureMode: exposureMode,
exposurePoint: focusPosition
}, true);
}
return true;
}
// 点击屏幕
private tapGesturePreview = (event: GestureEvent): void => {
HiLog.i(TAG, 'tapGesturePreview E.');
const isShowExtendBar: boolean = this.isShowExtendBar;
this.mAction.previewClicked();
if (this.isVdeCollapsed ||
this.isShowtimeLapse || this.isShowTimedShot ||
this.isImmersive && this.mainTrigCompo !== UIOperationType.LR_SWIPE || isShowExtendBar) {
return;
}
const timedShot: number = FeatureManager.getInstance().getFunction(FunctionId.TIMED_SHOT)?.getValue();
const timeLapse: number = FeatureManager.getInstance().getFunction(FunctionId.TIME_LAPSE)?.getValue();
const timeLapseType: boolean = !timeLapse || timeLapse <= SettingFuncDialogItemIndex.INDEX_FIR;
const timeShotType: boolean = !timedShot || timedShot <= SettingFuncDialogItemIndex.INDEX_FIR;
if (this.isLemCollaps && timeShotType && (timeLapseType || this.recordState === RecordingState.RECORDING)) {
// 考虑移除
this.mAction.captureLemCollaps();
return;
}
// 定时拍摄
if (this.isLemCollaps && timeLapse && timeLapse > SettingFuncDialogItemIndex.INDEX_FIR && timeShotType) {
this.mAction.changeTimeLapse(true);
}
if (this.tabSelector !== FunctionId.NONE) {
this.mAction.changeTabBarSelector(FunctionId.NONE, RenderLocation.NONE);
return;
}
this.handleFocusExposure(event);
}
// 长按
private longPressShowExposureAndFocus = (event: GestureEvent): void => {
HiLog.i(TAG, 'LongPressGesture onAction.');
const isShowExtendBar: boolean = this.isShowExtendBar;
this.mAction.previewClicked(true);
const isSupportLongPress: boolean = this.mFocusExposureService.isSupportLongPress();
if (this.isLemCollaps || !isSupportLongPress || this.isShowExposureRing || this.isVdeCollapsed || this.isShowtimeLapse ||
this.isImmersive && this.mainTrigCompo !== UIOperationType.LR_SWIPE || isShowExtendBar) {
HiLog.i(TAG, `LongPressGesture isShowExposureRing: ${this.isShowExposureRing}`);
return;
}
if (this.tabSelector !== FunctionId.NONE) {
this.mAction.changeTabBarSelector(FunctionId.NONE, RenderLocation.NONE);
return;
}
const isEffect: boolean = this.handleFocusExposure(event, true);
if (!isEffect) {
return;
}
VibratorService.getInstance().triggerVibrator(VibratorService.CAMERA_LONG_PRESS_ID);
if (TipService.getInstance().isAlwaysShowTip()) {
TipService.getInstance().hideAlwaysShowTip(TipService.getInstance().alwaysShowTipMessage);
}
const tipInfo: TipInfo = this.mFocusExposureService.getLongPressTip();
this.longPressTip = tipInfo.tipText;
TipService.getInstance().showTip(this.longPressTip, tipInfo.tipDuring, true);
HiLog.i(TAG, 'LongPressGesture end');
}
// 更新曝光图标位置 (包括 手机旋转)
private changeExposureViewPosition(): void {
if (!this.isShowExposure) {
return;
}
this.exposurePosition = this.mFocusExposureService.getExposurePosition(this.focusPosition);
}
// 处理锁定状态
private handleLockLevelChange(): void {
HiLog.i(TAG, `handleLockLevelChange lockLevel:${this.lockLevel}.`);
if (this.lockLevel === LockLevel.LOCK_PAUSE) {
this.clearFocusProgressTimer();
this.focusOpacity = 1;
this.exposureOpacity = 1;
// 是否从 continue auto 变为auto
const focusData: FocusData = FeatureManager.getInstance().getFunction(FunctionId.FOCUS).getValue();
if (this.isShowExposure && focusData.focusMode !== camera.FocusMode.FOCUS_MODE_AUTO) { // 使用 lock 解决
this.mFocusExposureService.setFocusValue({
focusMode: camera.FocusMode.FOCUS_MODE_AUTO,
focusPoint: this.focusPosition
});
}
} else if (this.lockLevel !== LockLevel.UNLOCK) {
this.handleFocusProgress();
}
if (this.lockLevel === LockLevel.UNLOCK || this.lockLevel === LockLevel.TEMPORARY_LOCK) {
TipService.getInstance().hideAlwaysShowTip(this.longPressTip);
}
}
// 监听 AF 状态上报
private handleFocusStateChange = (data: FocusState): void => {
HiLog.i(TAG,
`handleFocusStateChange lockLevel:${this.lockLevel}, isShowFocus:${this.isShowFocus}, focusState:${data.focusState}.`);
if (data.focusState === camera.FocusState.FOCUS_STATE_SCAN && this.isShowFocus &&
this.lockLevel === LockLevel.UNLOCK) {
this.mFocusExposureService.setExposureValue({
exposureMode: camera.ExposureMode.EXPOSURE_MODE_AUTO,
exposureValue: 0
});
this.resetFocusState();
}
}
build() {
Stack() {
this.childBuilderParam()
FocusArea({
focusPosition: this.focusPosition,
previewAnimatePosition: this.previewAnimatePosition,
touchPreviewAgain: this.touchPreviewAgain,
isHdr: this.isHdr
})
.zIndex(ZIndex.FOCUS_INDEX)
.position(this.focusPosition)
.opacity(this.focusOpacity)
.visibility(AppStorage.get('isPSDCollaps') ? Visibility.None : Visibility.Visible)
ExposureArea({
focusPosition: this.exposurePosition,
previewAnimatePosition: this.previewAnimatePosition,
exposurePosition: this.exposurePosition,
panGestureAction: this.panGestureAction,
panGestureEventOffset: this.panGestureEventOffset,
touchPreviewAgain: this.touchPreviewAgain,
isHdr: this.isHdr
})
.zIndex(ZIndex.FOCUS_INDEX)
.position(this.exposurePosition)
.opacity(this.exposureOpacity)
.visibility(Visibility.Visible)
FocusLock({
focusPosition: this.focusPosition,
touchPreviewAgain: this.touchPreviewAgain,
isHdr: this.isHdr
})
.zIndex(ZIndex.FOCUS_INDEX)
.position(this.focusLockPosition)
.opacity(this.focusOpacity)
}
.width('100%')
.height('100%')
.key('focusExposureArea')
.enabled(this.uiEnable)
.hitTestBehavior(HitTestMode.Transparent)
.accessibilityLevel(this.isShowMorePage && this.isEditMorePage ?
CommonConstants.ACCESSIBILITY_LEVEL_NO : CommonConstants.ACCESSIBILITY_LEVEL_YES)
.accessibilityText(this.cameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT ?
ResGetter.getStringSafe($r('app.string.accessibility_front_preview')) :
ResGetter.getStringSafe($r('app.string.accessibility_back_preview')))
.onTouch(() => {
// 无障碍场景 PreviewGestureArea无法聚焦,但是需要播报对应的引导提示语
})
.gesture(GestureGroup(GestureMode.Exclusive,
LongPressGesture({
repeat: false,
duration: 500,
isFingerCountLimited: true
})
.onAction(this.longPressShowExposureAndFocus),
PanGesture({
fingers: 1,
direction: this.rotateAngle % 180 === 0 ? PanDirection.Vertical : PanDirection.Horizontal,
distance: this.isShowExposure ? 1 : Number.MAX_VALUE,
isFingerCountLimited: true
})
.onActionStart(this.handlePanGestureAction(TouchType.Down))
.onActionUpdate(this.handlePanGestureAction(TouchType.Move))
.onActionEnd(this.handlePanGestureAction(TouchType.Up)),
TapGesture({
fingers: 1,
distanceThreshold: 5,
isFingerCountLimited: true
})
.onAction(this.tapGesturePreview)
))
.onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
if (this.isCustomFilterCardTouched) {
return GestureJudgeResult.REJECT
}
return GestureJudgeResult.CONTINUE;
})
}
// ================== 动效 start ==================
private clearFocusProgressTimer = (): void => {
if (Number.MIN_VALUE === this.focusProgressTimerId) {
return;
}
HiLog.i(TAG, `clearFocusTimer focusProgressTimerId: ${this.focusProgressTimerId}.`);
clearTimeout(this.focusProgressTimerId);
this.focusProgressTimerId = Number.MIN_VALUE;
}
private isFocusDelayDisappear(): boolean {
let isDelay: boolean = false;
if (this.lockLevel === LockLevel.TEMPORARY_LOCK) {
if (this.isSupportedFocusStateDetect) {
isDelay = this.focusMode === camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO;
}
} else {
isDelay = true;
}
HiLog.i(TAG, `isFocusDelayDisappear isDelay: ${isDelay}`);
return isDelay;
}
private handleFocusProgressFinish(isDelay: boolean): void {
HiLog.i(TAG, `handleFocusProgress onFinish, recordState: ${this.recordState}, lockLevel: ${this.lockLevel}.`);
const exposureData: ExposureData = FeatureManager.getInstance().getFunction(FunctionId.EXPOSURE).getValue();
if (this.lockLevel === LockLevel.TEMPORARY_LOCK &&
(this.recordState === RecordingState.READY || exposureData.exposureValue === 0)) {
this.mAction.updateLockLevel(LockLevel.UNLOCK);
if (isDelay) {
this.resetFocusMode();
} else {
this.resetFocusExposure();
}
}
}
// 对焦变成半透明 存留/消失
private handleFocusProgress = (): void => {
HiLog.i(TAG, 'handleFocusProgress E.');
this.clearFocusProgressTimer();
this.focusProgressTimerId = setTimeout(() => {
this.clearFocusProgressTimer();
const isDelay: boolean = this.isFocusDelayDisappear();
const opacity: number = isDelay ? 0.5 : 0;
HiLog.i(TAG,
`handleFocusProgress opacity: ${opacity}, lockLevel: ${this.lockLevel}, isImmersive: ${this.isImmersive}`);
animateToImmediately({
duration: 200,
curve: Curve.Sharp,
onFinish: () => {
this.handleFocusProgressFinish(isDelay);
}
}, () => {
if (this.isImmersive && this.mainTrigCompo !== UIOperationType.LR_SWIPE || this.isShowMorePage ||
this.isShowtimeLapse || this.isShowTimedShot) {
this.progressOpacity = opacity;
} else {
this.focusOpacity = opacity;
this.exposureOpacity = opacity;
}
});
}, EXPOSE_FOCUS_DURING);
}
private handleFocusHidden = (): void => {
HiLog.i(TAG, 'handleFocusHidden E.');
animateToImmediately({
duration: 200,
curve: Curve.Sharp,
onFinish: () => {
HiLog.i(TAG, 'handleFocusHidden onFinish.');
}
}, () => {
this.focusOpacity = 0;
this.exposureOpacity = 0;
});
}
private handleFocusShow = (): void => {
HiLog.i(TAG, 'handleFocusShow start.');
this.focusOpacity = 0;
this.exposureOpacity = 0;
animateToImmediately({
duration: 200,
curve: Curve.Sharp,
onFinish: () => {
HiLog.i(TAG, 'handleFocusVisibility onFinish');
}
}, () => {
this.focusOpacity = 1;
this.exposureOpacity = 1;
});
HiLog.i(TAG, 'handleFocusVisibility end.');
}
// 对焦显示
private handleFocusVisibility(): void {
HiLog.i(TAG, `handleFocusVisibility isShowFocus: ${this.isShowFocus}.`);
if (this.isShowFocus) {
this.handleFocusShow();
} else {
this.handleFocusHidden();
}
}
// 暂时隐藏或从暂时隐藏出来
private handleTempHideShow(isHide: boolean, mainTrigCompo?: UIOperationType): void {
if (isHide) {
this.progressOpacity = this.focusOpacity;
}
const targetOpacity = isHide ? 0 : this.progressOpacity;
HiLog.i(TAG, `handleEnterImmersiveForFocus, start ${targetOpacity}`);
animateToImmediately({
duration: 150,
curve: Curve.Sharp,
onFinish: () => {
HiLog.i(TAG, 'handleEnterImmersiveForFocus, onFinish');
}
}, () => {
this.focusOpacity = targetOpacity;
if (mainTrigCompo !== UIOperationType.EXPOSURE) {
this.exposureOpacity = targetOpacity;
}
});
}
// 处理进/出更多页
private handleMorePageShow(): void {
if (!this.isShowFocus) {
return;
}
this.handleTempHideShow(this.isShowMorePage);
}
// 右滑录像中,滑动调曝光沉浸式结束
private handleMainTrigCompoChange = (type: UIOperationType, oldType: UIOperationType): void => {
HiLog.i(TAG, `handleMainTrigCompoChange type: ${type},oldType: ${oldType}`);
if (type === UIOperationType.LR_SWIPE && oldType === UIOperationType.EXPOSURE) {
this.progressOpacity = 1;
this.handleTempHideShow(false, type);
}
};
// 沉浸式显隐动效
private handleEnterImmersiveForFocus(data: EnterImmersiveStruct): void {
if (!this.isShowFocus || data.mainTrigCompo === UIOperationType.LR_SWIPE) {
return;
}
const isEnterImmersive = !!(data.isImmersive || data.isShowtimeLapse || data.isShowTimedShot);
HiLog.i(TAG, `handleEnterImmersive data: ${JSON.stringify(data)}`);
this.handleTempHideShow(isEnterImmersive, data.mainTrigCompo);
}
}