diff --git a/README.md b/README.md index af578c77a9c206c232ee055cec013f77b1d9a19a..35514f95ad5dcc56653ebf74b339e244dfe5f87f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -## 自定义相机开发实践 +## 实现自定义相机功能 ### 介绍 -本示例基于Camera Kit相机服务,实现预览、双路预览、拍照、录像等功能。为开发者提供基于自定义相机的开发指导。 +本示例基于Camera Kit相机服务,实现基础预览、预览画面调整(前后置镜头切换、闪光灯、对焦、调焦等)、预览进阶功能(网格线、水平仪、超时暂停等)、双路预览、拍照(动图拍摄、延迟拍摄等)、录像等核心功能。为开发者提供自定义相机开发的完整参考与实践指导。 ### 效果预览 @@ -15,7 +15,7 @@ 1. 打开应用,授权后展示预览界面。 2. 上方从左至右按钮功能依次为:闪光灯设置、延迟拍照模式设置、动态拍照模式设置、单双段拍照模式设置(单段拍照模式不支持动态拍摄)。 3. 切换录像模式,上方按钮依次为:闪关灯设置、防抖模式设置。 -4. 右侧按钮依次为:网格线、水平仪。 +4. 右侧按钮依次为:网格线、水平仪、双路预览。 5. 下方按钮可拍照,录像,切换前后置摄像头。 ### 工程目录 @@ -79,9 +79,9 @@ 1.本示例仅支持标准系统上运行,支持设备:华为手机、平板。 -2.HarmonyOS系统:HarmonyOS 5.0.0 Release及以上。 +2.HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。 -3.DevEco Studio版本:DevEco Studio 5.0.0 Release及以上。 +3.DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。 -4.HarmonyOS SDK版本:HarmonyOS 5.0.0 Release SDK及以上。 +4.HarmonyOS SDK版本:HarmonyOS 5.1.1 Release SDK及以上。 diff --git a/build-profile.json5 b/build-profile.json5 index f799a25ed0cdd13394c636ed016873a5eb1e8599..9b6d8d5173cd9a95321811c39152a042b15d74d0 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -5,8 +5,8 @@ { "name": "default", "signingConfig": "default", - "targetSdkVersion": "5.0.4(16)", - "compatibleSdkVersion": "5.0.4(16)", + "targetSdkVersion": "5.1.1(19)", + "compatibleSdkVersion": "5.1.1(19)", "runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { diff --git a/camera/src/main/ets/cameramanagers/CameraManager.ets b/camera/src/main/ets/cameramanagers/CameraManager.ets index e2f94ecb2dff0c824200f9db6162b0d9d13d0bfa..942e6a50eeb6a3d00c9ce8aa24bb4d7261fa503c 100644 --- a/camera/src/main/ets/cameramanagers/CameraManager.ets +++ b/camera/src/main/ets/cameramanagers/CameraManager.ets @@ -27,7 +27,9 @@ export class CameraManager { private outputManagers: OutputManager[] = []; constructor(context: Context, outputManagers: OutputManager[]) { + // [Start cameraManager] this.cameraManager = camera.getCameraManager(context); + // [End cameraManager] this.outputManagers = outputManagers; this.addCameraStatusListener(); } @@ -50,25 +52,30 @@ export class CameraManager { xComponentSurfaceId: string, cameraPosition: camera.CameraPosition, sceneMode: camera.SceneMode, - profile: camera.Profile + getProfile: (cameraOrientation: number) => camera.Profile ) { try { const device = this.getCameraDevice(cameraPosition); if (!device) { return; } + // [Start cameraInput] this.cameraInput = this.cameraManager.createCameraInput(device); await this.cameraInput.open(); + // [End cameraInput] + // [Start session] const session = this.cameraManager.createSession(sceneMode); session.beginConfig(); session.addInput(this.cameraInput); + // [StartExclude session] const config: CreateOutputConfig = { cameraManager: this.cameraManager, device, sceneMode, - profile, + profile: getProfile(device.cameraOrientation), surfaceId: xComponentSurfaceId }; + // [EndExclude session] for (const outputManager of this.outputManagers) { if (outputManager.isActive) { const output = await outputManager.createOutput(config); @@ -77,6 +84,7 @@ export class CameraManager { }; await session.commitConfig(); await session.start(); + // [End session] this.session = session as (camera.PhotoSession | camera.VideoSession); this.setFocusMode(camera.FocusMode.FOCUS_MODE_AUTO); } catch (e) { @@ -93,6 +101,7 @@ export class CameraManager { await this.session?.start(); } + // [Start release] async release() { await this.session?.stop(); for (const outputManager of this.outputManagers) { @@ -103,7 +112,9 @@ export class CameraManager { await this.cameraInput?.close(); await this.session?.release(); } + // [End release] + // [Start getCameraDevice] getCameraDevice(cameraPosition: camera.CameraPosition) { const cameraDevices = this.cameraManager.getSupportedCameras(); const device = cameraDevices?.find(device => device.cameraPosition === cameraPosition) || cameraDevices[0]; @@ -112,11 +123,15 @@ export class CameraManager { } return device; } + // [End getCameraDevice] + // [Start getZoomRange] getZoomRange() { return this.session!.getZoomRatioRange(); } + // [End getZoomRange] + // [Start setFocusMode] setFocusMode(focusMode: camera.FocusMode) { try { const isSupported = this.session?.isFocusModeSupported(focusMode); @@ -129,7 +144,9 @@ export class CameraManager { Logger.error(TAG, 'setFocusMode error ' + JSON.stringify(e)); } } + // [End setFocusMode] + // [Start setFocusPoint] setFocusPoint(point: camera.Point) { try { this.session?.setFocusPoint(point); @@ -137,6 +154,7 @@ export class CameraManager { Logger.error(TAG, 'setFocusPoint error ' + JSON.stringify(e)); } } + // [End setFocusPoint] setZoomRatio(zoom: number) { try { @@ -146,6 +164,7 @@ export class CameraManager { } } + // [Start setSmoothZoom] setSmoothZoom(zoom: number) { try { this.session?.setSmoothZoom(zoom); @@ -153,7 +172,9 @@ export class CameraManager { Logger.error(TAG, 'setSmoothZoom error ' + JSON.stringify(e)); } } + // [End setSmoothZoom] + // [Start setFlashMode] setFlashMode(flashMode: camera.FlashMode) { try { const isSupported = this.session?.isFlashModeSupported(flashMode); @@ -166,6 +187,7 @@ export class CameraManager { Logger.error(TAG, 'setFlashMode error ' + JSON.stringify(e)); } } + // [End setFlashMode] setVideoStabilizationMode(stabilizationMode: camera.VideoStabilizationMode) { try { diff --git a/camera/src/main/ets/cameramanagers/ImageReceiverManager.ets b/camera/src/main/ets/cameramanagers/ImageReceiverManager.ets index fb4632ba11a7757ce7ec22cfb0d2c7d8fe1cefc3..c6943617cf3386cf7340fa7e2e6d80f3e9204bf6 100644 --- a/camera/src/main/ets/cameramanagers/ImageReceiverManager.ets +++ b/camera/src/main/ets/cameramanagers/ImageReceiverManager.ets @@ -59,26 +59,16 @@ export class ImageReceiverManager implements OutputManager { this.output = undefined; } + // [Start init] async init(size: Size, format = image.ImageFormat.JPEG, capacity = 8) { const receiver = image.createImageReceiver(size, format, capacity); const surfaceId = await receiver.getReceivingSurfaceId(); this.onImageArrival(receiver); return surfaceId; } + // [End init] - getRotate(): number { - const displayRotation = display.getDefaultDisplaySync().rotation; - if (this.position === camera.CameraPosition.CAMERA_POSITION_BACK) { - return (displayRotation * camera.ImageRotation.ROTATION_90 + 90) % 360; - } - const rotation = displayRotation * camera.ImageRotation.ROTATION_90; - const angle = (displayRotation * camera.ImageRotation.ROTATION_90 + 270) % 360; - if (rotation === 90 || rotation === 270) { - return (angle + 180) % 360; - } - return angle; - } - + // [Start getPixelMap] async getPixelMap(imgComponent: image.Component, width: number, height: number, stride: number) { if (stride === width) { return await image.createPixelMap(imgComponent.byteBuffer, { @@ -86,7 +76,7 @@ export class ImageReceiverManager implements OutputManager { srcPixelFormat: image.PixelMapFormat.NV21, }); } - const dstBufferSize = width * height * 1.5 + const dstBufferSize = width * height * 1.5; const dstArr = new Uint8Array(dstBufferSize); for (let j = 0; j < height * 1.5; j++) { const srcBuf = new Uint8Array(imgComponent.byteBuffer, j * stride, width); @@ -97,7 +87,9 @@ export class ImageReceiverManager implements OutputManager { srcPixelFormat: image.PixelMapFormat.NV21, }); } + // [End getPixelMap] + // [Start onImageArrival] onImageArrival(receiver: image.ImageReceiver): void { receiver.on('imageArrival', () => { Logger.info(TAG, 'image arrival'); @@ -106,11 +98,15 @@ export class ImageReceiverManager implements OutputManager { Logger.error(TAG, 'readNextImage failed'); return; } + // [Start release] + // [Start nextImage] nextImage.getComponent(image.ComponentType.JPEG, async (err: BusinessError, imgComponent: image.Component) => { + // [StartExclude release] if (err || imgComponent === undefined) { Logger.error(TAG, 'getComponent failed'); } if (imgComponent.byteBuffer) { + // [StartExclude onImageArrival] const width = nextImage.size.width; const height = nextImage.size.height; const stride = imgComponent.rowStride; @@ -129,13 +125,22 @@ export class ImageReceiverManager implements OutputManager { await pixelMap.rotate(rotation); } this.callback(pixelMap); + // [EndExclude onImageArrival] } else { Logger.error(TAG, 'byteBuffer is null'); } + // [EndExclude release] + // [StartExclude nextImage] + // [StartExclude onImageArrival] nextImage.release(); Logger.info(TAG, 'image process done'); - }) - }) - }) + // [EndExclude onImageArrival] + // [EndExclude nextImage] + }); + // [End release] + // [End nextImage] + }); + }); } + // [End onImageArrival] } diff --git a/camera/src/main/ets/cameramanagers/OutputManager.ets b/camera/src/main/ets/cameramanagers/OutputManager.ets index f5fc54fa3330afd0bc980c4d6718ca1bbbed9ed4..c476ae134b6c41de7df7b61c0cd47eb2700dfd2b 100644 --- a/camera/src/main/ets/cameramanagers/OutputManager.ets +++ b/camera/src/main/ets/cameramanagers/OutputManager.ets @@ -23,9 +23,11 @@ export interface CreateOutputConfig { surfaceId?: string; } +// [Start OutputManager] export default interface OutputManager { output?: camera.CameraOutput; isActive: boolean; createOutput: (config: CreateOutputConfig) => Promise; release: () => Promise; -} \ No newline at end of file +} +// [End OutputManager] \ No newline at end of file diff --git a/camera/src/main/ets/cameramanagers/PhotoManager.ets b/camera/src/main/ets/cameramanagers/PhotoManager.ets index 61d33f4ffa56a9978577fc13f6509f8ef75ec862..94f655666a396fb963c0cc90c97818847a5214f5 100644 --- a/camera/src/main/ets/cameramanagers/PhotoManager.ets +++ b/camera/src/main/ets/cameramanagers/PhotoManager.ets @@ -22,6 +22,7 @@ import { image } from '@kit.ImageKit'; import { Logger } from 'commons/src/main/ets/utils/Logger'; import OutputManager, { CreateOutputConfig } from './OutputManager'; import CameraConstant from '../constants/CameraConstants'; +import { colorSpaceManager } from '@kit.ArkGraphics2D'; const LOG_TAG = 'PhotoManager'; @@ -140,38 +141,41 @@ export class PhotoManager implements OutputManager { Logger.error(LOG_TAG, 'getPhoto failed'); return; } - let imageObj: image.Image = photo.main; - imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => { - Logger.info(LOG_TAG, 'getComponent start'); - if (errCode || component === undefined) { - Logger.error(LOG_TAG, 'getComponent failed'); - return; - } - let buffer: ArrayBuffer; - if (component.byteBuffer) { - buffer = component.byteBuffer; - } else { - Logger.error(LOG_TAG, 'byteBuffer is null'); - return; - } - let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE; - let extension: string = 'jpg'; - let options: photoAccessHelper.CreateOptions = { - title: 'testPhoto' - } - let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = - photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoType, extension, options); - assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, buffer) - assetChangeRequest.saveCameraPhoto(); - let accessHelper: photoAccessHelper.PhotoAccessHelper = - photoAccessHelper.getPhotoAccessHelper(context); - await accessHelper.applyChanges(assetChangeRequest); - let imageSource = image.createImageSource(buffer); - let pixelmap = imageSource.createPixelMapSync(); - this.callback(pixelmap, assetChangeRequest.getAsset().uri); - accessHelper.release(); - imageObj.release(); - }); + this.mediaLibSavePhotoSingle(photo.main, context) + }); + } + + mediaLibSavePhotoSingle(imageObj: image.Image, context: Context) { + imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => { + Logger.info(LOG_TAG, 'getComponent start'); + if (errCode || component === undefined) { + Logger.error(LOG_TAG, 'getComponent failed'); + return; + } + let buffer: ArrayBuffer; + if (component.byteBuffer) { + buffer = component.byteBuffer; + } else { + Logger.error(LOG_TAG, 'byteBuffer is null'); + return; + } + let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE; + let extension: string = 'jpg'; + let options: photoAccessHelper.CreateOptions = { + title: 'testPhoto' + } + let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = + photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoType, extension, options); + assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, buffer) + assetChangeRequest.saveCameraPhoto(); + let accessHelper: photoAccessHelper.PhotoAccessHelper = + photoAccessHelper.getPhotoAccessHelper(context); + await accessHelper.applyChanges(assetChangeRequest); + let imageSource = image.createImageSource(buffer); + let pixelmap = imageSource.createPixelMapSync(); + this.callback(pixelmap, assetChangeRequest.getAsset().uri); + accessHelper.release(); + imageObj.release(); }); } @@ -253,6 +257,29 @@ export class PhotoManager implements OutputManager { photoSession.setZoomRatio(zoomRatio || photoZoomRatio); } + getSupportedColorSpaces(session: camera.PhotoSession): Array { + let colorSpaces: Array = []; + try { + colorSpaces = session.getSupportedColorSpaces(); + } catch (error) { + let err = error as BusinessError; + Logger.error(LOG_TAG,`The getSupportedColorSpaces call failed. error code: ${err.code}`); + } + return colorSpaces; + } + + setColorSpaceBeforeCommitConfig(session: camera.PhotoSession, isHdr: boolean): void { + let colorSpace: colorSpaceManager.ColorSpace = + isHdr ? colorSpaceManager.ColorSpace.DISPLAY_P3 : colorSpaceManager.ColorSpace.SRGB; + let colorSpaces: Array = this.getSupportedColorSpaces(session); + let isSupportedColorSpaces = colorSpaces.indexOf(colorSpace) >= 0; + if (isSupportedColorSpaces) { + session.setColorSpace(colorSpace); + } else { + Logger.info(LOG_TAG,`colorSpace: ${colorSpace} is not support`); + } + } + public async capture(isFront: boolean) { const rotation = await this.getPhotoRotation(isFront); let settings: camera.PhotoCaptureSetting = { diff --git a/camera/src/main/ets/cameramanagers/PreviewManager.ets b/camera/src/main/ets/cameramanagers/PreviewManager.ets index af512e4a157b88caf2faee169187778ff0ef0fea..4b270250682f7008fc6d06007237f03d6130fd28 100644 --- a/camera/src/main/ets/cameramanagers/PreviewManager.ets +++ b/camera/src/main/ets/cameramanagers/PreviewManager.ets @@ -24,7 +24,13 @@ const TAG_LOG = 'PreviewManager' export class PreviewManager implements OutputManager { output?: camera.PreviewOutput; isActive: boolean = true; + onPreviewStart: () => void = () => {}; + constructor(onPreviewStart: () => void) { + this.onPreviewStart = onPreviewStart; + } + + // [Start createOutput] async createOutput(config: CreateOutputConfig) { const cameraOutputCap = config.cameraManager.getSupportedOutputCapability(config.device, config.sceneMode); const displayRatio = config.profile.size.width / config.profile.size.height; @@ -44,12 +50,14 @@ export class PreviewManager implements OutputManager { this.addOutputListener(this.output); return this.output; } + // [End createOutput] addOutputListener(output: camera.PreviewOutput) { this.addFrameStartEventListener(output); this.addFrameEndEventListener(output); } + // [Start onFrame] addFrameStartEventListener(output: camera.PreviewOutput) { output.on('frameStart', (err: BusinessError) => { if (err !== undefined && err.code !== 0) { @@ -57,6 +65,7 @@ export class PreviewManager implements OutputManager { return; } Logger.info(TAG_LOG, 'Preview frame started'); + this.onPreviewStart(); }); } @@ -69,16 +78,22 @@ export class PreviewManager implements OutputManager { Logger.info(TAG_LOG, 'Preview frame end'); }); } + // [End onFrame] + // [Start release] async release() { await this.output?.release(); this.output = undefined; } + // [End release] + // [Start getSupportedFrameRates] getSupportedFrameRates() { return this.output?.getSupportedFrameRates(); } + // [End getSupportedFrameRates] + // [Start setFrameRate] setFrameRate(minFps: number, maxFps: number) { try { this.output?.setFrameRate(minFps, maxFps); @@ -86,4 +101,5 @@ export class PreviewManager implements OutputManager { Logger.error(TAG_LOG, 'setFrameRate error ' + JSON.stringify(e)); } } + // [End setFrameRate] } diff --git a/camera/src/main/ets/cameramanagers/VideoManager.ets b/camera/src/main/ets/cameramanagers/VideoManager.ets index ead02bfc4bd22c55463f7f75ba1f5e92e5e231eb..fa0c051eb3e066fbf4a707d707175baf8a746c69 100644 --- a/camera/src/main/ets/cameramanagers/VideoManager.ets +++ b/camera/src/main/ets/cameramanagers/VideoManager.ets @@ -33,13 +33,13 @@ enum QualityLevel { } export enum AVRecorderState { - IDLE = "idle", - PREPARED = "prepared", - STARTED = "started", - PAUSED = "paused", - STOPPED = "stopped", - RELEASED = "released", - ERROR = "error" + IDLE = 'idle', + PREPARED = 'prepared', + STARTED = 'started', + PAUSED = 'paused', + STOPPED = 'stopped', + RELEASED = 'released', + ERROR = 'error' } export class VideoManager implements OutputManager { @@ -87,7 +87,6 @@ export class VideoManager implements OutputManager { return this.output; } - // 配置录制参数。 async prepare() { try { if (this.avRecorder?.state === AVRecorderState.IDLE && this.avConfig) { @@ -102,7 +101,7 @@ export class VideoManager implements OutputManager { async start() { try { if (this.avRecorder?.state === AVRecorderState.PREPARED) { - await this.avRecorder.updateRotation(this.getVideoRotation(await this.getGravity())); // 配置录像旋转角度。 + await this.avRecorder.updateRotation(this.getVideoRotation(await this.getGravity())); await this.output?.start(); await this.avRecorder?.start(); } @@ -127,7 +126,6 @@ export class VideoManager implements OutputManager { } } - // 暂停录制。 async pause() { try { if (this.avRecorder?.state === AVRecorderState.STARTED) { diff --git a/camera/src/main/ets/components/GridLine.ets b/camera/src/main/ets/components/GridLine.ets index acbaeea29fd27c096ca4a0d2ad458dd1b83eb47a..dfdc02e402cdfe4a53766510db66ed23018d57cd 100644 --- a/camera/src/main/ets/components/GridLine.ets +++ b/camera/src/main/ets/components/GridLine.ets @@ -22,13 +22,14 @@ export struct GridLine { @Prop strokeStyle: string |number |CanvasGradient | CanvasPattern = Color.White; @Prop lineWidth: number = 1; + // [Start draw] draw() { const ctx = this.context; ctx.strokeStyle = this.strokeStyle; ctx.lineWidth = this.lineWidth; const height = this.context.height; const width = this.context.width; - // hor + // horizontal for (let i = 1; i < this.cols; i++) { const x = (width / this.cols) * i; ctx.beginPath(); @@ -53,4 +54,5 @@ export struct GridLine { .hitTestBehavior(HitTestMode.Transparent) .onReady(() => this.draw()) } + // [End draw] } \ No newline at end of file diff --git a/camera/src/main/ets/components/LevelIndicator.ets b/camera/src/main/ets/components/LevelIndicator.ets index 97f5c861acd4ebd67aff360422d6606a3f0d9bf2..b3e6ac57d1c83d978e9842ad459dec00dfaf6791 100644 --- a/camera/src/main/ets/components/LevelIndicator.ets +++ b/camera/src/main/ets/components/LevelIndicator.ets @@ -16,6 +16,9 @@ import { curves, display } from '@kit.ArkUI'; import { sensor } from '@kit.SensorServiceKit'; +const ANGLE_DIFFERENCE: number = 3; + +// [Start LevelIndicator] @Component export struct LevelIndicator { @Prop acc: sensor.AccelerometerResponse; @@ -29,8 +32,8 @@ export struct LevelIndicator { } isAlign() { - return Math.abs(this.getRotate()) - 0 <= 3 - || Math.abs(Math.abs(this.getRotate()) - 90) <= 3; + return Math.abs(this.getRotate()) - 0 <= ANGLE_DIFFERENCE + || Math.abs(Math.abs(this.getRotate()) - 90) <= ANGLE_DIFFERENCE; } build() { @@ -39,8 +42,10 @@ export struct LevelIndicator { width: 200, height: 1 }) + // [StartExclude LevelIndicator] .stroke(Color.White) .endPoint([200, 0]) + // [EndExclude LevelIndicator] .strokeDashArray([3, this.isAlign() ? 0 : 3]) .opacity(this.isAlign() ? 1 : 0.5) .rotate({ angle: this.getRotate(), centerX: '50%', centerY: '50%' }) @@ -50,14 +55,19 @@ export struct LevelIndicator { playMode: PlayMode.Normal }) Circle() + // [StartExclude LevelIndicator] .width(48) .height(48) .stroke(Color.White) .fill(Color.Transparent) + // [EndExclude LevelIndicator] .opacity(this.isAlign() ? 1 : 0.5) } + // [StartExclude LevelIndicator] .width('100%') .height('100%') + // [EndExclude LevelIndicator] .hitTestBehavior(HitTestMode.Transparent) } -} \ No newline at end of file +} +// [End LevelIndicator] diff --git a/camera/src/main/module.json5 b/camera/src/main/module.json5 index c5409dfaf8befd98d4e0d6076a3d487d2c7e565e..24f5f42e9f03181b11126c80e3876da0b72da87d 100644 --- a/camera/src/main/module.json5 +++ b/camera/src/main/module.json5 @@ -3,7 +3,7 @@ "name": "camera", "type": "har", "deviceTypes": [ - "default", + "phone", "tablet" ] } diff --git a/commons/src/main/module.json5 b/commons/src/main/module.json5 index 6bae1d9e86d12d221ca82d741d95b8e9e59eb1f1..74e2fe95124ab0568a2619f91d8c8e9bdb3297c6 100644 --- a/commons/src/main/module.json5 +++ b/commons/src/main/module.json5 @@ -3,7 +3,7 @@ "name": "commons", "type": "har", "deviceTypes": [ - "default", + "phone", "tablet" ] } diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index e03cab748815db07bbab403231c21f109eb196e3..f888fba3885967eb6a030298ac614b522664ee8b 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -51,7 +51,7 @@ struct Index { @State isSinglePhoto: boolean = false; @State isLivePhoto: boolean = false; private photoManager: PhotoManager = new PhotoManager(this.context, true, this.isSinglePhoto); - private previewManager: PreviewManager = new PreviewManager(); + private previewManager: PreviewManager = new PreviewManager(() => { this.onPreviewStart() }); private imageReceiverManager: ImageReceiverManager = new ImageReceiverManager(px => { this.onImageReceiver(px); }); @@ -80,8 +80,6 @@ struct Index { @State isZoomPinching: boolean = false; private originZoomBeforePinch: number = 1; // record zoom after pinch, sale base it. - @State blurRadius: number = 0; - @State previewImage: PixelMap | ResourceStr = ''; private PreviewImageHeight: number = 80; @@ -93,19 +91,25 @@ struct Index { private setPreviewSize: () => void = () => { this.previewVM.setPreviewSize(); } async aboutToAppear() { - sensor.on(sensor.SensorId.GRAVITY, (data) => { - this.acc = data; - }, { interval: 100 * 1000 * 1000 }); // 100ms + this.addGravityEventListener(); this.initSleepTimer(); this.registerApplicationStateChange(); this.addOrientationChangeEventListener(); - display.on('foldStatusChange', () => { this.onFoldStatusChange() }) + display.on('foldStatusChange', () => { this.onFoldStatusChange() }); } aboutToDisappear(): void { this.removeOrientationChangeEventListener(); } + // [Start addGravityEventListener] + addGravityEventListener() { + sensor.on(sensor.SensorId.GRAVITY, (data) => { + this.acc = data; + }, { interval: 100 * 1000 * 1000 }); // 100ms + } + // [End addGravityEventListener] + addOrientationChangeEventListener() { this.windowClass.on('windowSizeChange', this.setPreviewSize); } @@ -118,8 +122,10 @@ struct Index { this.previewImage = pixelMap; } + // [Start initSleepTimer] initSleepTimer() { this.sleepTimer = new RefreshableTimer(() => { + this.previewVM.openPreviewBlur(); this.isSleeping = true; this.cameraManager.release(); }, 30 * 1000); @@ -127,16 +133,9 @@ struct Index { const observer = this.getUIContext().getUIObserver(); observer.on('willClick', () => { this.sleepTimer?.refresh(); - }) - } - - openPreviewBlur() { - this.blurRadius = 150; - } - - closePreviewBlur() { - this.blurRadius = 0; + }); } + // [End initSleepTimer] async onFoldStatusChange() { await this.cameraManager.release(); @@ -144,18 +143,39 @@ struct Index { this.syncButtonSettings(); } + // [Start registerApplicationStateChange] registerApplicationStateChange() { this.applicationContext.on('applicationStateChange', { onApplicationForeground: async () => { await this.startCamera(); + // [StartExclude registerApplicationStateChange] this.syncButtonSettings(); + // [EndExclude registerApplicationStateChange] }, onApplicationBackground: () => { + // [StartExclude registerApplicationStateChange] + this.previewVM.openPreviewBlur(); + // [EndExclude registerApplicationStateChange] this.cameraManager.release(); } }) } + async startCamera() { + const cameraPosition = this.previewVM.getCameraPosition(); + const sceneMode = this.previewVM.getSceneMode(); + await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, this.previewVM.getProfile); + } + // [End registerApplicationStateChange] + + exitApp() { + this.applicationContext.killAllProcesses(); + } + + onPreviewStart() { + this.previewVM.closePreviewBlur(); + } + initZooms() { const zoomRange = this.cameraManager.getZoomRange(); const minZoom = zoomRange[0]; @@ -176,17 +196,6 @@ struct Index { }; } - async startCamera() { - const cameraPosition = this.previewVM.getCameraPosition(); - const sceneMode = this.previewVM.getSceneMode(); - const profile = this.previewVM.getProfile(); - await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, profile); - } - - exitApp() { - this.applicationContext.killAllProcesses(); - } - syncButtonSettings() { this.previewManager.setFrameRate(this.previewVM.currentRate, this.previewVM.currentRate); this.photoManager.enableMovingPhoto(this.isLivePhoto); @@ -195,23 +204,34 @@ struct Index { @Builder preview() { + // [Start Stack] Stack({ alignContent: Alignment.Center }) { + // [Start XComponent] + // [Start XComponent_gesture] XComponent({ type: XComponentType.SURFACE, controller: this.previewVM.xComponentController }) + // [StartExclude Stack] + // [StartExclude XComponent_gesture] .onLoad(async () => { + // [StartExclude XComponent] await PermissionManager.request(CameraConstant.PERMISSIONS, this.context) .catch(() => { this.exitApp() }); + // [EndExclude XComponent] this.previewVM.surfaceId = this.previewVM.xComponentController.getXComponentSurfaceId(); this.previewVM.setPreviewSize(); - this.previewVM.xComponentController.setXComponentSurfaceRotation({ lock: true }) + this.previewVM.xComponentController.setXComponentSurfaceRotation({ lock: true }); + // [StartExclude XComponent] await this.startCamera(); this.initZooms(); this.initRates(); + // [EndExclude XComponent] }) + // [StartExclude XComponent_gesture] + // [End XComponent] .gesture( PinchGesture({ fingers: 2 }) .onActionStart(() => { @@ -227,6 +247,7 @@ struct Index { this.isZoomPinching = false; }) ) + // [End XComponent_gesture] .onClick(event => { this.isFocusBoxVisible = true; const previewSize = this.previewVM.previewSize; @@ -243,11 +264,11 @@ struct Index { }, event) this.focusBoxTimer.refresh(); }) - // gird line + // [EndExclude Stack] if (this.isGridLineVisible) { GridLine() } - // level indicator + // [StartExclude Stack] if (this.isLevelIndicatorVisible) { LevelIndicator({ acc: this.acc @@ -267,16 +288,20 @@ struct Index { .fontWeight(FontWeight.Regular) .fontColor(Color.White) } + // [EndExclude Stack] } + // [End Stack] .alignRules({ middle: { anchor: '__container__', align: HorizontalAlign.Center } }) .width(this.previewVM.getPreviewWidth()) .height(this.previewVM.getPreviewHeight()) .margin({ top: this.previewVM.getPreviewTop() }) - .blur(this.blurRadius) + .blur(this.previewVM.blurRadius) + .rotate(this.previewVM.blurRotation) } + // [Start wakeupMask] @Builder wakeupMask() { Column() { @@ -284,10 +309,12 @@ struct Index { .fontColor(Color.White) .opacity(0.6) } + // [StartExclude wakeupMask] .width('100%') .height('100%') .backgroundColor(Color.Black) .justifyContent(FlexAlign.Center) + // [EndExclude wakeupMask] .onClick(async () => { this.isSleeping = false; this.sleepTimer?.refresh(); @@ -295,6 +322,7 @@ struct Index { this.syncButtonSettings(); }) } + // [End wakeupMask] @Builder gridLineButton() { diff --git a/entry/src/main/ets/utils/CommonUtil.ets b/entry/src/main/ets/utils/CommonUtil.ets index f1de783836afb30e705e732c9fcfba83e09aae34..fcf47ba52d1008455efad159b4c1ef32803399b5 100644 --- a/entry/src/main/ets/utils/CommonUtil.ets +++ b/entry/src/main/ets/utils/CommonUtil.ets @@ -24,7 +24,8 @@ export function limitNumberInRange(src: number, range: number[]) { return src; } -// 1.5 [0, 1, 5, 10] return 1 +// find start index the target in which range +// eg: target: 1.5 arr: [0, 1, 5, 10] return 1 export function findRangeIndex(target: number, arr: number[]) { if (arr.length === 0) { return -1; @@ -37,12 +38,14 @@ export function findRangeIndex(target: number, arr: number[]) { }); } -// toFixed(9.97, 1) -> 9.9 +// Math floor float by digit +// eg: toFixed(9.97, 1) -> 9.9 export function toFixed(num: number, digit: number): string { const scale = 10 ** digit; return (Math.floor(num * scale) / scale).toFixed(digit); } +// [Start getClampedChildPosition] // cal absolute position in parent area export function getClampedChildPosition(childSize: Size, parentSize: Size, point: Point): Edges { // center point @@ -66,6 +69,7 @@ export function getClampedChildPosition(childSize: Size, parentSize: Size, point } return { left, top }; } +// [End getClampedChildPosition] export function showToast( UIContext: UIContext, @@ -82,6 +86,7 @@ export function showToast( }); } +// [Start calCameraPoint] export function calCameraPoint(eventX: number, eventY: number, width: number, height: number): camera.Point { const displayRotation = display.getDefaultDisplaySync().rotation * 90; if (displayRotation === 0) { @@ -94,4 +99,5 @@ export function calCameraPoint(eventX: number, eventY: number, width: number, he return { x: 1 - eventY / height, y: eventX / width }; } return { x: eventX / width, y: eventY / height }; -} \ No newline at end of file +} +// [End calCameraPoint] \ No newline at end of file diff --git a/entry/src/main/ets/utils/PermissionManager.ets b/entry/src/main/ets/utils/PermissionManager.ets index 395185a82946381836ab80d67af9909a6821a717..af0c98fd9278d25d60e26f37b875c4edf2ca3a88 100644 --- a/entry/src/main/ets/utils/PermissionManager.ets +++ b/entry/src/main/ets/utils/PermissionManager.ets @@ -18,6 +18,7 @@ import { Logger } from 'commons/src/main/ets/utils/Logger'; const TAG = 'PermissionManager'; +// [Start request_permissions] class PermissionManager { private static atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); @@ -34,5 +35,6 @@ class PermissionManager { } } } +// [End request_permissions] export default PermissionManager; \ No newline at end of file diff --git a/entry/src/main/ets/utils/RefreshableTimer.ets b/entry/src/main/ets/utils/RefreshableTimer.ets index 1cff64773844eff07b52051ece53716afa6e16c3..4956219f7c1c0ff6d3a80d1c46978f556ca02768 100644 --- a/entry/src/main/ets/utils/RefreshableTimer.ets +++ b/entry/src/main/ets/utils/RefreshableTimer.ets @@ -13,6 +13,7 @@ * limitations under the License. */ +// [Start RefreshableTimer] class RefreshableTimer { private timerId?: number; private readonly timeout: number; @@ -47,5 +48,6 @@ class RefreshableTimer { return this.isActive; } } +// [End RefreshableTimer] export default RefreshableTimer; diff --git a/entry/src/main/ets/viewmodels/PreviewViewModel.ets b/entry/src/main/ets/viewmodels/PreviewViewModel.ets index ab00f94200e34c9a4b34094a48d9d6dfe7e15e7c..60fa3e0a9e08e7f3cf7b1de386597f37614d0eb2 100644 --- a/entry/src/main/ets/viewmodels/PreviewViewModel.ets +++ b/entry/src/main/ets/viewmodels/PreviewViewModel.ets @@ -1,7 +1,7 @@ import CameraConstant from '../constants/Constants'; import { camera } from '@kit.CameraKit'; import WindowUtil from '../utils/WindowUtil'; -import { display } from '@kit.ArkUI'; +import { curves, display } from '@kit.ArkUI'; export enum CameraMode { PHOTO, @@ -13,18 +13,30 @@ export enum CameraMode { */ class PreviewViewModel { private uiContext: UIContext; + // [Start isFront] isFront: boolean = false; + // [StartExclude isFront] cameraMode: CameraMode = CameraMode.PHOTO; xComponentController: XComponentController = new XComponentController(); surfaceId: string = ''; previewSize: Size = WindowUtil.getMaxDisplaySize(CameraConstant.RATIO_PHOTO); rates?: number[] = []; currentRate: number = 0; + blurRadius: number = 0; + blurRotation: RotateOptions = { y: 0.5, angle: 0}; constructor(uiContext: UIContext) { this.uiContext = uiContext; } + // [EndExclude isFront] + getCameraPosition() { + return this.isFront + ? camera.CameraPosition.CAMERA_POSITION_FRONT + : camera.CameraPosition.CAMERA_POSITION_BACK; + } + // [End isFront] + getPreviewRatio() { return this.cameraMode === CameraMode.PHOTO ? CameraConstant.RATIO_PHOTO @@ -37,13 +49,8 @@ class PreviewViewModel { : camera.SceneMode.NORMAL_VIDEO; } - getCameraPosition() { - return this.isFront - ? camera.CameraPosition.CAMERA_POSITION_FRONT - : camera.CameraPosition.CAMERA_POSITION_BACK; - } - - getProfile(cameraOrientation: number = 90): camera.Profile { + // [Start getProfile] + getProfile: (cameraOrientation: number) => camera.Profile = cameraOrientation => { const displaySize: Size = WindowUtil.getMaxDisplaySize(this.getPreviewRatio()); const displayRotation = display.getDefaultDisplaySync().rotation * 90; const isRevert = (cameraOrientation + displayRotation) % 180 !== 0; @@ -55,15 +62,18 @@ class PreviewViewModel { } }; } + // [End getProfile] + // [Start setPreviewSize] setPreviewSize() { - const displaySize: Size = WindowUtil.getMaxDisplaySize(this.getPreviewRatio()) + const displaySize: Size = WindowUtil.getMaxDisplaySize(this.getPreviewRatio()); this.previewSize = displaySize; this.xComponentController.setXComponentSurfaceRect({ surfaceWidth: displaySize.width, surfaceHeight: displaySize.height - }) + }); } + // [End setPreviewSize] getPreviewTop() { const previewRatio = this.getPreviewRatio(); @@ -89,6 +99,50 @@ class PreviewViewModel { isCurrentCameraMode(mode: CameraMode) { return this.cameraMode === mode; } + + openPreviewBlur() { + animateToImmediately({ + duration: 200, + curve: Curve.Friction + }, () => { + this.blurRadius = 150; + }); + } + + rotatePreviewBlur() { + animateToImmediately({ + delay: 50, + duration: 200, + curve: curves.cubicBezierCurve(0.2, 0, 0.83, 1), + onFinish: () => { + this.rotatePreviewBlurSecond(); + } + }, () => { + this.blurRotation = { y: 0.5, angle: 90 }; + }); + } + + rotatePreviewBlurSecond() { + this.blurRotation = { y: 0.5, angle: 270 }; + animateToImmediately({ + duration: 200, + curve: curves.cubicBezierCurve(0.17, 0, 0.2, 1), + onFinish: () => { + this.blurRotation = { y: 0.5, angle: 0 }; + } + }, () => { + this.blurRotation = { y: 0.5, angle: 360 }; + }); + } + + closePreviewBlur() { + animateToImmediately({ + duration: 200, + curve: Curve.FastOutSlowIn + }, () => { + this.blurRadius = 0; + }); + } } export default PreviewViewModel; \ No newline at end of file diff --git a/entry/src/main/ets/views/ModeButtonsView.ets b/entry/src/main/ets/views/ModeButtonsView.ets index 3b3d9fd0929ebbb045934a741ac00fc334b7b453..0a8bd9352ccf756425e974a4cc71cfefcf4a114e 100644 --- a/entry/src/main/ets/views/ModeButtonsView.ets +++ b/entry/src/main/ets/views/ModeButtonsView.ets @@ -54,15 +54,15 @@ struct ModeButtonsView { if (this.previewVM.isCurrentCameraMode(modeBtn.mode)) { return; } + this.previewVM.openPreviewBlur(); this.previewVM.cameraMode = modeBtn.mode; this.previewVM.setPreviewSize(); const sceneMode = this.previewVM.getSceneMode(); const cameraPosition = this.previewVM.getCameraPosition(); - const profile = this.previewVM.getProfile(); await this.cameraManager.release(); this.photoManager.setIsActive(this.previewVM.isPhotoMode() ? true : false); this.videoManager.setIsActive(this.previewVM.isPhotoMode() ? false : true); - await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, profile); + await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, this.previewVM.getProfile); this.syncButtonSettings(); } }) diff --git a/entry/src/main/ets/views/OperateButtonsView.ets b/entry/src/main/ets/views/OperateButtonsView.ets index bf02db47364fe9d6666d63ecf197ba4ff4eafd54..767410299a3a6124a5cc378976816903ef06224c 100644 --- a/entry/src/main/ets/views/OperateButtonsView.ets +++ b/entry/src/main/ets/views/OperateButtonsView.ets @@ -130,8 +130,7 @@ struct OperateButtonsView { await this.cameraManager.release(); const cameraPosition = this.previewVM.getCameraPosition(); const sceneMode = this.previewVM.getSceneMode(); - const profile = this.previewVM.getProfile(); - await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, profile); + await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, this.previewVM.getProfile); this.syncButtonSettings(); } }) @@ -207,21 +206,28 @@ struct OperateButtonsView { }) } + // [Start toggleCameraPositionButton] @Builder toggleCameraPositionButton() { Image($r('app.media.toggle_position')) .width(48) .height(48) .onClick(async () => { + // [StartExclude toggleCameraPositionButton] + this.previewVM.openPreviewBlur(); + this.previewVM.rotatePreviewBlur(); + // [EndExclude toggleCameraPositionButton] this.previewVM.isFront = !this.previewVM.isFront; const cameraPosition = this.previewVM.getCameraPosition(); - const profile: camera.Profile = this.previewVM.getProfile(); const sceneMode = this.previewVM.getSceneMode(); await this.cameraManager.release(); - await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, profile); + await this.cameraManager.start(this.previewVM.surfaceId, cameraPosition, sceneMode, this.previewVM.getProfile); + // [StartExclude toggleCameraPositionButton] this.syncButtonSettings(); + // [EndExclude toggleCameraPositionButton] }) } + // [End toggleCameraPositionButton] build() { Row() { diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index fb70c5e2a8a79e04068e3ee5271a08073e7bfbc4..9cc478c2027067723c19d006c0744bdfdf5c1151 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -33,12 +33,20 @@ ] } ], + // [Start permissions] + // [Start permissions_acc] "requestPermissions": [ + // [StartExclude permissions_acc] { "name": "ohos.permission.CAMERA", "reason": "$string:permission_CAMERA", - "usedScene": {} + "usedScene": { + "abilities": [ + "EntryAbility" + ] + } }, + // [StartExclude permissions] { "name": "ohos.permission.MICROPHONE", "reason": "$string:reason_microphone", @@ -75,6 +83,7 @@ ] }, }, + // [EndExclude permissions_acc] { "name": "ohos.permission.ACCELEROMETER", "reason": "$string:permission_SENSOR", @@ -84,6 +93,9 @@ ] } } + // [EndExclude permissions] ] + // [End permissions] + // [End permissions_acc] } } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 7459b0335f649661d5ed646c69f768cd90e993d8..976dad857133bf0a79e9aef8ebfa79057417413f 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -14,123 +14,123 @@ }, { "name": "permission_CAMERA", - "value": "用于相机操作" + "value": "For camera operations" }, { "name": "reason_microphone", - "value": "用于相机录像场景" + "value": "For camera video recording" }, { "name": "reason_media_location", - "value": "用于相机获取媒体信息场景" + "value": "For scenarios where the camera retrieves media information" }, { "name": "reason_write_imagevideo", - "value": "用于相机读写媒体文件" + "value": "For reading and writing media files with the camera" }, { "name": "reason_read_imagevideo", - "value": "用于相机读写媒体文件" + "value": "For camera media file read/write" }, { "name": "permission_SENSOR", - "value": "用于传感器" + "value": "For sensors" }, { "name": "recording", - "value": "录制中" + "value": "Recording in progress" }, { "name": "paused", - "value": "已暂停" + "value": "Paused" }, { "name": "wakeup_text", - "value": "点击屏幕唤醒相机" + "value": "Tap the screen to wake up the camera" }, { "name": "photo", - "value": "拍照" + "value": "Photo" }, { "name": "video", - "value": "录像" + "value": "Video" }, { "name": "preview_rate", - "value": "预览帧率%d" + "value": "Preview frame rate%d" }, { "name": "delay", - "value": "延时拍照%d" + "value": "Time-lapse%d" }, { "name": "delay_close", - "value": "延时拍照关闭" + "value": "Off time-lapse" }, { "name": "moving_open", - "value": "动态拍照已开启" + "value": "Live photo mode is on" }, { "name": "moving_close", - "value": "动态拍照已关闭" + "value": "Live photo mode is off" }, { "name": "photo_single", - "value": "单段拍照" + "value": "Single-segment shooting" }, { "name": "photo_double", - "value": "双段拍照" + "value": "Dual-segment shooting" }, { "name": "stabilization_enable", - "value": "录像防抖已开启" + "value": "Video stabilization is on" }, { "name": "stabilization_disabled", - "value": "录像防抖已关闭" + "value": "Video stabilization is off" }, { "name": "grid_line_open", - "value": "网格线已开启" + "value": "Grid lines are enabled" }, { "name": "grid_line_close", - "value": "网格线已关闭" + "value": "Grid lines are disabled" }, { "name": "level_open", - "value": "水平仪已开启" + "value": "The spirit level is enabled" }, { "name": "level_close", - "value": "水平仪已关闭" + "value": "The spirit level is disabled" }, { "name": "flash_auto", - "value": "闪关灯已自动" + "value": "Flash is in auto mode" }, { "name": "flash_close", - "value": "闪光灯已关闭" + "value": "The flash is off" }, { "name": "flash_always", - "value": "闪光灯已常亮" + "value": "The flash is always on" }, { "name": "flash_open", - "value": "闪光灯已开启" + "value": "The flash is on" }, { "name": "preview_image_open", - "value": "双路预览已开启" + "value": "Dual-channel preview has been enabled" }, { "name": "preview_image_close", - "value": "双路预览已关闭" + "value": "Dual-channel preview has been disabled" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index e0f57eb8698404d31892d8f9d01f072be5d43825..06cf5aeb40bd35672cf10bd4c9c17d6f852b6744 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -11,6 +11,126 @@ { "name": "EntryAbility_label", "value": "CustomCamera" + }, + { + "name": "permission_CAMERA", + "value": "For camera operations" + }, + { + "name": "reason_microphone", + "value": "For camera video recording" + }, + { + "name": "reason_media_location", + "value": "For scenarios where the camera retrieves media information" + }, + { + "name": "reason_write_imagevideo", + "value": "For reading and writing media files with the camera" + }, + { + "name": "reason_read_imagevideo", + "value": "For camera media file read/write" + }, + { + "name": "permission_SENSOR", + "value": "For sensors" + }, + { + "name": "recording", + "value": "Recording in progress" + }, + { + "name": "paused", + "value": "Paused" + }, + { + "name": "wakeup_text", + "value": "Tap the screen to wake up the camera" + }, + { + "name": "photo", + "value": "Photo" + }, + { + "name": "video", + "value": "Video" + }, + { + "name": "preview_rate", + "value": "Preview frame rate%d" + }, + { + "name": "delay", + "value": "Time-lapse%d" + }, + { + "name": "delay_close", + "value": "Off time-lapse" + }, + { + "name": "moving_open", + "value": "Live photo mode is on" + }, + { + "name": "moving_close", + "value": "Live photo mode is off" + }, + { + "name": "photo_single", + "value": "Single-segment shooting" + }, + { + "name": "photo_double", + "value": "Dual-segment shooting" + }, + { + "name": "stabilization_enable", + "value": "Video stabilization is on" + }, + { + "name": "stabilization_disabled", + "value": "Video stabilization is off" + }, + { + "name": "grid_line_open", + "value": "Grid lines are enabled." + }, + { + "name": "grid_line_close", + "value": "Grid lines are disabled." + }, + { + "name": "level_open", + "value": "The spirit level is enabled" + }, + { + "name": "level_close", + "value": "The spirit level is disabled" + }, + { + "name": "flash_auto", + "value": "Flash is in auto mode" + }, + { + "name": "flash_close", + "value": "The flash is off." + }, + { + "name": "flash_always", + "value": "The flash is always on" + }, + { + "name": "flash_open", + "value": "The flash is on" + }, + { + "name": "preview_image_open", + "value": "Dual-channel preview has been enabled" + }, + { + "name": "preview_image_close", + "value": "Dual-channel preview has been disabled" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index 1ce1de976e8b2aaaa94fa33c89849eb508ec4324..bcf0e2cff2c605952f27c43b5f8a8e261c465edf 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -11,6 +11,126 @@ { "name": "EntryAbility_label", "value": "自定义相机" + }, + { + "name": "permission_CAMERA", + "value": "用于相机操作" + }, + { + "name": "reason_microphone", + "value": "用于相机录像场景" + }, + { + "name": "reason_media_location", + "value": "用于相机获取媒体信息场景" + }, + { + "name": "reason_write_imagevideo", + "value": "用于相机读写媒体文件" + }, + { + "name": "reason_read_imagevideo", + "value": "用于相机读写媒体文件" + }, + { + "name": "permission_SENSOR", + "value": "用于传感器" + }, + { + "name": "recording", + "value": "录制中" + }, + { + "name": "paused", + "value": "已暂停" + }, + { + "name": "wakeup_text", + "value": "点击屏幕唤醒相机" + }, + { + "name": "photo", + "value": "拍照" + }, + { + "name": "video", + "value": "录像" + }, + { + "name": "preview_rate", + "value": "预览帧率%d" + }, + { + "name": "delay", + "value": "延时拍照%d" + }, + { + "name": "delay_close", + "value": "延时拍照关闭" + }, + { + "name": "moving_open", + "value": "动态拍照已开启" + }, + { + "name": "moving_close", + "value": "动态拍照已关闭" + }, + { + "name": "photo_single", + "value": "单段拍照" + }, + { + "name": "photo_double", + "value": "双段拍照" + }, + { + "name": "stabilization_enable", + "value": "录像防抖已开启" + }, + { + "name": "stabilization_disabled", + "value": "录像防抖已关闭" + }, + { + "name": "grid_line_open", + "value": "网格线已开启" + }, + { + "name": "grid_line_close", + "value": "网格线已关闭" + }, + { + "name": "level_open", + "value": "水平仪已开启" + }, + { + "name": "level_close", + "value": "水平仪已关闭" + }, + { + "name": "flash_auto", + "value": "闪关灯已自动" + }, + { + "name": "flash_close", + "value": "闪光灯已关闭" + }, + { + "name": "flash_always", + "value": "闪光灯已常亮" + }, + { + "name": "flash_open", + "value": "闪光灯已开启" + }, + { + "name": "preview_image_open", + "value": "双路预览已开启" + }, + { + "name": "preview_image_close", + "value": "双路预览已关闭" } ] } \ No newline at end of file diff --git a/screenshots/devices/photo.png b/screenshots/devices/photo.png index 0f70e9a9c7b4b12318dafa933b0161b6a7469f73..32c49239a739ebac845c2d4f13e7f72261e698ff 100644 Binary files a/screenshots/devices/photo.png and b/screenshots/devices/photo.png differ diff --git a/screenshots/devices/video.png b/screenshots/devices/video.png index a233ed9c764edbb7401d8c0726d43a4f02a57cec..40e1cf67b305ca84b0487709cfb9974f5c0f55d5 100644 Binary files a/screenshots/devices/video.png and b/screenshots/devices/video.png differ