From 55c0caab6a8c44af178cde97a0890beee98094c5 Mon Sep 17 00:00:00 2001 From: caorunyu Date: Tue, 10 Jun 2025 14:28:29 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E8=BF=9E=E6=8B=8D=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E6=8E=A5=E5=8F=A3=E4=BB=A5=E5=85=BC=E5=AE=B9=E4=BD=8E?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E7=9B=B8=E6=9C=BA=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=9C=80=E9=AB=98=E5=88=86=E8=BE=A8=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: caorunyu --- .../main/ets/constants/CameraConstants.ets | 32 +--- entry/src/main/ets/pages/Index.ets | 155 +++++++++++++----- entry/src/main/ets/utils/CameraShooter.ets | 43 +++-- entry/src/main/ets/utils/PreviewUtil.ets | 12 +- entry/src/main/ets/utils/VideoRecorder.ets | 4 +- .../main/resources/base/element/color.json | 4 + .../main/resources/base/element/float.json | 40 +++++ .../main/resources/base/element/string.json | 12 ++ .../main/resources/en_US/element/string.json | 12 ++ .../main/resources/zh_CN/element/string.json | 12 ++ 10 files changed, 231 insertions(+), 95 deletions(-) create mode 100644 entry/src/main/resources/base/element/float.json diff --git a/entry/src/main/ets/constants/CameraConstants.ets b/entry/src/main/ets/constants/CameraConstants.ets index 6ae7753..844f84d 100644 --- a/entry/src/main/ets/constants/CameraConstants.ets +++ b/entry/src/main/ets/constants/CameraConstants.ets @@ -17,7 +17,7 @@ export class CameraConstants { /** * full screen */ - public static readonly FULL_SCREEN: string = '100%'; + public static readonly FULL_PERCENT: string = '100%'; /** * camera selection width */ @@ -26,22 +26,10 @@ export class CameraConstants { * time format */ public static readonly TIME_FORMAT: string = 'mm:ss.SS'; - /** - * zoom button margin top - */ - public static readonly PREVIEW_HEIGHT_BUTTON: number = 420; - /** - * surface height - */ - public static readonly SURFACE_HEIGHT: number = 500; /** * Parameter Configuration button size */ - public static readonly IMAGE_SIZE: number = 24; - /** - * Parameter Configuration button size - */ - public static readonly IMAGE_HEIGHT: number = 23; + public static readonly IMAGE_HEIGHT: number = 24; /** * Parameter Configuration button margin */ @@ -106,10 +94,6 @@ export class CameraConstants { * camera preview size */ public static readonly FRONT_HEIGHT: number = 3072; - /** - * count down time font size - */ - public static readonly COUNT_DOWN_FONT_SIZE: number = 100; /** * camera shoot ratio */ @@ -119,15 +103,15 @@ export class CameraConstants { */ public static readonly VIDEO_RATIO: number = 16 / 9; /** - * control panel bottom margin + * burst capture count step */ - public static readonly CONTROL_MARGIN_BOTTOM: number = 80; + public static readonly BURST_CAPTURE_STEP: number = 5; /** - * mode panel bottom margin + * minimum burst capture count */ - public static readonly MODE_MARGIN_BOTTOM: number = 20; + public static readonly BURST_CAPTURE_MIN_COUNT: number = 5; /** - * zoom panel bottom margin + * maximum burst capture count */ - public static readonly ZOOM_MARGIN_BOTTOM: number = 40; + public static readonly BURST_CAPTURE_MAX_COUNT: number = 100; } \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 78a5bc7..9a27cb8 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -31,6 +31,7 @@ import { videoRecording } from '../utils/VideoRecorder'; import { + burstCapture, cameraShooting, capture, enableLivePic, @@ -56,7 +57,7 @@ let storage = new LocalStorage(); let videoUri: string; let currentFov: number = 1; const DEFAULT_TOP_OFFSET = - CameraConstants.IMAGE_SIZE + CameraConstants.MARGIN * 2 + CameraConstants.MARGIN_TOP + CameraConstants.MARGIN_HEIGHT; + CameraConstants.IMAGE_HEIGHT + CameraConstants.MARGIN * 2 + CameraConstants.MARGIN_TOP + CameraConstants.MARGIN_HEIGHT; class CountDownTimerModifier implements ContentModifier { applyContent(): WrappedBuilder<[TextTimerConfiguration]> { @@ -67,9 +68,9 @@ class CountDownTimerModifier implements ContentModifier @Builder function buildTextTimer(config: TextTimerConfiguration) { Text(Math.ceil(config.count / 1000 - config.elapsedTime / 100).toString()) - .width(CameraConstants.FULL_SCREEN) - .height(CameraConstants.FULL_SCREEN) - .fontSize(CameraConstants.COUNT_DOWN_FONT_SIZE) + .width(CameraConstants.FULL_PERCENT) + .height(CameraConstants.FULL_PERCENT) + .fontSize($r('app.float.count_down_font_size')) .fontColor(Color.White) .textAlign(TextAlign.Center) } @@ -98,6 +99,8 @@ struct XComponentPage { @LocalStorageLink('isStabilization') isStabilization: boolean = false; @LocalStorageLink('isMovingPhoto') isMovingPhoto: boolean = false; @LocalStorageLink('countDownTime') countDownTime: number = 0; + @LocalStorageLink('isBurstCapture') isBurstCapture: boolean = false; + @LocalStorageLink('burstCaptureCount') burstCaptureCount: number = CameraConstants.BURST_CAPTURE_MIN_COUNT; // Video record timer textTimerController: TextTimerController = new TextTimerController(); // Custom count down timer @@ -121,6 +124,55 @@ struct XComponentPage { // Count down view offset relative to the screen @State countDownOffset: Position = { x: 0, y: 0 }; + @Builder + burstCaptureView() { + Column() { + Row() { + Text($r('app.string.enable_burst_capture')) + Toggle({ type: ToggleType.Switch, isOn: this.isBurstCapture }) + .selectedColor($r('sys.color.brand')) + .switchPointColor($r('sys.color.comp_background_list_card')) + .onChange((isOn: boolean) => { + this.isBurstCapture = isOn; + }) + } + .width(CameraConstants.FULL_PERCENT) + .height($r('app.float.menu_item_height')) + .justifyContent(FlexAlign.SpaceBetween) + + Divider() + .color($r('app.color.divider_color')) + + Column() { + Row() { + Text($r('app.string.burst_capture_count')) + Text($r('app.string.shot_count', this.burstCaptureCount.toFixed(0))) + } + .width(CameraConstants.FULL_PERCENT) + .justifyContent(FlexAlign.SpaceBetween) + + Slider({ + value: this.burstCaptureCount, + min: CameraConstants.BURST_CAPTURE_MIN_COUNT, + step: CameraConstants.BURST_CAPTURE_STEP, + max: CameraConstants.BURST_CAPTURE_MAX_COUNT, + style: SliderStyle.OutSet + }) + .onChange((value: number, _mode: SliderChangeMode) => { + this.burstCaptureCount = value; + }) + } + .margin({ + top: $r('app.float.menu_item_margin_top') + }) + } + .width($r('app.float.burst_control_width')) + .margin({ + left: $r('app.float.menu_item_margin_left'), + right: $r('app.float.menu_item_margin_right') + }) + } + onPageShow(): void { filePreview.closePreview(this.context); } @@ -230,7 +282,7 @@ struct XComponentPage { // Option panel Row() { Image(this.isStabilization ? $r('app.media.stabilization_on') : $r('app.media.stabilization_off')) - .height(CameraConstants.IMAGE_SIZE) + .height(CameraConstants.IMAGE_HEIGHT) .margin(CameraConstants.MARGIN) .visibility(this.isPhoto || this.isFront ? Visibility.Hidden : Visibility.Visible) .rotate({ angle: this.rotation }) @@ -239,9 +291,9 @@ struct XComponentPage { this.isStabilization = !this.isStabilization; isVideo = true; videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); - }); + }) Image(this.isMovingPhoto ? $r('app.media.live_photo_on') : $r('app.media.live_photo_off')) - .height(CameraConstants.IMAGE_SIZE) + .height(CameraConstants.IMAGE_HEIGHT) .margin(CameraConstants.MARGIN) .visibility(this.isPhoto && !this.isFront ? Visibility.Visible : Visibility.Hidden) .rotate({ angle: this.rotation }) @@ -249,7 +301,7 @@ struct XComponentPage { .onClick(() => { this.isMovingPhoto = !this.isMovingPhoto; enableLivePic(this.isMovingPhoto); - }); + }) Image(this.flashPic) .height(CameraConstants.IMAGE_HEIGHT) .margin(CameraConstants.MARGIN) @@ -302,33 +354,7 @@ struct XComponentPage { } }, ] - ); - Image($r('app.media.resolution')) - .height(CameraConstants.IMAGE_SIZE) - .margin(CameraConstants.MARGIN) - .visibility(this.isPhoto ? Visibility.Hidden : this.isFront ? Visibility.Hidden : Visibility.Visible) - .rotate({ angle: this.rotation }) - .animation({ curve: curves.springMotion() }) - .bindMenu( - [ - { - value: '1080P | 60fps', - action: (): void => { - qualityLevel = 0; - stopRecordPreview(); - videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); - } - }, - { - value: '2160P | 60fps', - action: (): void => { - qualityLevel = 1; - stopRecordPreview(); - videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); - } - } - ] - ); + ) Image($r('sys.media.ohos_ic_public_clock')) .fillColor(Color.White) .height(CameraConstants.IMAGE_HEIGHT) @@ -363,11 +389,39 @@ struct XComponentPage { } }, ] - ); + ) + Image(this.isPhoto ? $r('sys.media.ohos_ic_public_more') : $r('app.media.resolution')) + .fillColor(Color.White) + .height(CameraConstants.IMAGE_HEIGHT) + .margin(CameraConstants.MARGIN) + .visibility(this.isFront ? Visibility.Hidden : Visibility.Visible) + .rotate({ angle: this.rotation }) + .animation({ curve: curves.springMotion() }) + .bindMenu( + this.isPhoto ? this.burstCaptureView() : + [ + { + value: '1080P | 60fps', + action: (): void => { + qualityLevel = 0; + stopRecordPreview(); + videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); + } + }, + { + value: '2160P | 60fps', + action: (): void => { + qualityLevel = 1; + stopRecordPreview(); + videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); + } + } + ] + ) } .id('optionPanel') .visibility(this.recording || this.isCountingDown ? Visibility.Hidden : Visibility.Visible) - .width(CameraConstants.FULL_SCREEN) + .width(CameraConstants.FULL_PERCENT) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top } }) @@ -382,7 +436,7 @@ struct XComponentPage { bottom: { anchor: 'zoomPanel', align: VerticalAlign.Top }, middle: { anchor: 'zoomPanel', align: HorizontalAlign.Center } }) - .margin({ bottom: CameraConstants.ZOOM_MARGIN_BOTTOM }) + .margin({ bottom: $r('app.float.zoom_margin_bottom') }) .zIndex(1) // Zoom panel @@ -441,7 +495,7 @@ struct XComponentPage { bottom: { anchor: 'modePanel', align: VerticalAlign.Top }, middle: { anchor: 'modePanel', align: HorizontalAlign.Center } }) - .margin({ bottom: CameraConstants.ZOOM_MARGIN_BOTTOM }) + .margin({ bottom: $r('app.float.zoom_margin_bottom') }) // Mode panel Row() { @@ -483,7 +537,7 @@ struct XComponentPage { bottom: { anchor: 'controlPanel', align: VerticalAlign.Top }, middle: { anchor: 'controlPanel', align: HorizontalAlign.Center } }) - .margin({ bottom: CameraConstants.MODE_MARGIN_BOTTOM }) + .margin({ bottom: $r('app.float.mode_margin_bottom') }) // Control panel Row() { @@ -513,7 +567,7 @@ struct XComponentPage { this.countDownInterval = setTimeout(() => { this.isCountingDown = false; this.countDownTimeController.reset(); - capture(this.isFront); + this.customCapture(); this.currentPic = true; }, this.countDownTime); }) @@ -573,12 +627,12 @@ struct XComponentPage { } .id('controlPanel') .visibility(this.isCountingDown ? Visibility.Hidden : Visibility.Visible) - .width(CameraConstants.FULL_SCREEN) + .width(CameraConstants.FULL_PERCENT) .justifyContent(FlexAlign.SpaceAround) .alignRules({ bottom: { anchor: '__container__', align: VerticalAlign.Bottom } }) - .margin({ bottom: CameraConstants.CONTROL_MARGIN_BOTTOM }) + .margin({ bottom: $r('app.float.control_margin_bottom') }) TextTimer({ controller: this.textTimerController }) .format(CameraConstants.TIME_FORMAT) @@ -590,7 +644,7 @@ struct XComponentPage { middle: { anchor: 'controlPanel', align: HorizontalAlign.Center } }) } - .height(CameraConstants.FULL_SCREEN) + .height(CameraConstants.FULL_PERCENT) .backgroundColor(Color.Black) } @@ -611,6 +665,14 @@ struct XComponentPage { this.isPhoto ? setPhotoSmoothZoom(this.zoom) : setVideoSmoothZoom(this.zoom); } + customCapture(): void { + if (this.isBurstCapture) { + burstCapture(this.isFront, this.burstCaptureCount); + } else { + capture(this.isFront); + } + } + async getThumbnail(): Promise { let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates(); predicates.orderByDesc(photoAccessHelper.PhotoKeys.DATE_ADDED); @@ -638,7 +700,8 @@ struct XComponentPage { // Adjust count down view size and position to match preview this.countDownOffset = { - x: previewSize.width >= windowSize.width ? 0 : this.getUIContext().px2vp((windowSize.width - previewSize.width) / 2), + x: previewSize.width >= windowSize.width ? + 0 : this.getUIContext().px2vp((windowSize.width - previewSize.width) / 2), y: previewSize.height >= windowSize.height ? 0 : DEFAULT_TOP_OFFSET }; this.countDownSize = { @@ -655,5 +718,7 @@ export async function fromBack(context: Context): Promise { storage.setOrCreate('isStabilization', false); storage.setOrCreate('isMovingPhoto', false); storage.setOrCreate('countDownTime', 0); + storage.setOrCreate('isBurstCapture', false); + storage.setOrCreate('burstCaptureCount', CameraConstants.BURST_CAPTURE_MIN_COUNT); cameraShooting(isVideo, cameraPosition, surfaceId, context); } \ No newline at end of file diff --git a/entry/src/main/ets/utils/CameraShooter.ets b/entry/src/main/ets/utils/CameraShooter.ets index 1b39336..3017f5f 100644 --- a/entry/src/main/ets/utils/CameraShooter.ets +++ b/entry/src/main/ets/utils/CameraShooter.ets @@ -15,12 +15,12 @@ import { camera } from '@kit.CameraKit'; import { videoRecording } from './VideoRecorder'; -import { BusinessError } from '@kit.BasicServicesKit'; +import { BusinessError, emitter } from '@kit.BasicServicesKit'; import { photoAccessHelper } from '@kit.MediaLibraryKit'; import { common } from '@kit.AbilityKit'; import { colorSpaceManager } from '@kit.ArkGraphics2D'; import { getGravity } from './GravityUtil'; -import { getPreviewSize, getTargetPreviewProfile, setPreviewRotation } from './PreviewUtil'; +import { getPreviewSize, getTargetPreviewProfile } from './PreviewUtil'; import { CameraConstants } from '../constants/CameraConstants'; let previewOutput: camera.PreviewOutput; @@ -72,17 +72,17 @@ export async function cameraShooting(isVideo: boolean, cameraPosition: number, s let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles; // [End preview_ProfilesArray] // [Start photo_Profiles_Array] - let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles; + let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles.slice().reverse(); // [End photo_Profiles_Array] - let previewProfile = getTargetPreviewProfile(CameraConstants.PHOTO_RATIO, previewProfilesArray); - let photoProfile: undefined | camera.Profile = photoProfilesArray.find((profile: camera.Profile) => { - if (previewProfile) { - return profile.size.width <= 4096 && profile.size.width >= 2448 && - profile.size.height === (previewProfile.size.height / previewProfile.size.width) * profile.size.width; - } - return undefined; - }); + const previewProfile = getTargetPreviewProfile(CameraConstants.PHOTO_RATIO, previewProfilesArray); + const rPhotoProfilesArray = photoProfilesArray.slice().reverse(); + let photoProfile: camera.Profile | undefined = undefined; + if (previewProfile !== undefined) { + photoProfile = rPhotoProfilesArray.find((profile: camera.Profile) => { + return profile.size.height * previewProfile!.size.width === previewProfile!.size.height * profile.size.width; + }) + } // Set preview window by callback callback?.(getPreviewSize(false)); // [Start preview_ProfilesArray] @@ -114,7 +114,6 @@ export async function cameraShooting(isVideo: boolean, cameraPosition: number, s await photoSession.commitConfig(); await photoSession.start(); // [End photo_Session1] - setPreviewRotation(previewOutput); // Check whether the device supports the flash. let flashStatus: boolean = photoSession.hasFlash(); @@ -162,7 +161,7 @@ export function setPhotoSmoothZoom(zoom: number): void { } export async function capture(isFront: boolean) { - const curRotation = await getGravity() + const curRotation = await getGravity(); // [Start PhotoCapture_Setting] let settings: camera.PhotoCaptureSetting = { quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, @@ -173,6 +172,21 @@ export async function capture(isFront: boolean) { // [End PhotoCapture_Setting] } +export async function burstCapture(isFront: boolean, captureCount: number) { + await capture(isFront); + let currCount = 1; + photoOutPut.on('captureReady', async () => { + await capture(isFront); + currCount++; + if (currCount >= captureCount) { + emitter.emit('burstCaptureOff'); + } + }); + emitter.once('burstCaptureOff', () => { + photoOutPut.off('captureReady'); + }); +} + export async function setPhotoFlashMode(flashMode: number): Promise { photoSession.setFlashMode(flashMode); } @@ -192,6 +206,9 @@ export async function releaseCamera(): Promise { photoSession.release(); } if (photoOutPut) { + emitter.off('burstCaptureOff'); + photoOutPut.off('photoAssetAvailable'); + photoOutPut.off('captureReady'); photoOutPut.release(); } } diff --git a/entry/src/main/ets/utils/PreviewUtil.ets b/entry/src/main/ets/utils/PreviewUtil.ets index 397b235..60ed58d 100644 --- a/entry/src/main/ets/utils/PreviewUtil.ets +++ b/entry/src/main/ets/utils/PreviewUtil.ets @@ -87,16 +87,6 @@ export function getTargetPreviewProfile(targetRatio: number, previewProfilesArra return previewProfile; } -/** - * Set property preview rotation according to display rotation - * @param previewOutput - */ -export function setPreviewRotation(previewOutput: camera.PreviewOutput) { - let initDisplayRotation = display.getDefaultDisplaySync().rotation; - let initPreviewRotation = previewOutput.getPreviewRotation(initDisplayRotation * camera.ImageRotation.ROTATION_90); - previewOutput.setPreviewRotation(initPreviewRotation, true); -} - /** * Get current window size * @returns window size if window exist, otherwise zero @@ -112,6 +102,6 @@ export function getCurrentWindowSize() { * @returns screen display size */ export function getCurrentDisplaySize() { - const displaySize = display.getPrimaryDisplaySync(); + const displaySize = display.getDefaultDisplaySync(); return { width: displaySize.width, height: displaySize.height } as Size; } \ No newline at end of file diff --git a/entry/src/main/ets/utils/VideoRecorder.ets b/entry/src/main/ets/utils/VideoRecorder.ets index 5140b00..6a423d9 100644 --- a/entry/src/main/ets/utils/VideoRecorder.ets +++ b/entry/src/main/ets/utils/VideoRecorder.ets @@ -20,7 +20,7 @@ import { fileIo } from '@kit.CoreFileKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { getGravity } from './GravityUtil'; -import { getPreviewSize, getTargetPreviewProfile, setPreviewRotation } from './PreviewUtil'; +import { getPreviewSize, getTargetPreviewProfile } from './PreviewUtil'; import { CameraConstants } from '../constants/CameraConstants'; let file: fileIo.File; @@ -176,7 +176,7 @@ export async function videoRecording(isStabilization: boolean, cameraPosition: n // [EndExclude begin_config] await videoSession.start(); // [End begin_config] - setPreviewRotation(previewOutput); + // Obtains the variable focal length ratio range supported by the camera. let zoomRatioRange = videoSession.getZoomRatioRange(); return zoomRatioRange; diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json index 3c71296..b6c8395 100644 --- a/entry/src/main/resources/base/element/color.json +++ b/entry/src/main/resources/base/element/color.json @@ -3,6 +3,10 @@ { "name": "start_window_background", "value": "#FFFFFF" + }, + { + "name": "divider_color", + "value": "#34000000" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000..17e60e4 --- /dev/null +++ b/entry/src/main/resources/base/element/float.json @@ -0,0 +1,40 @@ +{ + "float": [ + { + "name": "menu_item_height", + "value": "48vp" + }, + { + "name": "menu_item_margin_left", + "value": "10vp" + }, + { + "name": "menu_item_margin_right", + "value": "10vp" + }, + { + "name": "menu_item_margin_top", + "value": "14vp" + }, + { + "name": "zoom_margin_bottom", + "value": "40vp" + }, + { + "name": "mode_margin_bottom", + "value": "20vp" + }, + { + "name": "control_margin_bottom", + "value": "80vp" + }, + { + "name": "burst_control_width", + "value": "240vp" + }, + { + "name": "count_down_font_size", + "value": "100vp" + } + ] +} \ 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 a32078e..0416a61 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -40,6 +40,18 @@ { "name": "reason_read_imagevideo", "value": "For third-party camera to read media files" + }, + { + "name": "enable_burst_capture", + "value": "Enable burst capture" + }, + { + "name": "burst_capture_count", + "value": "Burst capture count" + }, + { + "name": "shot_count", + "value": "%s shots" } ] } \ 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 e15d78b..6296cae 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -39,6 +39,18 @@ { "name": "reason_read_imagevideo", "value": "For third-party camera to read media files" + }, + { + "name": "enable_burst_capture", + "value": "Enable burst capture" + }, + { + "name": "burst_capture_count", + "value": "Burst capture count" + }, + { + "name": "shot_count", + "value": "%s shots" } ] } \ 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 3f792ef..ef0eee5 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -39,6 +39,18 @@ { "name": "reason_read_imagevideo", "value": "用于三方相机读取媒体文件" + }, + { + "name": "enable_burst_capture", + "value": "启用连拍" + }, + { + "name": "burst_capture_count", + "value": "拍摄数量" + }, + { + "name": "shot_count", + "value": "%s 张" } ] } \ No newline at end of file -- Gitee