* 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 lazy { HiLog } from '../../utils/HiLog';
import lazy { BaseFunction } from '../core/BaseFunction';
import lazy { FunctionId } from '../core/functionproperty/FunctionId';
import lazy { RenderLocation } from '../core/functionproperty/RenderLocation';
import lazy { RenderType } from '../core/functionproperty/RenderType';
import lazy { UiElement } from '../core/UiElement';
import lazy { FeatureManager } from '../core/FeatureManager';
import lazy { GlobalContext } from '../../utils/GlobalContext';
import lazy { CaptureAction } from './CaptureAction';
import lazy { ThumbnailAction, ThumbnailUpdateScene } from '../../component/thumbnail/ThumbnailAction';
import lazy { CameraAppCapability } from '../../camera/CameraAppCapability';
import lazy { CaptureUtil } from './CaptureUtil';
import lazy { image } from '@kit.ImageKit';
import lazy { BlurAnimateUtil } from '../../utils/BlurAnimateUtil';
import lazy { getCaptureSetting } from './CaptureSetting';
import lazy { simpleStringify } from '../../utils/SimpleStringify';
import lazy { ModeType } from '../../mode/ModeType';
import lazy { CaptureMessage } from './CaptureMessage';
import lazy { execAction } from '../../redux/ActionRegistry';
import lazy { getStates } from '../../redux';
import ResGetter from '../../utils/ResGetter';
import lazy { SystemLanguageUtil } from '../../utils/SystemLanguageUtil';
import lazy { ContextActionType } from '../../redux/actions/ContextActionType';
import lazy { OutputOperation } from '../outputswitcher/OutputOperation';
import lazy { CameraBasicService } from '../../camera/uithread/CameraBasicService';
import lazy { SettingFuncDialogItemIndex } from '../../component/settingview/SettingFuncDialogItemIndex';
import lazy { FrameRateOperation } from '../framerate/FrameRateOperation';
const TAG = 'CaptureFunction';
const SCREENSHOT_THUMBNAIL_TIMER: number = 400;
export class CaptureScene {
public static ULTRA_SNAPSHOT: string = 'ULTRA_SNAPSHOT';
public static SHUTTER_DOWN_CAPTURE: string = 'DOWN';
}
export class CaptureFunction extends BaseFunction {
private mShutterDown: boolean;
private isFirstCapture: boolean = false;
private cameraForeGroundTime: number = 0;
private readonly RENDER_LOCATIONS: RenderLocation[] = [RenderLocation.NONE];
constructor() {
super();
this.mShutterDown = false;
}
getFunctionId(): FunctionId {
return FunctionId.CAPTURE;
}
getRenderType(renderLocation: RenderLocation): RenderType {
return RenderType.NONE;
}
getRenderLocations(): RenderLocation[] {
return this.RENDER_LOCATIONS;
}
getUiElements(): Map<string | number, UiElement> {
let uiElements = new Map();
uiElements.set(UiElement.DEFAULT, new UiElement().setIcon($r('app.media.ic_circled_filled')));
return uiElements;
}
getDefaultValue(): boolean {
return false;
}
setValue(value: boolean | string): void {
HiLog.i(TAG, `setValue value: ${value}.`);
this.mShutterDown = true;
this.capture(getStates().get<camera.CameraPosition>('cameraReducer', 'cameraPosition'), value);
this.mShutterDown = false;
}
getValue(): boolean {
return this.mShutterDown;
}
isAvailable(): boolean {
return true;
}
load(renderLocations: RenderLocation[]): void {
HiLog.i(TAG, 'load E.');
super.load(renderLocations);
this.mEventBus.on([ContextActionType.ABILITY_ON_FOREGROUND], async () => {
this.isFirstCapture = true;
this.cameraForeGroundTime = Date.now();
}, this.mBase.hashCode());
HiLog.i(TAG, 'load x.');
}
unload(renderLocations: RenderLocation[]): void {
HiLog.i(TAG, 'unload E.');
super.unload(renderLocations);
this.mEventBus.clear(this.mBase.hashCode());
HiLog.i(TAG, 'unload X.');
}
private capture(position: camera.CameraPosition, value: boolean | string): void {
HiLog.i(TAG, 'capture begin.');
if (this.isFirstCapture) {
this.isFirstCapture = false;
let firstCaptureInterval = Date.now() - this.cameraForeGroundTime;
HiLog.i(TAG, `first capture interval: ${firstCaptureInterval}`);
}
const isNeedShowRingBackFlash: boolean = getStates().get<boolean>('ringLightReducer', 'isShowPhotoFlash');
if (isNeedShowRingBackFlash) {
this.mStoreManager.postMessage(execAction('RingLightAction', 'emitBackGroundWhite', true));
}
const setting: camera.PhotoCaptureSetting = getCaptureSetting(position);
HiLog.i(TAG, `captureSetting rotatio: ${setting.rotation}, quality: 1, mirror: ${setting.mirror}.`);
this.mStoreManager.postMessage(CaptureAction.pickerViewRotation(setting.rotation));
const captureMessage: CaptureMessage = this.getCaptureMessage(value);
HiLog.i(TAG, `captureMessage: ${simpleStringify(captureMessage)}.`);
this.mCameraProxy.capture(setting, captureMessage);
if (GlobalContext.get().getIsPicker()) {
BlurAnimateUtil.generatePixelMapFromSurface();
}
HiLog.i(TAG, 'capture end.');
}
private getCaptureMessage(value: boolean | string): CaptureMessage {
const isString = typeof value === 'string';
const mode: ModeType = getStates().get<ModeType>('modeReducer', 'mode');
let isBackCapture: boolean = getStates().get<camera.CameraPosition>('cameraReducer', 'cameraPosition') ===
camera.CameraPosition.CAMERA_POSITION_BACK;
const mCustomFilterMessage = this.prepareCustomFilterMessage(isBackCapture, mode);
if (isString && value === CaptureScene.ULTRA_SNAPSHOT) {
HiLog.i(TAG, 'shutter capture ultraSnapshot');
return {
downCapture: true,
isBackCapture: isBackCapture,
};
}
let convertMode: ModeType = mode;
if (OutputOperation.isPanVideoOutput(mode)) {
convertMode = ModeType.PHOTO;
}
const downCapture: boolean = isString && value === CaptureScene.SHUTTER_DOWN_CAPTURE;
HiLog.i(TAG, `shutter down capture: ${downCapture}`);
return {
downCapture,
isBackCapture,
};
}
private prepareCustomFilterMessage(isBackCapture: boolean, mode: ModeType) {
const styleIndex = getStates().get<number>('customFilterStyleReducer', 'styleIndex');
const templateIndex = getStates().get<number>('customFilterStyleReducer', 'templateIndex');
const currentHue = getStates().get<number>('customFilterStyleReducer', 'currentHue');
const currentSaturation = getStates().get<number>('customFilterStyleReducer', 'currentSaturation');
const currentTone = getStates().get<number>('customFilterStyleReducer', 'currentTone');
const customFilterCardOpened = getStates().get<boolean>('customFilterStyleReducer', 'customFilterCardOpened');
let customFilterEffectEnable = getStates().get<boolean>('customFilterStyleReducer', 'customFilterEffectEnable');
const customFilterCardOpenedWhenExit =
getStates().get<boolean>('customFilterStyleReducer', 'customFilterCardOpenedWhenExit');
const isOpenPhotoPicker = getStates().get<boolean>('customFilterStyleReducer', 'isOpenPhotoPicker');
let useIndex = styleIndex;
useIndex = useIndex < 0 ? 0 : useIndex;
let colorValue = '';
let templateName = '';
const mCustomFilterMessage = {
templateIndex: templateIndex,
templateName: customFilterEffectEnable ? templateName : '',
customFilterCardOpened: customFilterCardOpened,
customFilterEffectEnable: customFilterEffectEnable,
customFilterCardOpenedWhenExit: customFilterCardOpenedWhenExit,
isOpenPhotoPicker: isOpenPhotoPicker,
colorValue: colorValue,
};
return mCustomFilterMessage;
}
private async execScreenshotThumbnail(): Promise<void> {
HiLog.i(TAG, 'execScreenshotThumbnail E.');
const pixelMap: image.PixelMap = await CaptureUtil.execScreenshotThumbnail();
this.mStoreManager.postMessage(ThumbnailAction.received(pixelMap, ThumbnailUpdateScene.CAPTURE));
}
}