diff --git a/CameraKit/CameraPicker/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/CameraPicker/entry/src/ohosTest/ets/test/Ability.test.ets index fb801029ff1dc7d987d933b5b863eb10e9dab4aa..5e7e87d4f136fc5f724aab46c4e3a641c411ab61 100644 --- a/CameraKit/CameraPicker/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/CameraPicker/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,7 +52,7 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.CameraPicker', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); diff --git a/CameraKit/DualPreview/build-profile.json5 b/CameraKit/DualPreview/build-profile.json5 index 71bb7f2951455edaeb5ec58548844cf2226d0750..c0df95ca5bdee500f8ea4472bd933fb82c97daca 100644 --- a/CameraKit/DualPreview/build-profile.json5 +++ b/CameraKit/DualPreview/build-profile.json5 @@ -16,19 +16,6 @@ { "app": { "signingConfigs": [ - { - "name": "default", - "type": "HarmonyOS", - "material": { - "certpath": "C:\\Users\\s00888898\\.ohos\\config\\default_DualPreview_0aEu6yVQ_GYI3nseL90IHsXGUNJ4qF5ie7u-kPlBQOE=.cer", - "keyAlias": "debugKey", - "keyPassword": "0000001BE5803A91B9ED88AF98081DCB80B67A57A3435A25805349ADF9447E4F87F5A556D52959A9562F5B", - "profile": "C:\\Users\\s00888898\\.ohos\\config\\default_DualPreview_0aEu6yVQ_GYI3nseL90IHsXGUNJ4qF5ie7u-kPlBQOE=.p7b", - "signAlg": "SHA256withECDSA", - "storeFile": "C:\\Users\\s00888898\\.ohos\\config\\default_DualPreview_0aEu6yVQ_GYI3nseL90IHsXGUNJ4qF5ie7u-kPlBQOE=.p12", - "storePassword": "0000001B04FB919820EA7FF37CC503DA1624E2A79108E943BFD833C6A3D914D5DBC9A3ECDA4B0A0622427A" - } - } ], "products": [ { diff --git a/CameraKit/DualPreview/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/DualPreview/entry/src/ohosTest/ets/test/Ability.test.ets index b5f95e765dbee7c650b4f75791aa58eb1009de34..16427d655c5086156eb45da083b69d801c7cbd24 100644 --- a/CameraKit/DualPreview/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/DualPreview/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,7 +52,7 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.DualPreview', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); diff --git a/CameraKit/MoreResolution/entry/src/main/ets/common/Constants.ets b/CameraKit/MoreResolution/entry/src/main/ets/common/Constants.ets index c49462d02ea40950e045442867e29217e21c24c8..2ed249a13a242b88565660fa672ad3646a0d741c 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/common/Constants.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/common/Constants.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/common/utils/GlobalContext.ets b/CameraKit/MoreResolution/entry/src/main/ets/common/utils/GlobalContext.ets index 31112490a158ce04a4ec9500509bd63e066f13f4..dc753961bf2d70d4c37859960a18a087abbbee05 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/common/utils/GlobalContext.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/common/utils/GlobalContext.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/common/utils/Logger.ets b/CameraKit/MoreResolution/entry/src/main/ets/common/utils/Logger.ets index 367fba66d9fdcd96178501d10ee3bf3ab4330d41..a31be5a5a2e8abdb722b80d89a1e2f98fe842cb0 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/common/utils/Logger.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/common/utils/Logger.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/entryability/EntryAbility.ets b/CameraKit/MoreResolution/entry/src/main/ets/entryability/EntryAbility.ets index 16d5fa141c576240708fd9dc5ca3fa8309627f04..52b16098abc5f228f6943796b5c4f6b69ea128f3 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/entryability/EntryAbility.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/entryability/EntryAbility.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/model/CameraService.ets b/CameraKit/MoreResolution/entry/src/main/ets/model/CameraService.ets index c565bd4f67819d81c0e07078548bcb5f027748bf..3fc1fc4354d43208a5c5b6650264c9a5578406fd 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/model/CameraService.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/model/CameraService.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/pages/EditPage.ets b/CameraKit/MoreResolution/entry/src/main/ets/pages/EditPage.ets index 9009bdd20cd45d7c8742fe0f470ea31b4b9f5f03..d8e9804f6d239ffeefdae126c648b8911eee851e 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/pages/EditPage.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/pages/EditPage.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/pages/Index.ets b/CameraKit/MoreResolution/entry/src/main/ets/pages/Index.ets index f029e885a1640994d552d4f548e7fc528672305b..0edb7fca83661263f5033bb3032e274ca9374ca1 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/pages/Index.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/pages/Index.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/main/ets/views/ModeComponent.ets b/CameraKit/MoreResolution/entry/src/main/ets/views/ModeComponent.ets index f7ebbb548fac7df626a124a3052678422100a734..72ebf7232f4fdf11452239ea72e2a895ec372e6d 100644 --- a/CameraKit/MoreResolution/entry/src/main/ets/views/ModeComponent.ets +++ b/CameraKit/MoreResolution/entry/src/main/ets/views/ModeComponent.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/MoreResolution/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/MoreResolution/entry/src/ohosTest/ets/test/Ability.test.ets index 37863660d2924741b459851e8d4dc58bdceb30aa..934ef4e3afe57d86f2116e0ed53eb2c47157873f 100644 --- a/CameraKit/MoreResolution/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/MoreResolution/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,7 +52,7 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.MoreResolution', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); diff --git a/CameraKit/NDKPreviewImageSample/entry/src/main/cpp/types/libentry/Index.d.ts b/CameraKit/NDKPreviewImageSample/entry/src/main/cpp/types/libentry/Index.d.ts index 2c8ab63a40238ff48064d81cec1f6bd4db1ffe2e..51b0419c513c36592fed7322cc3f243b3fd0aba0 100644 --- a/CameraKit/NDKPreviewImageSample/entry/src/main/cpp/types/libentry/Index.d.ts +++ b/CameraKit/NDKPreviewImageSample/entry/src/main/cpp/types/libentry/Index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * 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 diff --git a/CameraKit/PhotoAndVideoCamera/camera/src/main/ets/cameramanagers/CameraManager.ets b/CameraKit/PhotoAndVideoCamera/camera/src/main/ets/cameramanagers/CameraManager.ets index 4e144466be1bc508be25d045310a3cc89182f2f0..c74a930f0efb98ab4b45e4da0e13ce39b57c935a 100644 --- a/CameraKit/PhotoAndVideoCamera/camera/src/main/ets/cameramanagers/CameraManager.ets +++ b/CameraKit/PhotoAndVideoCamera/camera/src/main/ets/cameramanagers/CameraManager.ets @@ -200,8 +200,8 @@ export class CameraManager { } async restartCamera() { - this.release(); - this.start(this.xComponentSurfaceId, this.cameraPosition, this.sceneMode, this.ratio); + await this.release(); + await this.start(this.xComponentSurfaceId, this.cameraPosition, this.sceneMode, this.ratio); } // [Start release] diff --git a/CameraKit/PhotoAndVideoCamera/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/PhotoAndVideoCamera/entry/src/ohosTest/ets/test/Ability.test.ets index 8e38054cd39eb703d2fc5691c1efe6a5117f31b2..164f45e6c3f6598f4b169e95fc6c691d4d810b71 100644 --- a/CameraKit/PhotoAndVideoCamera/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/PhotoAndVideoCamera/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,66 +52,48 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.PhotoAndVideoCamera', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); abilityDelegator.startAbility(want, (err: Base.BusinessError) => { hilog.info(domain, TAG, 'StartAbility get err ' + JSON.stringify(err)); + // expect(err).assertNull(); }) console.info('%{public}s', ' beforeAll 4'); - let atManager = abilityAccessCtrl.createAtManager(); - console.info('%{public}s', ' beforeAll 5'); - let arr: Array = new Array(); - arr.push('ohos.permission.CAMERA') - arr.push('ohos.permission.GRANT_SENSITIVE_PERMISSIONS') - arr.push('ohos.permission.MICROPHONE') - arr.push('ohos.permission.WRITE_MEDIA') - arr.push('ohos.permission.MEDIA_LOCATION') let driver = Driver.create(); await driver.delayMs(1000); let permissionButton: Component | null = null; - for (let i = 0; i < 4; i++) { - if( i < 3) { - permissionButton = await driver.waitForComponent(ON.text('允许', MatchPattern.EQUALS), 1000); - if (permissionButton != null) { - await permissionButton.click(); - console.info(' after permissionButton.click '); - await driver.delayMs(300); // 等待下一个权限请求弹窗出现 - } - } else { - permissionButton = await driver.waitForComponent(ON.text('本次使用允许', MatchPattern.EQUALS), 1000); - if (permissionButton != null) { - await permissionButton.click(); - console.info(' after permissionButton.click '); - await driver.delayMs(300); // 等待下一个权限请求弹窗出现 - } + for (let i = 0; i < 3; i++) { + permissionButton = await driver.waitForComponent(ON.text('允许', MatchPattern.EQUALS), 1000); + console.info('captureTest btn1' + JSON.stringify(permissionButton)); + if (permissionButton != null) { + await permissionButton.click(); + console.info('captureTest after permissionButton.click '); + await driver.delayMs(300); // 等待下一个权限请求弹窗出现 } - - - } - + } + permissionButton = await driver.findComponent(ON.text('仅使用期间允许', MatchPattern.EQUALS)); + await permissionButton.click(); + await driver.delayMs(300); // 等待下一个权限请求弹窗出现 await driver.delayMs(300); console.info('%{public}s', ' beforeAll 6'); - let btn1 = await driver.findComponent(ON.text('Test Picker Photo&Video', MatchPattern.EQUALS)); + let btn1 = await driver.findComponent(ON.text('拍照', MatchPattern.EQUALS)); console.info('captureTest btn1' + JSON.stringify(btn1)); await btn1.click(); await driver.delayMs(300); console.info('%{public}s', ' beforeAll 8'); - let btn2 = await driver.findComponent(ON.type('Column').id('CaptureOrVideoButton')); + let btn2 = await driver.findComponent(ON.type('Column').id('Capture')); console.info('captureTest btn2' + JSON.stringify(btn2)); await btn2.click(); console.info('%{public}s', ' beforeAll 10'); await driver.delayMs(3000); - let btn3 = await driver.findComponent(ON.text('允许', MatchPattern.EQUALS)); - console.info('captureTest btn3' + JSON.stringify(btn3)); - await btn3.click(); - expect(btn3 == null).assertFalse(); + expect(btn2 == null).assertFalse(); await driver.delayMs(1000); - } catch (err) { + } catch (err) { console.info(' captureTest failed, err: ' + err); - } + } }) it('recordTest', 0, async () => { @@ -124,16 +106,20 @@ export default function abilityTest() { await btn1.click(); await driver.delayMs(300); console.info('%{public}s', ' recordTest 8'); - let btn2 = await driver.findComponent(ON.type('Column').id('CaptureOrVideoButton')); + let btn2 = await driver.findComponent(ON.type('Column').id('VideoStartButton')); console.info('recordTest btn2' + JSON.stringify(btn2)); await btn2.click(); console.info('%{public}s', ' recordTest 10'); await driver.delayMs(3000); - let btn3 = await driver.findComponent(ON.type('Image').id('StopVideo')); + let btn3 = await driver.findComponent(ON.type('Column').id('VideoStopButton')); console.info('recordTest btn3' + JSON.stringify(btn3)); expect(btn3 == null).assertFalse(); await btn3.click(); await driver.delayMs(500); + let btn4 = await driver.findComponent(ON.type('Image').id('Thumbnail')); + console.info('recordTest btn4' + JSON.stringify(btn4)); + await btn4.click(); + await driver.delayMs(500); } catch (err) { console.info(' recordTest failed, err: ' + err); } diff --git a/CameraKit/RotationDisplayCamera/build-profile.json5 b/CameraKit/RotationDisplayCamera/build-profile.json5 index ad55f15ba3361ff21b1acba7eee32c93d37fcc7d..c620b1b0ac81241127dcac5f0cc1dd170e768658 100644 --- a/CameraKit/RotationDisplayCamera/build-profile.json5 +++ b/CameraKit/RotationDisplayCamera/build-profile.json5 @@ -21,10 +21,9 @@ { "name": "default", "signingConfig": "default", - "compileSdkVersion": 20, - "compatibleSdkVersion": 20, - "targetSdkVersion": 20, - "runtimeOS": "OpenHarmony", + "targetSdkVersion": "6.0.0(20)", + "compatibleSdkVersion": "6.0.0(20)", + "runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { "caseSensitiveCheck": true, diff --git a/CameraKit/RotationDisplayCamera/entry/src/main/ets/pages/Index.ets b/CameraKit/RotationDisplayCamera/entry/src/main/ets/pages/Index.ets index cc79c2a3fcb3dd702bab99582e83ff84970a3398..4ba929c04f6737b0c0eae2d04587392e44318012 100644 --- a/CameraKit/RotationDisplayCamera/entry/src/main/ets/pages/Index.ets +++ b/CameraKit/RotationDisplayCamera/entry/src/main/ets/pages/Index.ets @@ -573,19 +573,17 @@ struct Index { // WhiteBalanceMode Column() { // 主按钮 - if (this.firstBol) { - Text('白平衡') - .fontSize(16) - .fontColor('#FFFFFF') - .backgroundColor(Color.Transparent) - .borderRadius('20px') - .textAlign(TextAlign.Center) - .width('20%') - .height('40px') - .onClick(() => { - this.showOptions = !this.showOptions; - }) - } + Text('白平衡') + .fontSize(16) + .fontColor('#FFFFFF') + .backgroundColor(Color.Transparent) + .borderRadius('20px') + .textAlign(TextAlign.Center) + .width('20%') + .height('40px') + .onClick(() => { + this.showOptions = !this.showOptions; + }) // 选项面板 - 垂直排列在主按钮下方 if (this.showOptions) { Column() { @@ -680,7 +678,7 @@ struct Index { .shadow({ radius: 10, color: '#00000030', offsetX: 0, offsetY: 2 }) } } - .position({ x: '80%', y: '35%' }) + .position({ x: '80%', y: '40%' }) .id('WhiteBalanceButton') } } diff --git a/CameraKit/RotationDisplayCamera/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/RotationDisplayCamera/entry/src/ohosTest/ets/test/Ability.test.ets index b5f95e765dbee7c650b4f75791aa58eb1009de34..d5593b661e10cf3f05e28fa9f824308c162e022b 100644 --- a/CameraKit/RotationDisplayCamera/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/RotationDisplayCamera/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,7 +52,7 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.RotationDisplayCamera', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); diff --git a/CameraKit/cameraAnimSample/.gitignore b/CameraKit/cameraAnimSample/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b8017deb0acccd6154f5770a667148c7a495787c --- /dev/null +++ b/CameraKit/cameraAnimSample/.gitignore @@ -0,0 +1,13 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/oh-package-lock.json5 +/entry/oh-package-lock.json5 \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/AppScope/app.json5 b/CameraKit/cameraAnimSample/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d66931c30c7fe0b44ea339ff00d76522118e0605 --- /dev/null +++ b/CameraKit/cameraAnimSample/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.samples.cameraAnimSample", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/CameraKit/cameraAnimSample/AppScope/resources/base/element/string.json b/CameraKit/cameraAnimSample/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..89beb4728756f3bcc6eb4cd02df49d6d4055b0a7 --- /dev/null +++ b/CameraKit/cameraAnimSample/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "camerademo" + } + ] +} diff --git a/CameraKit/cameraAnimSample/AppScope/resources/base/media/app_icon.png b/CameraKit/cameraAnimSample/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd45accb1dfd2fd0da16c732c72faa6e46b26521 Binary files /dev/null and b/CameraKit/cameraAnimSample/AppScope/resources/base/media/app_icon.png differ diff --git a/CameraKit/cameraAnimSample/LICENSE b/CameraKit/cameraAnimSample/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..18795a48d6b12fcdc1aa7bac9a9cb99f83815267 --- /dev/null +++ b/CameraKit/cameraAnimSample/LICENSE @@ -0,0 +1,78 @@ + Copyright (c) 2025 Huawei Device Co., Ltd. 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. + +Apache License, Version 2.0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and +2.You must cause any modified files to carry prominent notices stating that You changed the files; and +3.You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +4.If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/README.md b/CameraKit/cameraAnimSample/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5800e8dbffd707c2723c0736470592b3342bf8c8 --- /dev/null +++ b/CameraKit/cameraAnimSample/README.md @@ -0,0 +1,52 @@ +## 实现相机旋转功能 + +### 介绍 + +本示例基于Camera Kit相机服务,使用ArkTS API实现基础动效,包含闪黑动效和模糊动效。 +在前述功能的基础上,并适配预览、预览画面调整(前后置镜头切换、闪光灯、对焦、调焦、设置曝光中心点等)、拍照、录像等核心功能,为开发者提供自定义相机开发的完整参考与实践指导。 + +### 效果预览 + +![](./screenshot/device/cameraSample.png) + +使用说明: +1. 打开应用,授权后展示预览界面。 +2. 上方按钮功能为:闪光灯设置。 +3. 切换录像模式,上方按钮为:闪关灯设置。 +5. 下方按钮可拍照,录像,切换前后置摄像头。 + +### 工程目录 + +``` +├──entry/src/main/ets/ +│ ├──entryability +│ │ └──EntryAbility.ets // 程序入口类 +│ ├──constants +│ │ └──Constants.ets // 常量文件 +│ ├──pages +│ │ └──Index.ets // 入口预览页面 +│ └──views +│ ├──ModeComponent.ets // 拍照录像模式切换按钮视图 +│ ├──FlashingLightComponent.ets // 闪光灯模式按钮视图 +│ └──FocusComponent.ets // 对焦模式视图 +└──entry/src/main/resources // 应用静态资源目录 +``` + +### 具体实现 + +1. 使用Camera Kit相关能力。 +2. 使用animateToImmediately接口实现闪黑或者模糊等动效的实现 + +### 相关权限 + +- ohos.permission.CAMERA:用于相机操作 +- ohos.permission.MICROPHONE:麦克风权限,用于录像 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 + +2. 本示例支持API20版本SDK,版本号:6.0.0.47。 + +3. 本示例已支持使DevEco Studio 6.0.0 Release(构建版本:6.0.0.858,构建 2025年9月25日)编译运行。 + diff --git a/CameraKit/cameraAnimSample/build-profile.json5 b/CameraKit/cameraAnimSample/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e172f89ff36d2e2c7bcc0b62cd4f2560b879ca87 --- /dev/null +++ b/CameraKit/cameraAnimSample/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "6.0.0(20)", + "compatibleSdkVersion": "6.0.0(20)", + "runtimeOS": "HarmonyOS", + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + }, + ] + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/build-profile.json5 b/CameraKit/cameraAnimSample/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..61b787aaed73199023b8f81f67b1ed4f0952d477 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/hvigorfile.ts b/CameraKit/cameraAnimSample/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..81677eec2b98092b37aed59a8b9b29e6d0f73b3a --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/CameraKit/cameraAnimSample/entry/obfuscation-rules.txt b/CameraKit/cameraAnimSample/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..985b2aeb7658286b17bd26eab8f217c3fe75ea8b --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/oh-package.json5 b/CameraKit/cameraAnimSample/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9378c13936be18fb8c695e5353ddfe4c7dc52482 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/common/Constants.ts b/CameraKit/cameraAnimSample/entry/src/main/ets/common/Constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..c72afdbc3602ef1e209ec3a24854e2b02f8fc790 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/common/Constants.ts @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +export class Constants { + /** + * Surface width in xComponent. + */ + static readonly X_COMPONENT_SURFACE_WIDTH = 1920; + + /** + * Surface height in xComponent. + */ + static readonly X_COMPONENT_SURFACE_HEIGHT = 1080; + + /** + * Border width in xComponent. + */ + static readonly X_COMPONENT_BORDER_WIDTH = 0.5; + + /** + * Border width in capture button. + */ + static readonly CAPTURE_BUTTON_BORDER_WIDTH = 3; + + /** + * Border radius size in capture button. + */ + static readonly CAPTURE_BUTTON_BORDER_RADIUS = 70; + + /** + * Margins of the component. + */ + static readonly CAPTURE_BUTTON_COLUMN_MARGIN = 24; + + /** + * Paddings of the component. + */ + static readonly CAPTURE_BUTTON_COLUMN_PADDING = 24; + + /** + * Size of the back icon. + */ + static readonly BACK_ICON_SIZE = 24; + + /** + * Size of the back icon. + */ + static readonly IMAGE_SIZE = 25; + + /** + * Margins of the back icon. + */ + static readonly BACK_ICON_MARGIN = 24; + + /** + * The full percentage of component. + */ + static readonly FULL_PERCENT: string = '100%'; + + /** + * The Eighty-five percent of component. + */ + static readonly EIGHTY_FIVE_PERCENT: string = '85%'; + + /** + * The Eighty percent of component. + */ + static readonly EIGHTY_PERCENT: string = '80%'; + + /** + * The seventy-five percent of the components. + */ + static readonly SEVENTY_FIVE_PERCENT: string = '75%'; + + + /** + * The seventy percent of the components. + */ + static readonly SEVENTY_PERCENT: string = '70%'; + + /** + * The forty percent of the components. + */ + static readonly FORTY_PERCENT: string = '40%'; + + /** + * The thirty percent of the components. + */ + static readonly THIRTY_PERCENT: string = '30%'; + + /** + * The fifteen percent of the bottom of the margin. + */ + static readonly FIFTEEN_PERCENT: string = '15%'; + + /** + * The ten percent of the bottom of the margin. + */ + static readonly TEN_PERCENT: string = '10%'; + + /** + * The zero percent of the bottom of the margin. + */ + static readonly ZERO_PERCENT: string = '0%'; + + /** + * border radius. + */ + static readonly TEXT_BORDER_RADIUS: number = 25; + + /** + * font size. + */ + static readonly FONT_SIZE_14: number = 14; + + /** + * column space. + */ + static readonly COLUMN_SPACE_24: number = 24; + + /** + * row space. + */ + static readonly ROW_SPACE_24: number = 24; + + /** + * default zoom ratio. + */ + static readonly DEFAULT_ZOOM_RATIO: number = 1; + + /** + * border radius. + */ + static readonly BORDER_RADIUS_14: number = 14; + + /** + * default zoom radio min. + */ + static readonly ZOOM_RADIO_MIN: number = 1; + + /** + * default zoom radio max. + */ + static readonly ZOOM_RADIO_MAX: number = 6; + + /** + * default zoom radio step. + */ + static readonly ZOOM_RADIO_MAX_STEP: number = 0.1; + + /** + * default zoom radio step. + */ + static readonly ZOOM_RADIO_MIN_STEP: number = 0.01; + + /** + * capture Column width. + */ + static readonly CAPTURE_COLUMN_WIDTH: number = 50; + + /** + * capture Column width. + */ + static readonly CAPTURE_ROW_HEIGHT: number = 28; + + /** + * AUDIO_BITRATE. + */ + static readonly AUDIO_BITRATE: number = 48000; + + /** + * AUDIO_CHANNELS. + */ + static readonly AUDIO_CHANNELS: number = 2; + + /** + * AUDIO_SAMPLE_RATE. + */ + static readonly AUDIO_SAMPLE_RATE: number = 48000; + + /** + * VIDEO_BITRATE. + */ + static readonly VIDEO_BITRATE: number = 512000; + + /** + * VIDEO_FRAME. + */ + static readonly MAX_VIDEO_FRAME: number = 60; + + /** + * FLASH_POSITION_X. + */ + static readonly FLASH_POSITION_X: number = 0; + + /** + * FLASH_POSITION_Y. + */ + static readonly FLASH_POSITION_Y: number = 50; +}; \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/BlurAnimateUtil.ts b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/BlurAnimateUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..1c60e77fcab52432c7260b5b920b9d351d5b5074 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/BlurAnimateUtil.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { image } from '@kit.ImageKit'; +import Logger from './Logger'; +import { Constants } from '../Constants'; + +const TAG: string = 'BlurAnimateUtil'; + +export class BlurAnimateUtil { + public static surfaceShot: image.PixelMap; + public static readonly SHOW_BLUR_DURATION: number = 200; + public static readonly HIDE_BLUR_DURATION: number = 200; + public static readonly ROTATION_DURATION: number = 200; + public static readonly FLIP_DELAY: number = 50; + public static readonly ROTATE_AXIS: number = 0.5; + public static readonly IMG_ROTATE_ANGLE_90: number = 90; + public static readonly IMG_ROTATE_ANGLE_270: number = 270; + public static readonly IMG_FLIP_ANGLE_0: number = 0; + public static readonly IMG_FLIP_ANGLE_180: number = 180; + public static readonly IMG_FLIP_ANGLE_90: number = 90; + public static readonly IMG_FLIP_ANGLE_270: number = 270; + public static readonly IMG_FLIP_ANGLE_MINUS_90: number = -90; + public static readonly ANIM_MODE_SWITCH_BLUR = 48; + public static readonly IMG_SCALE: number = 0.75; + + /** + * 获取surface截图 + * @param surfaceId + * @returns + */ + public static async doSurfaceShot(surfaceId: string) { + Logger.info(TAG, `doSurfaceShot surfaceId:${surfaceId}.`); + if ('' === surfaceId) { + Logger.error(TAG, 'surface not ready!'); + return; + } + try { + if (this.surfaceShot) { + await this.surfaceShot.release(); + } + this.surfaceShot = await image.createPixelMapFromSurface(surfaceId, { + size: { width: Constants.X_COMPONENT_SURFACE_WIDTH, height: Constants.X_COMPONENT_SURFACE_HEIGHT }, // 取预览流profile的宽高 + x: 0, + y: 0 + }); + let imageInfo: image.ImageInfo = await this.surfaceShot.getImageInfo(); + Logger.info('doSurfaceShot surfaceShot:' + JSON.stringify(imageInfo.size)); + } catch (err) { + Logger.error(JSON.stringify(err)) + } + } + + public static getSurfaceShot() { + return this.surfaceShot; + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/DateTimeUtil.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/DateTimeUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..ce6b2a6b9c83dd167944c9d985dc5e8e52880eaf --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/DateTimeUtil.ets @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +/** + * @file 日期工具 + */ +export default class DateTimeUtil { + + /** + * 时分秒 + */ + getTime(): string { + const DATETIME = new Date(); + return this.concatTime(DATETIME.getHours(), DATETIME.getMinutes(), DATETIME.getSeconds()); + } + + /** + * 年月日 + */ + getDate(): string { + const DATETIME = new Date(); + return this.concatDate(DATETIME.getFullYear(), DATETIME.getMonth() + 1, DATETIME.getDate()); + } + + /** + * 日期不足两位补充0 + * @param value-数据值 + */ + fill(value: number): string { + let maxNumber = 9; + return (value > maxNumber ? '' : '0') + value; + } + /** + * 录制时间定时器 + * @param millisecond-数据值 + */ + getVideoTime(millisecond: number): string { + let millisecond2minute = 60000; + let millisecond2second = 1000; + let minute = Math.floor(millisecond / millisecond2minute); + let second = Math.floor((millisecond - minute * millisecond2minute) / millisecond2second); + return `${this.fill(minute)} : ${this.fill(second)}`; + } + /** + * 年月日格式修饰 + * @param year + * @param month + * @param date + */ + concatDate(year: number, month: number, date: number): string { + return `${year}${this.fill(month)}${this.fill(date)}`; + } + + /** + * 时分秒格式修饰 + * @param hours + * @param minutes + * @param seconds + */ + concatTime(hours: number, minutes: number, seconds: number): string { + return `${this.fill(hours)}${this.fill(minutes)}${this.fill(seconds)}`; + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/GlobalContext.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/GlobalContext.ets new file mode 100644 index 0000000000000000000000000000000000000000..2adb4c0570cd2dd5b091107157d22aa373c2a6e0 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/GlobalContext.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { common } from '@kit.AbilityKit'; + +export class GlobalContext { + private static instance: GlobalContext; + private _objects = new Map(); + private cameraSettingContext: common.UIAbilityContext | undefined = undefined; + public static get(): GlobalContext { + if (!Boolean(GlobalContext.instance).valueOf()) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + getT(value: string): T { + return this._objects.get(value) as T; + } + + setObject(key: string, objectClass: Object): void { + this._objects.set(key, objectClass); + } + + apply(value: string): void { + const func = this._objects.get(value); + if (func) { + (func as Function)(); + } + } + + public setCameraSettingContext(context: common.UIAbilityContext): void { + this.cameraSettingContext = context; + } + + public getCameraSettingContext(): common.UIAbilityContext | undefined { + return this.cameraSettingContext; + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/Logger.ts b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/Logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..c661ecafd4178f3416042f0b49f880bc3e7952c7 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/common/utils/Logger.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { hilog } from '@kit.PerformanceAnalysisKit'; + +const TAG = 'cameraDemo'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + debug(...args: string[]): void { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]): void { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]): void { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]): void { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger(TAG); \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/entryability/EntryAbility.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..e58fc46a3ef5f1ad6ce86e5aabf1790bc7beb92d --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { abilityAccessCtrl, AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/utils/Logger'; +import { GlobalContext } from '../common/utils/GlobalContext'; + +const TAG = 'EntryAbility'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + Logger.info(TAG, 'Ability onCreate'); + GlobalContext.get().setCameraSettingContext(this.context); + } + + onDestroy(): void { + Logger.info(TAG, 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + Logger.info(TAG, 'Ability onWindowStageCreate'); + windowStage.getMainWindow().then((win: window.Window): void => { + win.setWindowLayoutFullScreen(true).then((): void => { + win.setWindowSystemBarEnable([]).then((): void => { + }); + }); + win.setWindowSystemBarProperties({ + statusBarContentColor: '#ffffff', + navigationBarContentColor: '#ffffff' + }) + .then((): void => { + }); + }); + this.requestPermissionsFn(); + windowStage.loadContent('pages/Index', (err, data) => { + if (err.code) { + Logger.info(TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + Logger.info(TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + Logger.info(TAG, 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + Logger.info(TAG, 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + Logger.info(TAG, 'Ability onBackground'); + AppStorage.setOrCreate('isOpenEditPage', false); + } + + /** + * 获取权限 + */ + requestPermissionsFn(): void { + let atManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(this.context, [ + 'ohos.permission.CAMERA', + 'ohos.permission.MICROPHONE' + ]).then((): void => { + AppStorage.setOrCreate('isShow', true); + Logger.info(TAG, 'request Permissions success!'); + }).catch((error: BusinessError): void => { + Logger.info(TAG, `requestPermissionsFromUser call Failed! error: ${error.code}`); + }); + } +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/mode/CameraService.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/mode/CameraService.ets new file mode 100644 index 0000000000000000000000000000000000000000..156982cb99db98d909745c0c114992c0df96eb88 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/mode/CameraService.ets @@ -0,0 +1,924 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { camera } from '@kit.CameraKit'; +import { media } from '@kit.MediaKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { JSON } from '@kit.ArkTS'; +import { fileIo } from '@kit.CoreFileKit'; +import { GlobalContext } from '../common/utils/GlobalContext'; +import Logger from '../common/utils/Logger'; +import { Constants } from '../common/Constants'; + +const TAG: string = 'CameraService'; + +export class SliderValue { + min: number = 1; + max: number = 6; + step: number = 0.1; +} + +class CameraService { + private cameraManager: camera.CameraManager | undefined = undefined; + private cameras: Array | Array = []; + private cameraInput: camera.CameraInput | undefined = undefined; + private previewOutput: camera.PreviewOutput | undefined = undefined; + private photoOutput: camera.PhotoOutput | undefined = undefined; + private videoOutput: camera.VideoOutput | undefined = undefined; + private avRecorder: media.AVRecorder | undefined = undefined; + private session: camera.PhotoSession | camera.VideoSession | undefined = undefined; + private handlePhotoAssetCb: (photoAsset: photoAccessHelper.PhotoAsset) => void = () => { + }; + private curCameraDevice: camera.CameraDevice | undefined = undefined; + private isRecording: boolean = false; + frameStartFlag: number = 0; + // 推荐拍照分辨率之一 + private photoProfileObj: camera.Profile = { + format: 2000, + size: { + width: Constants.X_COMPONENT_SURFACE_WIDTH, + height: Constants.X_COMPONENT_SURFACE_HEIGHT + } + }; + // 推荐预览分辨率之一 + private previewProfileObj: camera.Profile = { + format: 1003, + size: { + width: Constants.X_COMPONENT_SURFACE_WIDTH, + height: Constants.X_COMPONENT_SURFACE_HEIGHT + } + }; + // 推荐录像分辨率之一 + private videoProfileObj: camera.VideoProfile = { + format: 1003, + size: { + width: Constants.X_COMPONENT_SURFACE_WIDTH, + height: Constants.X_COMPONENT_SURFACE_HEIGHT + }, + frameRateRange: { + min: 30, + max: 60 + } + }; + private curSceneMode: camera.SceneMode = camera.SceneMode.NORMAL_PHOTO; + + constructor() { + } + + setSavePictureCallback(callback: (photoAsset: photoAccessHelper.PhotoAsset) => void): void { + this.handlePhotoAssetCb = callback; + } + + setSceneMode(sceneMode: camera.SceneMode): void { + this.curSceneMode = sceneMode; + } + + getSceneMode(): camera.SceneMode { + return this.curSceneMode; + } + + getPreviewProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined { + let previewProfiles = cameraOutputCapability.previewProfiles; + if (previewProfiles.length < 1) { + return undefined; + } + let index = previewProfiles.findIndex((previewProfile: camera.Profile) => { + return previewProfile.size.width === this.previewProfileObj.size.width && + previewProfile.size.height === this.previewProfileObj.size.height && + previewProfile.format === this.previewProfileObj.format; + }); + if (index === -1) { + return undefined; + } + return previewProfiles[index]; + } + + getPhotoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined { + let photoProfiles = cameraOutputCapability.photoProfiles; + if (photoProfiles.length < 1) { + return undefined; + } + let index = photoProfiles.findIndex((photoProfile: camera.Profile) => { + return photoProfile.size.width === this.photoProfileObj.size.width && + photoProfile.size.height === this.photoProfileObj.size.height && + photoProfile.format === this.photoProfileObj.format; + }); + if (index === -1) { + return undefined; + } + return photoProfiles[index]; + } + + getVideoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.VideoProfile | undefined { + let videoProfiles = cameraOutputCapability.videoProfiles; + if (videoProfiles.length < 1) { + return undefined; + } + for (let i = 0; i < videoProfiles.length; i++) { + Logger.info(TAG, `getVideoProfile: ${JSON.stringify(videoProfiles[i])}`); + } + let index = videoProfiles.findIndex((videoProfile: camera.VideoProfile) => { + return videoProfile.size.width === this.videoProfileObj.size.width && + videoProfile.size.height === this.videoProfileObj.size.height && + videoProfile.format === this.videoProfileObj.format && + videoProfile.frameRateRange.min <= Constants.MAX_VIDEO_FRAME && + videoProfile.frameRateRange.max <= Constants.MAX_VIDEO_FRAME; + }); + if (index === -1) { + return undefined; + } + return videoProfiles[index]; + } + + isSupportedSceneMode(cameraManager: camera.CameraManager, cameraDevice: camera.CameraDevice): boolean { + let sceneModes = cameraManager.getSupportedSceneModes(cameraDevice); + if (sceneModes === undefined) { + return false; + } + let index = sceneModes.findIndex((sceneMode: camera.SceneMode) => { + return sceneMode === this.curSceneMode; + }); + if (index === -1) { + return false; + } + return true; + } + + /** + * 初始化相机功能 + * @param surfaceId - Surface 的 ID + * @param cameraDeviceIndex - 相机设备索引 + * @returns 无返回值 + */ + async initCamera(surfaceId: string, cameraDeviceIndex: number): Promise { + Logger.debug(TAG, `initCamera cameraDeviceIndex: ${cameraDeviceIndex}`); + try { + await this.releaseCamera(); + // 获取相机管理器实例 + this.cameraManager = this.getCameraManagerFn(); + if (this.cameraManager === undefined) { + Logger.error(TAG, 'cameraManager is undefined'); + return; + } + // 获取支持指定的相机设备对象 + this.cameras = this.getSupportedCamerasFn(this.cameraManager); + if (this.cameras.length < 1 || this.cameras.length < cameraDeviceIndex + 1) { + return; + } + this.curCameraDevice = this.cameras[cameraDeviceIndex]; + let isSupported = this.isSupportedSceneMode(this.cameraManager, this.curCameraDevice); + if (!isSupported) { + Logger.error(TAG, 'The current scene mode is not supported.'); + return; + } + let cameraOutputCapability = + this.cameraManager.getSupportedOutputCapability(this.curCameraDevice, this.curSceneMode); + let previewProfile = this.getPreviewProfile(cameraOutputCapability); + if (previewProfile === undefined) { + Logger.error(TAG, 'The resolution of the current preview stream is not supported.'); + return; + } + this.previewProfileObj = previewProfile; + // 创建previewOutput输出对象 + this.previewOutput = this.createPreviewOutputFn(this.cameraManager, this.previewProfileObj, surfaceId); + if (this.previewOutput === undefined) { + Logger.error(TAG, 'Failed to create the preview stream.'); + return; + } + // 监听预览事件 + this.previewOutputCallBack(this.previewOutput); + if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) { + let photoProfile = this.getPhotoProfile(cameraOutputCapability); + if (photoProfile === undefined) { + Logger.error(TAG, 'The resolution of the current photo stream is not supported.'); + return; + } + this.photoProfileObj = photoProfile; + // 创建photoOutPut输出对象 + this.photoOutput = this.createPhotoOutputFn(this.cameraManager, this.photoProfileObj); + if (this.photoOutput === undefined) { + Logger.error(TAG, 'Failed to create the photo stream.'); + return; + } + } else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) { + let videoProfile = this.getVideoProfile(cameraOutputCapability); + if (videoProfile === undefined) { + Logger.error(TAG, 'The resolution of the current video stream is not supported.'); + return; + } + this.videoProfileObj = videoProfile; + this.avRecorder = await this.createAVRecorder(); + if (this.avRecorder === undefined) { + Logger.error(TAG, 'Failed to create the avRecorder.'); + return; + } + await this.prepareAVRecorder(); + let videoSurfaceId = await this.avRecorder.getInputSurface(); + // 创建videoOutPut输出对象 + this.videoOutput = this.createVideoOutputFn(this.cameraManager, this.videoProfileObj, videoSurfaceId); + if (this.videoOutput === undefined) { + Logger.error(TAG, 'Failed to create the video stream.'); + return; + } + } + // 创建cameraInput输出对象 + this.cameraInput = this.createCameraInputFn(this.cameraManager, this.curCameraDevice); + if (this.cameraInput === undefined) { + Logger.error(TAG, 'Failed to create the camera input.'); + return; + } + // 打开相机 + let isOpenSuccess = await this.cameraInputOpenFn(this.cameraInput); + if (!isOpenSuccess) { + Logger.error(TAG, 'Failed to open the camera.'); + return; + } + // 镜头状态回调 + this.onCameraStatusChange(this.cameraManager); + // 监听CameraInput的错误事件 + this.onCameraInputChange(this.cameraInput, this.curCameraDevice); + // 会话流程 + await this.sessionFlowFn(this.cameraManager, this.cameraInput, this.previewOutput, this.photoOutput, + this.videoOutput); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `initCamera fail: ${JSON.stringify(err)}`); + } + } + + /** + * 获取可变焦距范围 + */ + getZoomRatioRange(): Array { + let zoomRatioRange: Array = []; + if (this.session !== undefined) { + zoomRatioRange = this.session.getZoomRatioRange(); + } + return zoomRatioRange; + } + + /** + * 变焦 + */ + setZoomRatioFn(zoomRatio: number): void { + Logger.info(TAG, `setZoomRatioFn value ${zoomRatio}`); + // 获取支持的变焦范围 + try { + let zoomRatioRange = this.getZoomRatioRange(); + Logger.info(TAG, `getZoomRatioRange success: ${JSON.stringify(zoomRatioRange)}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `getZoomRatioRange fail: ${JSON.stringify(err)}`); + } + + try { + this.session?.setZoomRatio(zoomRatio); + Logger.info(TAG, 'setZoomRatioFn success'); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `setZoomRatioFn fail: ${JSON.stringify(err)}`); + } + } + + /** + * 以指定参数触发一次拍照 + */ + async takePicture(): Promise { + Logger.info(TAG, 'takePicture start'); + let cameraDeviceIndex = GlobalContext.get().getT('cameraDeviceIndex'); + let photoSettings: camera.PhotoCaptureSetting = { + quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, + mirror: cameraDeviceIndex ? true : false + }; + await this.photoOutput?.capture(photoSettings); + Logger.info(TAG, 'takePicture end'); + } + + async saveCameraPhoto(asset: photoAccessHelper.PhotoAsset) { + Logger.info('saveCameraPhoto'); + try { + let context = getContext(this); + let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); + let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = + new photoAccessHelper.MediaAssetChangeRequest(asset); + assetChangeRequest.saveCameraPhoto(); + await phAccessHelper.applyChanges(assetChangeRequest); + Logger.info('apply saveCameraPhoto successfully'); + } catch (err) { + Logger.error(`apply saveCameraPhoto failed with error: ${err.code}, ${err.message}`); + } + } + + /** + * 释放会话及其相关参数 + */ + async releaseCamera(): Promise { + Logger.info(TAG, 'releaseCamera is called'); + try { + await this.previewOutput?.release(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `previewOutput release fail: error: ${JSON.stringify(err)}`); + } finally { + this.previewOutput = undefined; + } + try { + await this.photoOutput?.release(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `photoOutput release fail: error: ${JSON.stringify(err)}`); + } finally { + this.photoOutput = undefined; + } + try { + await this.avRecorder?.release(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `avRecorder release fail: error: ${JSON.stringify(err)}`); + } finally { + this.avRecorder = undefined; + } + + try { + await this.videoOutput?.release(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `videoOutput release fail: error: ${JSON.stringify(err)}`); + } finally { + this.videoOutput = undefined; + } + try { + await this.session?.release(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `captureSession release fail: error: ${JSON.stringify(err)}`); + } finally { + this.session = undefined; + } + try { + await this.cameraInput?.close(); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `cameraInput close fail: error: ${JSON.stringify(err)}`); + } finally { + this.cameraInput = undefined; + } + this.offCameraStatusChange(); + Logger.info(TAG, 'releaseCamera success'); + } + + /** + * 获取相机管理器实例 + */ + getCameraManagerFn(): camera.CameraManager | undefined { + if (this.cameraManager) { + return this.cameraManager; + } + let cameraManager: camera.CameraManager | undefined = undefined; + try { + cameraManager = camera.getCameraManager(GlobalContext.get().getCameraSettingContext()); + Logger.info(TAG, `getCameraManager success: ${cameraManager}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `getCameraManager failed: ${JSON.stringify(err)}`); + } + return cameraManager; + } + + /** + * 获取支持指定的相机设备对象 + */ + getSupportedCamerasFn(cameraManager: camera.CameraManager): Array { + let supportedCameras: Array = []; + try { + supportedCameras = cameraManager.getSupportedCameras(); + Logger.info(TAG, `getSupportedCameras success: ${this.cameras}, length: ${this.cameras?.length}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `getSupportedCameras failed: ${JSON.stringify(err)}`); + } + return supportedCameras; + } + + /** + * 创建previewOutput输出对象 + */ + createPreviewOutputFn(cameraManager: camera.CameraManager, previewProfileObj: camera.Profile, + surfaceId: string): camera.PreviewOutput | undefined { + let previewOutput: camera.PreviewOutput | undefined = undefined; + try { + previewOutput = cameraManager.createPreviewOutput(previewProfileObj, surfaceId); + Logger.info(TAG, `createPreviewOutput success: ${previewOutput}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `createPreviewOutput failed: ${JSON.stringify(err)}`); + } + return previewOutput; + } + + /** + * 创建photoOutPut输出对象 + */ + createPhotoOutputFn(cameraManager: camera.CameraManager, + photoProfileObj: camera.Profile): camera.PhotoOutput | undefined { + let photoOutput: camera.PhotoOutput | undefined = undefined; + try { + photoOutput = cameraManager.createPhotoOutput(photoProfileObj); + Logger.info(TAG, `createPhotoOutputFn success: ${photoOutput}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `createPhotoOutputFn failed: ${JSON.stringify(err)}`); + } + return photoOutput; + } + + /** + * 创建videoOutPut输出对象 + */ + createVideoOutputFn(cameraManager: camera.CameraManager, videoProfileObj: camera.VideoProfile, + surfaceId: string): camera.VideoOutput | undefined { + let videoOutput: camera.VideoOutput | undefined = undefined; + try { + videoOutput = cameraManager.createVideoOutput(videoProfileObj, surfaceId); + Logger.info(TAG, `createVideoOutputFn success: ${videoOutput}`); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `createVideoOutputFn failed: ${JSON.stringify(err)}`); + } + return videoOutput; + } + + /** + * 创建cameraInput输出对象 + */ + createCameraInputFn(cameraManager: camera.CameraManager, + cameraDevice: camera.CameraDevice): camera.CameraInput | undefined { + Logger.info(TAG, 'createCameraInputFn is called.'); + let cameraInput: camera.CameraInput | undefined = undefined; + try { + cameraInput = cameraManager.createCameraInput(cameraDevice); + Logger.info(TAG, 'createCameraInputFn success'); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `createCameraInputFn failed: ${JSON.stringify(err)}`); + } + return cameraInput; + } + + /** + * 打开相机 + */ + async cameraInputOpenFn(cameraInput: camera.CameraInput): Promise { + let isOpenSuccess = false; + try { + await cameraInput.open(); + isOpenSuccess = true; + Logger.info(TAG, 'cameraInput open success'); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `createCameraInput failed : ${JSON.stringify(err)}`); + } + return isOpenSuccess; + } + + /** + * 会话流程 + */ + async sessionFlowFn(cameraManager: camera.CameraManager, cameraInput: camera.CameraInput, + previewOutput: camera.PreviewOutput, photoOutput: camera.PhotoOutput | undefined, + videoOutput: camera.VideoOutput | undefined): Promise { + try { + // 创建CaptureSession实例 + if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) { + this.session = cameraManager.createSession(this.curSceneMode) as camera.PhotoSession; + } else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) { + this.session = cameraManager.createSession(this.curSceneMode) as camera.VideoSession; + } + if (this.session === undefined) { + return; + } + this.onSessionErrorChange(this.session); + // 开始配置会话 + this.session.beginConfig(); + // 把CameraInput加入到会话 + this.session.addInput(cameraInput); + // 把previewOutput加入到会话 + this.session.addOutput(previewOutput); + if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) { + if (photoOutput === undefined) { + return; + } + // 拍照监听事件 + this.photoOutputCallBack(photoOutput); + // 把photoOutPut加入到会话 + this.session.addOutput(photoOutput); + } else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) { + if (videoOutput === undefined) { + return; + } + // 把photoOutPut加入到会话 + this.session.addOutput(videoOutput); + } + // 提交配置信息 + await this.session.commitConfig(); + if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) { + this.setVideoStabilizationFn(this.session as camera.VideoSession, camera.VideoStabilizationMode.MIDDLE); + } + this.updateSliderValue(); + this.setFocusMode(camera.FocusMode.FOCUS_MODE_AUTO); + // 开始会话工作 + await this.session.start(); + Logger.info(TAG, 'sessionFlowFn success'); + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `sessionFlowFn fail : ${JSON.stringify(err)}`); + } + } + + setVideoStabilizationFn(session: camera.VideoSession, videoStabilizationMode: camera.VideoStabilizationMode): void { + // 查询是否支持指定的视频防抖模式 + let isVideoStabilizationModeSupported: boolean = session.isVideoStabilizationModeSupported(videoStabilizationMode); + if (isVideoStabilizationModeSupported) { + session.setVideoStabilizationMode(videoStabilizationMode); + } + Logger.info(TAG, 'setVideoStabilizationFn success'); + } + + /** + * 更新滑动条数据 + */ + updateSliderValue(): void { + let zoomRatioRange = this.getZoomRatioRange(); + if (zoomRatioRange.length !== 0) { + let zoomRatioMin = zoomRatioRange[0]; + let zoomRatioMax = zoomRatioRange[1] > Constants.ZOOM_RADIO_MAX ? Constants.ZOOM_RADIO_MAX : zoomRatioRange[1]; + let sliderStep = + zoomRatioRange[1] > Constants.ZOOM_RADIO_MAX ? Constants.ZOOM_RADIO_MAX_STEP : Constants.ZOOM_RADIO_MIN_STEP; + AppStorage.set('sliderValue', { + min: zoomRatioMin, + max: zoomRatioMax, + step: sliderStep + }); + } + } + + /** + * 监听拍照事件 + */ + photoOutputCallBack(photoOutput: camera.PhotoOutput): void { + try { + // 监听拍照开始 + photoOutput.on('captureStartWithInfo', (err: BusinessError, captureStartInfo: camera.CaptureStartInfo): void => { + Logger.info(TAG, `photoOutputCallBack captureStartWithInfo success: ${JSON.stringify(captureStartInfo)}`); + }); + // 监听拍照帧输出捕获 + photoOutput.on('frameShutter', (err: BusinessError, frameShutterInfo: camera.FrameShutterInfo): void => { + Logger.info(TAG, `photoOutputCallBack frameShutter captureId: + ${frameShutterInfo.captureId}, timestamp: ${frameShutterInfo.timestamp}`); + }); + // 监听拍照结束 + photoOutput.on('captureEnd', (err: BusinessError, captureEndInfo: camera.CaptureEndInfo): void => { + Logger.info(TAG, `photoOutputCallBack captureEnd captureId: + ${captureEndInfo.captureId}, frameCount: ${captureEndInfo.frameCount}`); + }); + // 监听拍照异常 + photoOutput.on('error', (data: BusinessError): void => { + Logger.info(TAG, `photoOutPut data: ${JSON.stringify(data)}`); + }); + photoOutput.on('photoAssetAvailable', (err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset) => { + Logger.info(TAG, 'photoAssetAvailable begin'); + if (photoAsset === undefined) { + Logger.error(TAG, 'photoAsset is undefined'); + return; + } + this.handlePhotoAssetCb(photoAsset); + }); + } catch (err) { + Logger.error(TAG, 'photoOutputCallBack error'); + } + } + + /** + * 监听预览事件 + */ + previewOutputCallBack(previewOutput: camera.PreviewOutput): void { + Logger.info(TAG, 'previewOutputCallBack is called'); + try { + previewOutput.on('frameStart', (): void => { + Logger.debug(TAG, 'Preview frame started'); + AppStorage.setOrCreate('frameStart', ++this.frameStartFlag); + }); + previewOutput.on('frameEnd', (): void => { + Logger.debug(TAG, 'Preview frame ended'); + }); + previewOutput.on('error', (previewOutputError: BusinessError): void => { + Logger.info(TAG, `Preview output previewOutputError: ${JSON.stringify(previewOutputError)}`); + }); + } catch (err) { + Logger.error(TAG, 'previewOutputCallBack error'); + } + } + + /** + * 注册相机状态变化的回调函数 + * @param err - 错误信息(如果有) + * @param cameraStatusInfo - 相机状态信息 + * @returns 无返回值 + */ + registerCameraStatusChange(err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo): void { + Logger.info(TAG, `cameraId: ${cameraStatusInfo.camera.cameraId},status: ${cameraStatusInfo.status}`); + } + + /** + * 监听相机状态变化 + * @param cameraManager - 相机管理器对象 + * @returns 无返回值 + */ + onCameraStatusChange(cameraManager: camera.CameraManager): void { + Logger.info(TAG, 'onCameraStatusChange is called'); + try { + cameraManager.on('cameraStatus', this.registerCameraStatusChange); + } catch (error) { + Logger.error(TAG, 'onCameraStatusChange error'); + } + } + + /** + * 停止监听相机状态变化 + * @returns 无返回值 + */ + offCameraStatusChange(): void { + Logger.info(TAG, 'offCameraStatusChange is called'); + this.cameraManager?.off('cameraStatus', this.registerCameraStatusChange); + } + + /** + * 监听相机输入变化 + * @param cameraInput - 相机输入对象 + * @param cameraDevice - 相机设备对象 + * @returns 无返回值 + */ + onCameraInputChange(cameraInput: camera.CameraInput, cameraDevice: camera.CameraDevice): void { + Logger.info(TAG, `onCameraInputChange is called`); + try { + cameraInput.on('error', cameraDevice, (cameraInputError: BusinessError): void => { + Logger.info(TAG, `onCameraInputChange cameraInput error code: ${cameraInputError.code}`); + }); + } catch (error) { + Logger.error(TAG, 'onCameraInputChange error'); + } + } + + /** + * 监听捕获会话错误变化 + * @param session - 相机捕获会话对象 + * @returns 无返回值 + */ + onSessionErrorChange(session: camera.PhotoSession | camera.VideoSession): void { + try { + session.on('error', (captureSessionError: BusinessError): void => { + Logger.info(TAG, + 'onCaptureSessionErrorChange captureSession fail: ' + JSON.stringify(captureSessionError.code)); + }); + } catch (error) { + Logger.error(TAG, 'onCaptureSessionErrorChange error'); + } + } + + /** + * 监听系统压力 + * @param session - 相机捕获会话对象 + * @returns 无返回值 + */ + onSystemPressureLevelChange(session: camera.PhotoSession | camera.VideoSession): void { + try { + session.on('systemPressureLevelChange', (error: BusinessError, sysPressureLevel: camera.SystemPressureLevel): void => { + Logger.info(TAG, 'onSystemPressureLevelChange on, curLevel: ' + + JSON.stringify(sysPressureLevel) + ' ' + JSON.stringify(error)); + }) + } catch (error) { + Logger.error(TAG, 'register systemPressureLevelChange failed: ' + JSON.stringify(error)); + } + } + + // 相机控制器 + isControlCenterSupport(): boolean | undefined{ + if (this.curSceneMode == camera.SceneMode.NORMAL_VIDEO) { + let videoSession = this.session as camera.VideoSession; + return videoSession.isControlCenterSupported(); + } + return false; + } + + // 需要满足前置摄像模式 + enableControlCenter(isEnable: boolean): void { + let isSupport = this.isControlCenterSupport(); + if (isSupport) { + let videoSession = this.session as camera.VideoSession; + let effectTypes = videoSession.getSupportedEffectTypes(); + Logger.info(TAG, `can enableControlCenter, effectTypes count: ${effectTypes.length}, isEnable: ${isEnable}.`); + videoSession.enableControlCenter(isEnable); + } + Logger.error(TAG, `curStatus not supported.`); + } + + // 微距能力 + isMacroSupport(): boolean | undefined{ + return this.session?.isMacroSupported(); + } + + enableMacro(isEnable: boolean): void { + let isSupport = this.isMacroSupport(); + if (isSupport) { + this.session?.enableMacro(isEnable); + Logger.info(TAG, `enableMacro supported, curMode: ${isEnable}.`); + } + Logger.error(TAG, `enableMacro is refuse.`); + } + + async createAVRecorder(): Promise { + let avRecorder: media.AVRecorder | undefined = undefined; + try { + avRecorder = await media.createAVRecorder(); + } catch (error) { + Logger.error(TAG, `createAVRecorder error: ${error}`); + } + return avRecorder; + } + + initFd(): number { + Logger.info(TAG, 'initFd is called'); + let filesDir = getContext().filesDir; + let filePath = filesDir + `/${Date.now()}.mp4`; + AppStorage.setOrCreate('filePath', filePath); + let file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); + return file.fd; + } + + async prepareAVRecorder(): Promise { + Logger.info(TAG, 'prepareAVRecorder is called'); + let fd = this.initFd(); + let videoConfig: media.AVRecorderConfig = { + audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, + videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, + profile: { + audioBitrate: Constants.AUDIO_BITRATE, + audioChannels: Constants.AUDIO_CHANNELS, + audioCodec: media.CodecMimeType.AUDIO_AAC, + audioSampleRate: Constants.AUDIO_SAMPLE_RATE, + fileFormat: media.ContainerFormatType.CFT_MPEG_4, + videoBitrate: Constants.VIDEO_BITRATE, + videoCodec: media.CodecMimeType.VIDEO_AVC, + videoFrameWidth: this.videoProfileObj.size.width, + videoFrameHeight: this.videoProfileObj.size.height, + videoFrameRate: this.videoProfileObj.frameRateRange.max + }, + url: `fd://${fd.toString()}`, + rotation: this.curCameraDevice?.cameraOrientation + }; + Logger.info(TAG, `prepareAVRecorder videoConfig: ${JSON.stringify(videoConfig)}`); + await this.avRecorder?.prepare(videoConfig).catch((err: BusinessError): void => { + Logger.error(TAG, `prepareAVRecorder prepare err: ${JSON.stringify(err)}`); + }); + } + + /** + * 启动录制 + */ + async startVideo(): Promise { + Logger.info(TAG, 'startVideo is called'); + try { + await this.videoOutput?.start(); + await this.avRecorder?.start(); + this.isRecording = true; + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `startVideo err: ${JSON.stringify(err)}`); + } + Logger.info(TAG, 'startVideo End of call'); + } + + /** + * 停止录制 + */ + async stopVideo(): Promise { + Logger.info(TAG, 'stopVideo is called'); + if (!this.isRecording) { + Logger.info(TAG, 'not in recording'); + return; + } + try { + if (this.avRecorder) { + await this.avRecorder.stop(); + } + if (this.videoOutput) { + await this.videoOutput.stop(); + } + this.isRecording = false; + } catch (error) { + let err = error as BusinessError; + Logger.error(TAG, `stopVideo err: ${JSON.stringify(err)}`); + } + Logger.info(TAG, 'stopVideo End of call'); + } + + /** + * 闪关灯 + */ + hasFlashFn(flashMode: camera.FlashMode): void { + // 检测是否有闪关灯 + let hasFlash = this.session?.hasFlash(); + Logger.debug(TAG, `hasFlash success, hasFlash: ${hasFlash}`); + // 检测闪光灯模式是否支持 + let isFlashModeSupported = this.session?.isFlashModeSupported(flashMode); + Logger.debug(TAG, `isFlashModeSupported success, isFlashModeSupported: ${isFlashModeSupported}`); + // 设置闪光灯模式 + if (isFlashModeSupported) { + this.session?.setFlashMode(flashMode); + } + } + + /** + * 焦点 + */ + setFocusPoint(point: camera.Point): void { + // 设置焦点 + this.session?.setFocusPoint(point); + Logger.info(TAG, `setFocusPoint success point: ${JSON.stringify(point)}`); + // 获取当前的焦点 + let nowPoint: camera.Point | undefined = undefined; + nowPoint = this.session?.getFocusPoint(); + Logger.info(TAG, `getFocusPoint success, nowPoint: ${JSON.stringify(nowPoint)}`); + } + + /** + * 曝光区域 + */ + isMeteringPoint(point: camera.Point): void { + // 获取当前曝光模式 + let exposureMode: camera.ExposureMode | undefined = undefined; + exposureMode = this.session?.getExposureMode(); + Logger.info(TAG, `getExposureMode success, exposureMode: ${exposureMode}`); + this.session?.setMeteringPoint(point); + let exposurePoint: camera.Point | undefined = undefined; + exposurePoint = this.session?.getMeteringPoint(); + Logger.info(TAG, `getMeteringPoint exposurePoint: ${JSON.stringify(exposurePoint)}`); + } + + /** + * 曝光补偿 + */ + isExposureBiasRange(exposureBias: number): void { + Logger.debug(TAG, `setExposureBias value ${exposureBias}`); + // 查询曝光补偿范围 + let biasRangeArray: Array | undefined = []; + biasRangeArray = this.session?.getExposureBiasRange(); + Logger.debug(TAG, `getExposureBiasRange success, biasRangeArray: ${JSON.stringify(biasRangeArray)}`); + // 设置曝光补偿 + this.session?.setExposureBias(exposureBias); + } + + /** + * 对焦模式 + */ + setFocusMode(focusMode: camera.FocusMode): void { + // 检测对焦模式是否支持 + Logger.info(TAG, `setFocusMode is called`); + let isSupported = this.session?.isFocusModeSupported(focusMode); + Logger.info(TAG, `setFocusMode isSupported: ${isSupported}`); + // 设置对焦模式 + if (!isSupported) { + return; + } + this.session?.setFocusMode(focusMode); + } + + setWhiteBalanceMode(whiteBalanceMode: camera.WhiteBalanceMode) { + let isSupported: boolean | undefined = false; + isSupported = this.session?.isWhiteBalanceModeSupported(whiteBalanceMode); + if (isSupported) { + this.session?.setWhiteBalanceMode(whiteBalanceMode); + } + let curMode = this.session?.getWhiteBalanceMode(); + if (curMode === whiteBalanceMode) { + console.info(TAG, `setWhiteBalance success, curMode: ${curMode}`); + } else { + console.error(TAG, `setWhiteBalance failed, curMode: ${curMode}, setMode: ${whiteBalanceMode}`); + } + } +} + +export default new CameraService(); \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/pages/EditPage.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/pages/EditPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..142d4c1bf2775a223a432fceec3f3e81c0545947 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/pages/EditPage.ets @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { image } from '@kit.ImageKit'; +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { router } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { camera } from '@kit.CameraKit'; +import { fileIo } from '@kit.CoreFileKit'; +import Logger from '../common/utils/Logger'; +import { GlobalContext } from '../common/utils/GlobalContext'; +import { Constants } from '../common/Constants'; + +const TAG: string = 'EditPage'; + +class RequestImageParams { + context: Context | undefined = undefined; + photoAsset: photoAccessHelper.PhotoAsset | undefined = undefined; + callback: Function = () => { + }; +} + +@Entry +@Component +struct EditPage { + @State createPixelMapState: boolean = false; + @State curPixelMap: image.PixelMap | undefined = undefined; + @State photoUri: string = ''; + private sceneMode: camera.SceneMode = camera.SceneMode.NORMAL_PHOTO; + private backIconLayoutWeight = 1; + private textLayoutWeight = 8; + private videoController: VideoController | undefined = undefined; + private controls: boolean = true; + private videoUri: string | undefined = ''; + photoBufferCallback: (arrayBuffer: ArrayBuffer) => void = (arrayBuffer: ArrayBuffer) => { + Logger.info(TAG, 'photoBufferCallback is called'); + let imageSource = image.createImageSource(arrayBuffer); + imageSource.createPixelMap((err: BusinessError, data: image.PixelMap) => { + Logger.info(TAG, 'createPixelMap is called'); + this.curPixelMap = data; + }); + }; + + requestImage(requestImageParams: RequestImageParams): void { + try { + class MediaDataHandler implements photoAccessHelper.MediaAssetDataHandler { + onDataPrepared(data: ArrayBuffer, map: Map): void { + Logger.info(TAG, 'onDataPrepared begin'); + Logger.info(TAG, `onDataPrepared quality: ${map['quality']}`); + requestImageParams.callback(data); + Logger.info(TAG, 'onDataPrepared end'); + } + }; + let requestOptions: photoAccessHelper.RequestOptions = { + deliveryMode: photoAccessHelper.DeliveryMode.BALANCE_MODE, + }; + const handler = new MediaDataHandler(); + photoAccessHelper.MediaAssetManager.requestImageData(requestImageParams.context, requestImageParams.photoAsset, + requestOptions, handler); + } catch (error) { + Logger.error(TAG, `Failed in requestImage, error code: ${error.code}`); + } + } + + aboutToAppear() { + Logger.info(TAG, 'aboutToAppear begin'); + this.sceneMode = GlobalContext.get().getT('sceneMode'); + if (this.sceneMode === camera.SceneMode.NORMAL_PHOTO) { + let curPhotoAsset = GlobalContext.get().getT('photoAsset'); + this.photoUri = curPhotoAsset.uri; + let requestImageParams: RequestImageParams = { + context: getContext(), + photoAsset: curPhotoAsset, + callback: this.photoBufferCallback + }; + setTimeout(() => { + this.requestImage(requestImageParams); + }, 500); + Logger.info(TAG, `aboutToAppear photoUri: ${this.photoUri}`); + } else if (this.sceneMode === camera.SceneMode.NORMAL_VIDEO) { + this.videoController = new VideoController(); + this.videoUri = 'file://' + AppStorage.get('filePath'); + Logger.info(TAG, `aboutToAppear videoUri: ${this.videoUri}`); + } + } + + build() { + Column() { + Column() { + Image($r('app.media.ic_public_back')) + .objectFit(ImageFit.Fill) + .onClick(() => { + Logger.info(TAG, 'back onClick'); + // delete video file when router back + fileIo.unlink(this.videoUri); + router.back(); + }) + .width(Constants.BACK_ICON_SIZE) + .height(Constants.BACK_ICON_SIZE) + } + .padding({ left: Constants.BACK_ICON_MARGIN }) + .width(Constants.FULL_PERCENT) + .layoutWeight(this.backIconLayoutWeight) + .alignItems(HorizontalAlign.Start) + .justifyContent(FlexAlign.Center) + + Column() { + if (this.sceneMode === camera.SceneMode.NORMAL_PHOTO) { + Image(this.curPixelMap) + .objectFit(ImageFit.Contain) + .width(Constants.FULL_PERCENT) + .height(Constants.EIGHTY_PERCENT) + } else if (this.sceneMode === camera.SceneMode.NORMAL_VIDEO) { + Video({ + src: this.videoUri, + controller: this.videoController + }) + .width(Constants.FULL_PERCENT) + .height(Constants.EIGHTY_PERCENT) + .controls(this.controls) + .objectFit(ImageFit.Contain) + .onClick(() => { + if (this.controls) { + this.videoController?.start(); + } else { + this.videoController?.stop(); + } + this.controls = !this.controls; + }) + .onFinish(() => { + this.controls = true; + }) + } + } + .width(Constants.FULL_PERCENT) + .layoutWeight(this.textLayoutWeight) + } + .width(Constants.FULL_PERCENT) + .height(Constants.FULL_PERCENT) + .backgroundColor($r('app.color.dialog_background_color')) + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/pages/Index.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..11ac72e5e46687b5bea947bc65528239716d52b5 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 '@kit.CameraKit'; +import { image } from '@kit.ImageKit'; +import { curves } from '@kit.ArkUI'; +import { Constants } from '../common/Constants'; +import { BlurAnimateUtil } from '../common/utils/BlurAnimateUtil'; +import { GlobalContext } from '../common/utils/GlobalContext'; +import Logger from '../common/utils/Logger'; +import CameraService from '../mode/CameraService'; +import { ModeComponent } from '../views/ModeComponent'; +import { SlideComponent } from '../views/SlideComponent'; +import { FocusAreaComponent } from '../views/FocusAreaComponent'; +import { FocusComponent } from '../views/FocusComponent'; +import { FlashingLightComponent } from '../views/FlashingLightComponent'; + + +const TAG = 'Index'; + +@Entry +@Component +struct Index { + // 主页面是否显示 + @StorageLink('isShow') isShow: boolean = false; + @StorageLink('isOpenEditPage') isOpenEditPage: boolean = false; + // Flash Mode + @State flashMode: camera.FlashMode = camera.FlashMode.FLASH_MODE_CLOSE; + @State focusPointBol: boolean = false; + // 曝光区域手指点击坐标 + @State focusPointVal: Array = [0, 0]; + @State xComponentAspectRatio: number = 1; + private mXComponentController: XComponentController = new XComponentController(); + private defaultCameraDeviceIndex = 0; + private surfaceId = ''; + // 模糊动效 + @State isShowBlur: boolean = false; + @State isShowBlack: boolean = false; + @StorageLink('modeChange') @Watch('onModeChange') modeChangeFlag: number = 0; + @StorageLink('switchCamera') @Watch('onSwitchCamera') switchCameraFlag: number = 0; + @StorageLink('frameStart') @Watch('onFrameStart') frameStartFlag: number = 0; + @StorageLink('captureClick') @Watch('onCaptureClick') captureClickFlag: number = 0; + @StorageLink('surfaceShot') screenshotPixelMap: image.PixelMap | undefined = undefined; // 预览流截图 + @StorageLink('curPosition') curPosition: number = 0; // 当前镜头前后置状态 + @State shotImgBlur: number = 0; + @State shotImgOpacity: number = 1; + @State shotImgScale: ScaleOptions = { x: 1, y: 1 }; + @State shotImgRotation: RotateOptions = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_0 } + @State flashBlackOpacity: number = 1; + + aboutToAppear(): void { + Logger.info(TAG, 'aboutToAppear'); + } + + async aboutToDisAppear(): Promise { + Logger.info(TAG, 'aboutToDisAppear'); + this.flashMode = camera.FlashMode.FLASH_MODE_CLOSE; + await CameraService.releaseCamera(); + } + + async onPageShow(): Promise { + Logger.info(TAG, 'onPageShow'); + if (this.surfaceId !== '' && !this.isOpenEditPage) { + await CameraService.initCamera(this.surfaceId, GlobalContext.get().getT('cameraDeviceIndex')); + } + this.isOpenEditPage = false; + } + + async onPageHide(): Promise { + Logger.info(TAG, 'onPageHide'); + } + + /** + * 显示模糊动效,模式切换触发 + */ + private async showBlurAnim() { + Logger.info(TAG, 'showBlurAnim E'); + // 获取已完成的surface截图 + let shotPixel = BlurAnimateUtil.getSurfaceShot(); + // 后置 + if (this.curPosition === 0) { + Logger.info(TAG, 'showBlurAnim BACK'); + // 直板机后置截图旋转补偿90° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_90); + // 直板机后置截图初始翻转0° + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_0 }; + } else { + Logger.info(TAG, 'showBlurAnim FRONT'); + // 直板机前置截图旋转补偿270° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_270); + // 直板机前置截图镜像补偿 + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_180 }; + } + this.screenshotPixelMap = shotPixel; + // 初始化动效参数 + this.shotImgBlur = 0; // 无模糊 + this.shotImgOpacity = 1; // 不透明 + // 触发页面渲染 + this.isShowBlur = true; + animateToImmediately( + { + duration: BlurAnimateUtil.SHOW_BLUR_DURATION, + curve: Curve.Friction, + onFinish: async () => { + Logger.info(TAG, 'showBlurAnim X'); + } + }, + () => { + // 截图模糊度变化动效 + this.shotImgBlur = BlurAnimateUtil.ANIM_MODE_SWITCH_BLUR; + } + ); + } + + /** + * 隐藏模糊动效,预览流首帧回调触发 + */ + private hideBlurAnim(): void { + this.isShowBlack = false; + Logger.info(TAG, 'hideBlurAnim E'); + animateToImmediately({ + duration: BlurAnimateUtil.HIDE_BLUR_DURATION, + curve: Curve.FastOutSlowIn, + onFinish: () => { + // 模糊组件下树 + this.isShowBlur = false; + this.shotImgBlur = 0; + this.shotImgOpacity = 1; + Logger.info(TAG, 'hideBlurAnim X'); + } + }, () => { + // 截图透明度变化动效 + this.shotImgOpacity = 0; + }); + } + + /** + * 先向外翻转90°,前后置切换触发 + */ + private async rotateFirstAnim() { + Logger.info(TAG, 'rotateFirstAnim E'); + // 获取已完成的surface截图 + let shotPixel = BlurAnimateUtil.getSurfaceShot(); + // 后置切前置 + if (this.curPosition === 1) { + Logger.info(TAG, 'rotateFirstAnim BACK'); + // 直板机后置切前置截图旋转补偿90° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_90); // Image Kit提供的旋转,用于处理图片本身的旋转 + // 直板机后置切前置截图初始翻转0° + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_0 }; + } else { + Logger.info(TAG, 'rotateFirstAnim FRONT'); + // 直板机前置切后置截图旋转补偿270° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_270); + // 直板机前置切后置截图初始翻转180° + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_180 }; + } + this.screenshotPixelMap = shotPixel; + // 触发页面渲染 + this.isShowBlack = true; + this.isShowBlur = true; + animateToImmediately( + { + duration: BlurAnimateUtil.ROTATION_DURATION, + delay: BlurAnimateUtil.FLIP_DELAY, // 时延保证组件缩放模糊动效先行,再翻转后视觉效果更好 + curve: curves.cubicBezierCurve(0.20, 0.00, 0.83, 1.00), + onFinish: () => { + Logger.info(TAG, 'rotateFirstAnim X'); + // 在onFinish后触发二段旋转 + this.rotateSecondAnim(); + } + }, + () => { + // 截图向翻转动效 + if (this.curPosition === 1) { + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_90 }; + } else { + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_270 }; + } + } + ) + } + + /** + * 再向内翻转90° + */ + async rotateSecondAnim() { + Logger.info(TAG, 'rotateSecondAnim E'); + // 获取已完成的surface截图 + let shotPixel = BlurAnimateUtil.getSurfaceShot(); + // 后置 + if (this.curPosition === 1) { + // 直板机后置镜头旋转补偿90° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_90); // Image Kit提供的旋转,用于处理图片本身的旋转 + // 瞬时调整为-90°,保证二段旋转后,图片不是镜像的 + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_MINUS_90 }; + } else { // 前置 + // 直板机前置截图旋转补偿270° + await shotPixel.rotate(BlurAnimateUtil.IMG_ROTATE_ANGLE_270); + // 直板机前置截图镜像补偿 + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_180 }; + } + this.screenshotPixelMap = shotPixel; + + animateToImmediately( + { + duration: BlurAnimateUtil.ROTATION_DURATION, + curve: curves.cubicBezierCurve(0.17, 0.00, 0.20, 1.00), + onFinish: () => { + Logger.info(TAG, 'rotateSecondAnim X'); + } + }, + () => { + // 截图翻转为初始状态 + if (this.curPosition === 1) { + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_0 }; + } else { + this.shotImgRotation = { y: BlurAnimateUtil.ROTATE_AXIS, angle: BlurAnimateUtil.IMG_FLIP_ANGLE_180 }; + } + } + ) + } + + /** + * 向外翻转90°同时 + */ + blurFirstAnim() { + Logger.info(TAG, 'blurFirstAnim E'); + // 初始化动效参数 + this.shotImgBlur = 0; //无模糊 + this.shotImgOpacity = 1; //不透明 + this.shotImgScale = { x: 1, y: 1 }; + animateToImmediately( + { + duration: BlurAnimateUtil.ROTATION_DURATION, + curve: Curve.Sharp, + onFinish: () => { + Logger.info(TAG, 'blurFirstAnim X'); + this.blurSecondAnim(); + } + }, + () => { + // 截图模糊度变化动效 + this.shotImgBlur = BlurAnimateUtil.ANIM_MODE_SWITCH_BLUR; + // 截图比例动效 + this.shotImgScale = { x: BlurAnimateUtil.IMG_SCALE, y: BlurAnimateUtil.IMG_SCALE }; + } + ); + } + + /** + * 向内翻转90°同时 + */ + blurSecondAnim() { + Logger.info(TAG, 'blurSecondAnim E'); + animateToImmediately( + { + duration: BlurAnimateUtil.ROTATION_DURATION, + curve: Curve.Sharp, + onFinish: () => { + Logger.info(TAG, 'blurSecondAnim X'); + } + }, + () => { + this.shotImgScale = { x: 1, y: 1 }; + } + ) + } + + /** + * 闪黑动效,拍照触发 + */ + private flashBlackAnim() { + Logger.info(TAG, 'flashBlackAnim E'); + this.flashBlackOpacity = 1; + this.isShowBlack = true; + animateToImmediately({ + curve: curves.interpolatingSpring(1, 1, 410, 38), + delay: 50, + onFinish: () => { + this.isShowBlack = false; + this.flashBlackOpacity = 1; + Logger.info(TAG, 'flashBlackAnim X'); + } + }, () => { + this.flashBlackOpacity = 0; + }) + } + + onModeChange(): void { + Logger.info(TAG, 'onModeChange'); + this.showBlurAnim(); + } + + onSwitchCamera(): void { + Logger.info(TAG, 'onSwitchCamera'); + this.blurFirstAnim(); + this.rotateFirstAnim(); + } + + onFrameStart(): void { + Logger.info(TAG, 'onFrameStart'); + this.hideBlurAnim(); + } + + onCaptureClick(): void { + Logger.info(TAG, 'onCaptureClick'); + this.flashBlackAnim(); + } + + build() { + Stack() { + if (this.isShow) { + XComponent({ + id: 'componentId', + type: 'surface', + controller: this.mXComponentController + }) + .onLoad(async () => { + Logger.info(TAG, 'onLoad is called'); + this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); + GlobalContext.get().setObject('cameraDeviceIndex', this.defaultCameraDeviceIndex); + GlobalContext.get().setObject('xComponentSurfaceId', this.surfaceId); + let surfaceRect: SurfaceRect = { + surfaceWidth: Constants.X_COMPONENT_SURFACE_HEIGHT, + surfaceHeight: Constants.X_COMPONENT_SURFACE_WIDTH + }; + this.mXComponentController.setXComponentSurfaceRect(surfaceRect); + Logger.info(TAG, `onLoad surfaceId: ${this.surfaceId}`); + await CameraService.initCamera(this.surfaceId, this.defaultCameraDeviceIndex); + })// The width and height of the surface are opposite to those of the Xcomponent. + .width(px2vp(Constants.X_COMPONENT_SURFACE_HEIGHT)) + .height(px2vp(Constants.X_COMPONENT_SURFACE_WIDTH)) + } + + // 拍照闪黑及前后置切换时显示,用来遮挡XComponent组件 + if (this.isShowBlack) { + Column() + .key('black') + .width(px2vp(Constants.X_COMPONENT_SURFACE_HEIGHT)) + .height(px2vp(Constants.X_COMPONENT_SURFACE_WIDTH)) + .backgroundColor(Color.Black) + .opacity(this.flashBlackOpacity) + } + + + if (this.isShowBlur) { + Column() { + Image(this.screenshotPixelMap) + .blur(this.shotImgBlur) + .opacity(this.shotImgOpacity) + .rotate(this.shotImgRotation)// ArkUI提供的旋转,用于组件沿指定坐标系进行旋转 + .scale(this.shotImgScale) + .width(px2vp(Constants.X_COMPONENT_SURFACE_HEIGHT)) + .height(px2vp(Constants.X_COMPONENT_SURFACE_WIDTH)) + .syncLoad(true) + } + .width(px2vp(Constants.X_COMPONENT_SURFACE_HEIGHT)) + .height(px2vp(Constants.X_COMPONENT_SURFACE_WIDTH)) + } + + // 曝光框和对焦框 + FocusComponent({ + focusPointBol: $focusPointBol, + focusPointVal: $focusPointVal + }) + + // 曝光对焦手指点击区域 + FocusAreaComponent({ + focusPointBol: $focusPointBol, + focusPointVal: $focusPointVal, + xComponentWidth: px2vp(Constants.X_COMPONENT_SURFACE_HEIGHT), + xComponentHeight: px2vp(Constants.X_COMPONENT_SURFACE_WIDTH) + }) + + SlideComponent() + + // 拍照 + ModeComponent() + + Row({ space: Constants.ROW_SPACE_24 }) { + // 闪光灯 + FlashingLightComponent({ + flashMode: $flashMode + }) + } + .margin({ left: Constants.CAPTURE_BUTTON_COLUMN_MARGIN }) + .alignItems(VerticalAlign.Top) + .justifyContent(FlexAlign.Start) + .position({ + x: Constants.FLASH_POSITION_X, + y: Constants.FLASH_POSITION_Y + }) + } + .size({ + width: Constants.FULL_PERCENT, + height: Constants.FULL_PERCENT + }) + .backgroundColor(Color.Black) + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/views/FlashingLightComponent.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FlashingLightComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..f7c8b16472f41ddd98097492b9504c1d2f164c32 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FlashingLightComponent.ets @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 '@kit.CameraKit'; +import Logger from '../common/utils/Logger'; +import CameraService from '../mode/CameraService'; + +const TAG: string = 'FlashingLightComponent'; + +interface WhiteBalance { + auto: string; + manual: string; + cloudy: string; + white: string; + fluorescence: string; + sunlight: string; +} + +// 闪关灯页面 +@Component +export struct FlashingLightComponent { + // 闪光灯模式 + @Link @Watch('flashModeChange') flashMode: camera.FlashMode; + @State flashIconResource: Resource = $r('app.media.ic_camera_public_flash_off'); + + @State isControlEnable: boolean = false; + @State isMacroEnable: boolean = false; + + // Page judgment + @State showOptions: boolean = false; + @State firstBol: boolean = true; + // WhiteBalance mode + @State whiteBalanceNum: number = 0; + private whiteBalance: WhiteBalance = { + auto: '自动', + manual: '手动', + cloudy: '阴天', + white: '白炽光', + fluorescence: '荧光', + sunlight: '日光' + }; + + // Apply white balance setting + applyWhiteBalanceSetting(whiteBalance: number) { + this.whiteBalanceNum = whiteBalance; + this.showOptions = false; + + CameraService.setWhiteBalanceMode(whiteBalance); + console.info(`white balance set to ${whiteBalance}`); + } + + // Return to selected image + getTextDefault() { + if (this.whiteBalanceNum == 0) { + return this.whiteBalance.auto; + } + if (this.whiteBalanceNum == 1) { + return this.whiteBalance.cloudy; + } + if (this.whiteBalanceNum == 2) { + return this.whiteBalance.white; + } + if (this.whiteBalanceNum == 3) { + return this.whiteBalance.fluorescence; + } + if (this.whiteBalanceNum == 4) { + return this.whiteBalance.sunlight; + } + if (this.whiteBalanceNum == 5) { + return this.whiteBalance.manual; + } + return; + } + + + flashModeChange(): void { + switch (this.flashMode) { + case camera.FlashMode.FLASH_MODE_OPEN: + this.flashIconResource = $r('app.media.ic_camera_public_flash_on'); + break; + case camera.FlashMode.FLASH_MODE_AUTO: + this.flashIconResource = $r('app.media.ic_camera_public_flash_auto'); + break; + case camera.FlashMode.FLASH_MODE_ALWAYS_OPEN: + this.flashIconResource = $r('app.media.flash_always_on'); + break; + case camera.FlashMode.FLASH_MODE_CLOSE: + default: + this.flashIconResource = $r('app.media.ic_camera_public_flash_off'); + } + } + + build() { + Row() { + Row() { + Button() { + Image(this.flashIconResource) + .width($r('app.string.200px')) + .height($r('app.string.200px')) + .fillColor($r('app.color.white')) + } + .width($r('app.string.200px')) + .height($r('app.string.200px')) + .backgroundColor($r('app.color.flash_background_color')) + .borderRadius($r('app.string.50px')) + .onClick(() => { + this.flashMode = (this.flashMode + 1) % 4; + Logger.info(TAG, `flashMode: ${this.flashMode}`); + CameraService.hasFlashFn(this.flashMode); + }) + } + + Button() { + Text('相机控制器') + } + .width(80) + .height(60) + .background($r(this.isControlEnable? 'app.color.white' : 'app.color.flash_background_color')) + .onClick(() => { + if (CameraService.isControlCenterSupport()) { + this.isControlEnable = !this.isControlEnable; + CameraService.enableControlCenter(this.isControlEnable); + } else { + this.isControlEnable = false; + } + }) + .position({x: "20%", y: "0%"}) + + Button() { + Text('微距') + } + .width(60) + .height(60) + .background($r(this.isMacroEnable? 'app.color.white' : 'app.color.flash_background_color')) + .onClick(() => { + if (CameraService.isMacroSupport()) { + this.isMacroEnable = !this.isMacroEnable; + CameraService.enableMacro(this.isMacroEnable); + } else { + this.isMacroEnable = false; + } + }) + .position({x: "50%", y: "0%"}) + + // WhiteBalanceMode + Column() { + // 主按钮 + Text('白平衡') + .fontSize(16) + .fontColor('#FFFFFF') + .backgroundColor(Color.Transparent) + .borderRadius('20px') + .textAlign(TextAlign.Center) + .width('20%') + .height('40px') + .onClick(() => { + this.showOptions = !this.showOptions; + }) + // 选项面板 - 垂直排列在主按钮下方 + if (this.showOptions) { + Column() { + Text('自动') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 0 ? '#FFFFFF' : '#CCCCCC') + .backgroundColor(Color.Transparent) + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(0); + }) + .margin({ bottom: '8px' }) + + Text('阴天') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 1 ? '#FFFFFF' : '#CCCCCC') + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(1); + }) + + Text('白炽光') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 2 ? '#FFFFFF' : '#CCCCCC') + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(2); + }) + + Text('荧光') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 3 ? '#FFFFFF' : '#CCCCCC') + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(3); + }) + + Text('日光') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 4 ? '#FFFFFF' : '#CCCCCC') + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(4); + }) + + Text('手动') + .fontSize(16) + .fontColor(this.whiteBalanceNum === 5 ? '#FFFFFF' : '#CCCCCC') + .borderRadius('20px') + .textAlign(TextAlign.Center) + .margin({ top: '2px' }) + .width('100%') + .height('60px') + .onClick(() => { + this.firstBol = !this.firstBol; + this.applyWhiteBalanceSetting(5); + }) + } + .backgroundColor('rgba(0,0,0,0.3)') + .borderRadius('25px') + .padding({ top: '12px', bottom: '12px' }) + .margin({ top: '8px' }) // 与主按钮的间距 + .width('160px') + .height('300px') + .alignItems(HorizontalAlign.Center) + .zIndex(999) + .shadow({ radius: 10, color: '#00000030', offsetX: 0, offsetY: 2 }) + } + } + .position({ x: '70%', y: '2.5%' }) + .id('WhiteBalanceButton') + } + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusAreaComponent.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusAreaComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..e46527d9519b3c3d21b8279609381f1f25e4128a --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusAreaComponent.ets @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 Logger from '../common/utils/Logger'; +import CameraService from '../mode/CameraService'; + +const TAG: string = 'FocusAreaComponent'; + +// 对焦区域 +@Component +export struct FocusAreaComponent { + @Link focusPointBol: boolean; + @Link focusPointVal: Array; + @Prop xComponentWidth: number; + @Prop xComponentHeight: number; + // 对焦区域显示框定时器 + private areaTimer: number = -1; + private focusFrameDisplayDuration: number = 3500; + + build() { + Row() { + } + .width(this.xComponentWidth) + .height(this.xComponentHeight) + .opacity(1) + .onTouch((e: TouchEvent) => { + if (e.type === TouchType.Down) { + this.focusPointBol = true; + this.focusPointVal[0] = e.touches[0].windowX; + this.focusPointVal[1] = e.touches[0].windowY; + // 归一化焦点。 设置的焦点与相机sensor角度和窗口方向有关(相机sensor角度可通过CameraDevice的cameraOrientation属性获取),下面焦点是以竖屏窗口,相机sensor角度为90度场景下的焦点设置 + CameraService.setFocusPoint({ + x: e.touches[0].y / this.xComponentHeight, + y: 1 - (e.touches[0].x / this.xComponentWidth) + }); + } + if (e.type === TouchType.Up) { + if (this.areaTimer) { + clearTimeout(this.areaTimer); + } + this.areaTimer = setTimeout(() => { + this.focusPointBol = false; + }, this.focusFrameDisplayDuration); + } + }) + .onClick((event: ClickEvent) => { + Logger.info(TAG, 'onClick is called'); + }) + } +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusComponent.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..1f9b3fb890d97d94a0aaec848c0c158adbf7c75f --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/views/FocusComponent.ets @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +// 曝光选择 +@Component +export struct FocusComponent { + @Link focusPointBol: boolean; + @Link focusPointVal: Array; + private mBorderWidth = 1.6; + private mBorderRadius = 10; + private mRowSize = 40; + private mFocusPoint = 60; + private focusFrameSize = 120; + + build() { + if (this.focusPointBol) { + Row() { + // 对焦框 + Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) { + Flex({ justifyContent: FlexAlign.SpaceBetween }) { + Row() { + } + .border({ + width: { + left: this.mBorderWidth, + top: this.mBorderWidth + }, + color: Color.White, + radius: { topLeft: this.mBorderRadius } + }) + .size({ width: this.mRowSize, height: this.mRowSize }) + + Row() { + } + .border({ + width: { + right: this.mBorderWidth, + top: this.mBorderWidth + }, + color: Color.White, + radius: { topRight: this.mBorderRadius } + }) + .size({ width: this.mRowSize, height: this.mRowSize }) + } + + Flex({ justifyContent: FlexAlign.SpaceBetween }) { + Row() { + } + .border({ + width: { + left: this.mBorderWidth, + bottom: this.mBorderWidth + }, + color: Color.White, + radius: { bottomLeft: this.mBorderRadius } + }) + .size({ width: this.mRowSize, height: this.mRowSize }) + + Row() { + } + .border({ + width: { + right: this.mBorderWidth, + bottom: this.mBorderWidth + }, + color: Color.White, + radius: { bottomRight: this.mBorderRadius } + }) + .size({ width: this.mRowSize, height: this.mRowSize }) + } + } + .width(this.focusFrameSize) + .height(this.focusFrameSize) + .position({ + x: this.focusPointVal[0] - this.mFocusPoint, + y: this.focusPointVal[1] - this.mFocusPoint + }) + } + .zIndex(1) + } + } +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/views/ModeComponent.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/views/ModeComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..6ba4ece5ea24dbcfe383c365cc56eff7a2b4fb6c --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/views/ModeComponent.ets @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 { router } from '@kit.ArkUI'; +import { camera } from '@kit.CameraKit'; +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import CameraService from '../mode/CameraService'; +import Logger from '../common/utils/Logger'; +import { GlobalContext } from '../common/utils/GlobalContext'; +import { Constants } from '../common/Constants'; +import { BlurAnimateUtil } from '../common/utils/BlurAnimateUtil'; + +const TAG: string = 'ModeComponent'; + +@Component +export struct ModeComponent { + @StorageLink('isOpenEditPage') @Watch('changePageState') isOpenEditPage: boolean = false; + @State sceneMode: camera.SceneMode = camera.SceneMode.NORMAL_PHOTO; + @State isRecording: boolean = false; + modeChangeFlag: number = 0; + switchCameraFlag: number = 0; + captureClickFlag: number = 0; + + changePageState(): void { + if (this.isOpenEditPage) { + this.onJumpClick(); + } + } + + aboutToAppear(): void { + Logger.info(TAG, 'aboutToAppear'); + CameraService.setSavePictureCallback(this.handleSavePicture); + } + + handleSavePicture = (photoAsset: photoAccessHelper.PhotoAsset): void => { + Logger.info(TAG, 'handleSavePicture'); + this.setImageInfo(photoAsset); + CameraService.saveCameraPhoto(photoAsset); + // AppStorage.set('isOpenEditPage', true); + Logger.info(TAG, 'setImageInfo end'); + } + + setImageInfo(photoAsset: photoAccessHelper.PhotoAsset): void { + Logger.info(TAG, 'setImageInfo'); + GlobalContext.get().setObject('photoAsset', photoAsset); + } + + onJumpClick(): void { + GlobalContext.get().setObject('sceneMode', this.sceneMode); + // 目标url + router.pushUrl({ url: 'pages/EditPage' }, router.RouterMode.Single, (err) => { + if (err) { + Logger.error(TAG, `Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + Logger.info(TAG, 'Invoke pushUrl succeeded.'); + }); + } + + build() { + Column() { + Row({ space: Constants.COLUMN_SPACE_24 }) { + Column() { + Text('拍照') + .fontSize(Constants.FONT_SIZE_14) + .fontColor(Color.White) + } + .width(Constants.CAPTURE_COLUMN_WIDTH) + .backgroundColor(this.sceneMode === camera.SceneMode.NORMAL_PHOTO ? $r('app.color.theme_color') : '') + .borderRadius(Constants.BORDER_RADIUS_14) + .onClick(async () => { + if (this.sceneMode === camera.SceneMode.NORMAL_PHOTO) { + return; + } + this.sceneMode = camera.SceneMode.NORMAL_PHOTO; + CameraService.setSceneMode(this.sceneMode); + let cameraDeviceIndex = GlobalContext.get().getT('cameraDeviceIndex'); + let surfaceId = GlobalContext.get().getT('xComponentSurfaceId'); + Logger.info(TAG, 'surface shot E'); + await BlurAnimateUtil.doSurfaceShot(surfaceId); + Logger.info(TAG, 'surface shot X'); + AppStorage.setOrCreate('modeChange', ++this.modeChangeFlag); + Logger.info(TAG, 'switch to photo E'); + await CameraService.initCamera(surfaceId, cameraDeviceIndex); + Logger.info(TAG, 'switch to photo X'); + }) + + // 录像 + Column() { + Text('录像') + .fontSize(Constants.FONT_SIZE_14) + .fontColor(Color.White) + } + .width(Constants.CAPTURE_COLUMN_WIDTH) + .backgroundColor(this.sceneMode === camera.SceneMode.NORMAL_VIDEO ? $r('app.color.theme_color') : '') + .borderRadius(Constants.BORDER_RADIUS_14) + .onClick(async () => { + if (this.sceneMode === camera.SceneMode.NORMAL_VIDEO) { + return; + } + this.sceneMode = camera.SceneMode.NORMAL_VIDEO; + CameraService.setSceneMode(this.sceneMode); + let cameraDeviceIndex = GlobalContext.get().getT('cameraDeviceIndex'); + let surfaceId = GlobalContext.get().getT('xComponentSurfaceId'); + Logger.info(TAG, 'surface shot E'); + await BlurAnimateUtil.doSurfaceShot(surfaceId); + Logger.info(TAG, 'surface shot X'); + AppStorage.setOrCreate('modeChange', ++this.modeChangeFlag); + Logger.info(TAG, 'switch to video E'); + await CameraService.initCamera(surfaceId, cameraDeviceIndex); + Logger.info(TAG, 'switch to video X'); + }) + } + .height(Constants.CAPTURE_ROW_HEIGHT) + .width(Constants.FULL_PERCENT) + .justifyContent(FlexAlign.Center) + .alignItems(VerticalAlign.Center) + + Row() { + Column() { + } + .width($r('app.string.200px')) + + // 拍照-录像 按键 + Column() { + if (!this.isRecording) { + Row() { + Button() { + Text() + .width($r('app.string.120px')) + .height($r('app.string.120px')) + .borderRadius($r('app.string.40px')) + .backgroundColor(this.sceneMode === camera.SceneMode.NORMAL_VIDEO ? + $r('app.color.theme_color') : Color.White) + } + .id('Capture') + .border({ + width: Constants.CAPTURE_BUTTON_BORDER_WIDTH, + color: $r('app.color.border_color'), + radius: Constants.CAPTURE_BUTTON_BORDER_RADIUS + }) + .width($r('app.string.200px')) + .height($r('app.string.200px')) + .backgroundColor(Color.Black) + .onClick(async () => { + if (this.sceneMode === camera.SceneMode.NORMAL_PHOTO) { + Logger.info('capture onClick'); + AppStorage.setOrCreate('captureClick', ++this.captureClickFlag); + await CameraService.takePicture(); + } else { + await CameraService.startVideo(); + this.isRecording = true; + } + }) + } + } else { + Row() { + // 录像停止键 + Button() { + Image($r('app.media.ic_camera_video_close')) + .size({ width: Constants.IMAGE_SIZE, height: Constants.IMAGE_SIZE }) + } + .id('VideoStopButton') + .width($r('app.string.120px')) + .height($r('app.string.120px')) + .backgroundColor($r('app.color.theme_color')) + .onClick(() => { + this.isRecording = !this.isRecording; + CameraService.stopVideo().then(() => { + this.isOpenEditPage = true; + Logger.info(TAG, 'stopVideo success'); + }) + }) + } + .width($r('app.string.200px')) + .height($r('app.string.200px')) + .borderRadius($r('app.string.60px')) + .backgroundColor($r('app.color.theme_color')) + .justifyContent(FlexAlign.SpaceAround) + } + } + + // 前后置摄像头切换 + Column() { + Row() { + Button() { + Image($r('app.media.switch_camera')) + .width($r('app.string.120px')) + .height($r('app.string.120px')) + } + .width($r('app.string.200px')) + .height($r('app.string.200px')) + .backgroundColor($r('app.color.flash_background_color')) + .borderRadius($r('app.string.40px')) + .onClick(async () => { + let cameraDeviceIndex = GlobalContext.get().getT('cameraDeviceIndex'); + let surfaceId = GlobalContext.get().getT('xComponentSurfaceId'); + cameraDeviceIndex ? cameraDeviceIndex = 0 : cameraDeviceIndex = 1; + GlobalContext.get().setObject('cameraDeviceIndex', cameraDeviceIndex); + AppStorage.setOrCreate('curPosition', cameraDeviceIndex); + Logger.info(TAG, 'surface shot E'); + await BlurAnimateUtil.doSurfaceShot(surfaceId); + Logger.info(TAG, 'surface shot X'); + AppStorage.setOrCreate('switchCamera', ++this.switchCameraFlag); + await CameraService.initCamera(surfaceId, cameraDeviceIndex); + }) + } + } + .visibility(this.isRecording ? Visibility.Hidden : Visibility.Visible) + } + .padding({ + left: Constants.CAPTURE_BUTTON_COLUMN_PADDING, + right: Constants.CAPTURE_BUTTON_COLUMN_PADDING + }) + .width(Constants.FULL_PERCENT) + .justifyContent(FlexAlign.SpaceBetween) + .alignItems(VerticalAlign.Center) + } + .justifyContent(FlexAlign.End) + .height(Constants.TEN_PERCENT) + .padding({ + left: Constants.CAPTURE_BUTTON_COLUMN_PADDING, + right: Constants.CAPTURE_BUTTON_COLUMN_PADDING + }) + .margin({ bottom: Constants.CAPTURE_BUTTON_COLUMN_MARGIN }) + .position({ + x: Constants.ZERO_PERCENT, + y: Constants.EIGHTY_FIVE_PERCENT + }) + } +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/ets/views/SlideComponent.ets b/CameraKit/cameraAnimSample/entry/src/main/ets/views/SlideComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..4a03b2496dfd517391a5b2adba0306d5b5d0f702 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/ets/views/SlideComponent.ets @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 CameraService, { SliderValue } from '../mode/CameraService'; +import Logger from '../common/utils/Logger'; +import { Constants } from '../common/Constants'; + +const TAG: string = 'SlideComponent'; + +// 变焦组件 +@Component +export struct SlideComponent { + // slide滑块 + @StorageLink('zoomRatio') zoomRatio: number = 1; + @StorageLink('sliderValue') sliderValue: SliderValue | undefined = undefined; + private fractionDigits = 2; + private xString = 'x'; + + aboutToDisappear(): void { + } + + slideChange(value: number): void { + CameraService.setZoomRatioFn(value); + } + + build() { + if (this.sliderValue) { + Column() { + Row() { + Text(this.zoomRatio + this.xString) + .fontColor($r('app.color.slide_text_font_color')) + .width($r('app.string.120px')) + .height($r('app.string.50px')) + .borderRadius(Constants.TEXT_BORDER_RADIUS) + .backgroundColor(Color.White) + .fontSize(Constants.FONT_SIZE_14) + .textAlign(TextAlign.Center) + } + .justifyContent(FlexAlign.Center) + .width(Constants.FULL_PERCENT) + + Row() { + Text(this.sliderValue?.min + this.xString).fontColor(Color.White) + Text(this.sliderValue?.max + this.xString).fontColor(Color.White) + } + .justifyContent(FlexAlign.SpaceBetween).width(Constants.FULL_PERCENT) + + Row() { + Slider({ + value: this.zoomRatio, + min: this.sliderValue?.min, + max: this.sliderValue?.max, + step: this.sliderValue?.step, + style: SliderStyle.OutSet + }) + .showSteps(false) + .trackColor($r('app.color.slider_track_color')) + .selectedColor($r('app.color.theme_color')) + .onChange((value: number) => { + Logger.info(TAG, 'onChange'); + let val = Number(value.toFixed(this.fractionDigits)); + this.slideChange(val); + this.zoomRatio = val; + }) + } + .width(Constants.FULL_PERCENT) + } + .height($r('app.string.60px')) + .width(Constants.FORTY_PERCENT) + .position({ + x: Constants.THIRTY_PERCENT, + y: Constants.SEVENTY_FIVE_PERCENT + }) + } + } +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/module.json5 b/CameraKit/cameraAnimSample/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..00f5977c5295baff2a7452567095bbf8464e6a84 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/module.json5 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:black", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.CAMERA", + "reason": "$string:reason", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when":"always" + } + }, + { + "name": "ohos.permission.MICROPHONE", + "reason": "$string:reason", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when":"always" + } + } + ] + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/color.json b/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..59bed5748e3f8b0a9f3f471c2e0f1d1e3ee50b45 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/color.json @@ -0,0 +1,36 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + }, + { + "name": "theme_color", + "value": "#FF0034" + }, + { + "name": "black", + "value": "#000000" + }, + { + "name": "slider_track_color", + "value": "#99FFFFFF" + }, + { + "name": "slide_text_font_color", + "value": "#182431" + }, + { + "name": "border_color", + "value": "#FFFFFF" + }, + { + "name": "dialog_background_color", + "value": "#F1F3F5" + }, + { + "name": "flash_background_color", + "value": "#33FFFFFF" + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/string.json b/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..d9e6dd33511401634158f96f013bf05e73a94359 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "cameraDemo" + }, + { + "name": "reason", + "value": "test" + }, + { + "name": "200px", + "value": "200px" + }, + { + "name": "120px", + "value": "120px" + }, + { + "name": "100px", + "value": "100px" + }, + { + "name": "60px", + "value": "60px" + }, + { + "name": "50px", + "value": "50px" + }, + { + "name": "40px", + "value": "40px" + }, + { + "name": "save", + "value": "保存" + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/flash_always_on.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/flash_always_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..3f9f710f0d7fd21153ede07520121a328aa9bbd3 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/flash_always_on.svg @@ -0,0 +1,18 @@ + + + + ic/camera/public/flash_always_on + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ProgressBar_circle.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ProgressBar_circle.svg new file mode 100644 index 0000000000000000000000000000000000000000..770aba0b9fd99ad5fd710504daa59381aaaab640 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ProgressBar_circle.svg @@ -0,0 +1,8 @@ + + + ic_camera_ProgressBar_circle + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_Radio_open.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_Radio_open.svg new file mode 100644 index 0000000000000000000000000000000000000000..84d633d9a91fb54795a6314e81f021f02dc1cf43 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_Radio_open.svg @@ -0,0 +1,24 @@ + + + ic_camera_Radio_open + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ic_camera_Radio_close.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ic_camera_Radio_close.svg new file mode 100644 index 0000000000000000000000000000000000000000..b345380c7ed2143636828dc0768a6c2d0e70cae9 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_ic_camera_Radio_close.svg @@ -0,0 +1,8 @@ + + + ic_camera_ic_camera_Radio_close + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_pad.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_pad.svg new file mode 100644 index 0000000000000000000000000000000000000000..16f06746e38339eab8cff4b22a87a186ea1c4681 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_pad.svg @@ -0,0 +1,13 @@ + + + ic_camera_pad + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_phone.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_phone.svg new file mode 100644 index 0000000000000000000000000000000000000000..c2cc3251e233cbd25bb4c37ea98a2e0c46616cda --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_phone.svg @@ -0,0 +1,13 @@ + + + ic_camera_phone + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto.svg new file mode 100644 index 0000000000000000000000000000000000000000..5e4b45fcd70d9ff6851ddaa2b20563182176057e --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto.svg @@ -0,0 +1,13 @@ + + + ic_camera_public_flash_auto + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto_white.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..5e4b45fcd70d9ff6851ddaa2b20563182176057e --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_auto_white.svg @@ -0,0 +1,13 @@ + + + ic_camera_public_flash_auto + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_off.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_off.svg new file mode 100644 index 0000000000000000000000000000000000000000..deaae7f92646fc34753fd9c3666ab7d924e68e7a --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_off.svg @@ -0,0 +1,13 @@ + + + ic_camera_public_flash_off + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_on.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..ebd9922b5ea9ec16c902c07ba48c07839a1913f9 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_public_flash_on.svg @@ -0,0 +1,13 @@ + + + ic_camera_public_flash_on + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_AF.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_AF.svg new file mode 100644 index 0000000000000000000000000000000000000000..97618096eb370f76a39a9b1d3cf09f0d3a0f688b --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_AF.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_focus + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Checked.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Checked.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b62d7543215096ef0d7cf5b57bd0ff7255ae301 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Checked.svg @@ -0,0 +1,13 @@ + + + ic_camera_set_Checked + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Format.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Format.svg new file mode 100644 index 0000000000000000000000000000000000000000..44fbba6dc229262a17cf80ea33d2a2909255115e --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Format.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_Format + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Location.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Location.svg new file mode 100644 index 0000000000000000000000000000000000000000..7ea27aa4f8e2101df6d1afea60954ed60c18b89a --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Location.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_Location + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Quality.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Quality.svg new file mode 100644 index 0000000000000000000000000000000000000000..0c7de7e2074d025fa76ea38f7a2c9dceaf585879 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_Quality.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_Quality + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Antishake.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Antishake.svg new file mode 100644 index 0000000000000000000000000000000000000000..e2a48846dab24511323341d8c92334ee3e1169b9 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Antishake.svg @@ -0,0 +1,7 @@ + + + ic_camera_set__Antishake + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Mirror.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Mirror.svg new file mode 100644 index 0000000000000000000000000000000000000000..2737fc9c28730b11f23807b4bd6ff984a82f28c8 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set__Mirror.svg @@ -0,0 +1,9 @@ + + + ic_camera_set__Mirror + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_arrow.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_arrow.svg new file mode 100644 index 0000000000000000000000000000000000000000..97025a536ba73799ac3fba4710ff6f38f98a8586 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_arrow.svg @@ -0,0 +1,13 @@ + + + ic_camera_set_arrow + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_class.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_class.svg new file mode 100644 index 0000000000000000000000000000000000000000..c926c738af12619b9d23e7475ca290dd4f6f2900 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_class.svg @@ -0,0 +1,8 @@ + + + ic_camera_set_class + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_exposure.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_exposure.svg new file mode 100644 index 0000000000000000000000000000000000000000..0ffbb1531fd3c148ff7f8147f0754d63cf0fef13 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_exposure.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_exposure + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_focus.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_focus.svg new file mode 100644 index 0000000000000000000000000000000000000000..eb597d91d1a247cbe433ac96fee8b195179c6c04 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_focus.svg @@ -0,0 +1,9 @@ + + + ic_camera_set_focus + + + AF + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_line.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_line.svg new file mode 100644 index 0000000000000000000000000000000000000000..b5fa81891c209f76622304c1cbe8546ec5b7ffdd --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_line.svg @@ -0,0 +1,13 @@ + + + ic_camera_set_line + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_pic_Resolution.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_pic_Resolution.svg new file mode 100644 index 0000000000000000000000000000000000000000..618458951af82555e0ebe1de7052716efb72cf05 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_pic_Resolution.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_pic_Resolution + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Rate.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Rate.svg new file mode 100644 index 0000000000000000000000000000000000000000..d0c1af0c398f74b3b9ec011dbbbe2c5da369634c --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Rate.svg @@ -0,0 +1,13 @@ + + + ic_camera_set_video_Rate + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Resolution.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Resolution.svg new file mode 100644 index 0000000000000000000000000000000000000000..53d39983bf8e9e82d85dfcdd1c94ed3fede04d56 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_set_video_Resolution.svg @@ -0,0 +1,7 @@ + + + ic_camera_set_video_Resolution + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off.svg new file mode 100644 index 0000000000000000000000000000000000000000..0e4631448c6d77c116487e56da83ce44263961c9 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off.svg @@ -0,0 +1,9 @@ + + + ic_camera_switch_off + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off2.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off2.svg new file mode 100644 index 0000000000000000000000000000000000000000..0b4e58935df66e250a89c9dd787c08480b02a756 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_switch_off2.svg @@ -0,0 +1,14 @@ + + + ic_camera_switch_off 2 + + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_close.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_close.svg new file mode 100644 index 0000000000000000000000000000000000000000..476634fbbfb2959b0343cbb8e55db9c1c4211cdb --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_close.svg @@ -0,0 +1,7 @@ + + + ic_camera_video_close + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_off.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_off.svg new file mode 100644 index 0000000000000000000000000000000000000000..854f409f19546cfaa313609ef3979f22fb0dddf7 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_off.svg @@ -0,0 +1,13 @@ + + + ic_camera_video_off + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_on.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..6da84352329a4c864760c5e6bb3a82f5260ff6bd --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_camera_video_on.svg @@ -0,0 +1,7 @@ + + + ic_camera_video_on + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_back.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..66b6ad0150fb0bd1d73683e2f1eda2fe109941d5 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_back.svg @@ -0,0 +1,9 @@ + + + ic_public_back + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness.svg new file mode 100644 index 0000000000000000000000000000000000000000..b74c726facde9fcc364fa4750161d05bd8034e53 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness.svg @@ -0,0 +1,7 @@ + + + ic_public_brightness + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness_filled.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness_filled.svg new file mode 100644 index 0000000000000000000000000000000000000000..7c219cee8c2cb2e84e05bbf381c805b157f76dd2 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/ic_public_brightness_filled.svg @@ -0,0 +1,7 @@ + + + ic_public_brightness_filled + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon.png b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon.png differ diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting.svg new file mode 100644 index 0000000000000000000000000000000000000000..60c32abaa0be5b6064ea96910f1aa0f8117a3cf0 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting.svg @@ -0,0 +1,13 @@ + + + icon_camera_setting + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer.svg new file mode 100644 index 0000000000000000000000000000000000000000..853e04954758f26560ceb3ca1d246206dfdca3c3 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer.svg @@ -0,0 +1,13 @@ + + + icon_camera_setting_timer + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on.svg new file mode 100644 index 0000000000000000000000000000000000000000..5f8413271ee689148263d8db7dc05a54e1bc4a20 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on.svg @@ -0,0 +1,13 @@ + + + icon_camera_setting_timer_on + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on_balk.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on_balk.svg new file mode 100644 index 0000000000000000000000000000000000000000..875ecca5f058f9800a092b9d4f31be06fc662593 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/icon_camera_setting_timer_on_balk.svg @@ -0,0 +1,13 @@ + + + icon_camera_setting_timer_on + + + + + + + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/startIcon.png b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..366f76459ffd4494ec40d0ddd5c59385b9c5da11 Binary files /dev/null and b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/startIcon.png differ diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/switch_camera.svg b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/switch_camera.svg new file mode 100644 index 0000000000000000000000000000000000000000..43dab70a87aab51158e47ad6b57fdcee6cf71556 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/media/switch_camera.svg @@ -0,0 +1,7 @@ + + + switch_camera + + + + \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/base/profile/main_pages.json b/CameraKit/cameraAnimSample/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..eecb51b4ab45bb9640cd180f792b96a36ae1affb --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/Index", + "pages/EditPage" + ] +} diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/en_US/element/string.json b/CameraKit/cameraAnimSample/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7d880a88a2bb73f25219e811412a52214ae9926b --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "cameraDemo" + }, + { + "name": "reason", + "value": "test" + }, + { + "name": "200px", + "value": "200px" + }, + { + "name": "120px", + "value": "120px" + }, + { + "name": "100px", + "value": "100px" + }, + { + "name": "60px", + "value": "60px" + }, + { + "name": "50px", + "value": "50px" + }, + { + "name": "40px", + "value": "40px" + }, + { + "name": "save", + "value": "save" + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/main/resources/zh_CN/element/string.json b/CameraKit/cameraAnimSample/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1908692b7a42a27f046a3a9e22c8d48576686d97 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "cameraDemo" + }, + { + "name": "reason", + "value": "test" + }, + { + "name": "200px", + "value": "200px" + }, + { + "name": "120px", + "value": "120px" + }, + { + "name": "100px", + "value": "100px" + }, + { + "name": "60px", + "value": "60px" + }, + { + "name": "50px", + "value": "50px" + }, + { + "name": "40px", + "value": "40px" + }, + { + "name": "save", + "value": "保存" + } + ] +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..469980b81e0f5f6339535309fbdf8cce13375d91 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import Base from '@ohos.base'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import { PermissionRequestResult } from '@ohos.abilityAccessCtrl'; +import { Permissions } from '@kit.AbilityKit'; +import { Driver, ON, MatchPattern, Component } from '@ohos.UiTest'; +import fs from '@ohos.file.fs'; +import Want from '@ohos.app.ability.Want'; + +const TAG = 'abilityTest'; +const domain: number = 0x0000; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('captureTest', 0, async () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + try { + let want: Want = { + bundleName: 'com.samples.cameraAnimSample', + abilityName: 'EntryAbility' + }; + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + abilityDelegator.startAbility(want, (err: Base.BusinessError) => { + hilog.info(domain, TAG, 'StartAbility get err ' + JSON.stringify(err)); + // expect(err).assertNull(); + }) + + console.info('%{public}s', ' beforeAll 4'); + let driver = Driver.create(); + await driver.delayMs(1000); + let permissionButton: Component | null = null; + for (let i = 0; i < 3; i++) { + permissionButton = await driver.waitForComponent(ON.text('允许', MatchPattern.EQUALS), 1000); + console.info('captureTest btn1' + JSON.stringify(permissionButton)); + if (permissionButton != null) { + await permissionButton.click(); + console.info('captureTest after permissionButton.click '); + await driver.delayMs(300); // 等待下一个权限请求弹窗出现 + } + } + await driver.delayMs(300); + console.info('%{public}s', ' beforeAll 6'); + let btn1 = await driver.findComponent(ON.text('拍照', MatchPattern.EQUALS)); + console.info('captureTest btn1' + JSON.stringify(btn1)); + await btn1.click(); + await driver.delayMs(300); + console.info('%{public}s', ' beforeAll 8'); + let btn2 = await driver.findComponent(ON.type('Button').id('Capture')); + console.info('captureTest btn2' + JSON.stringify(btn2)); + await btn2.click(); + console.info('%{public}s', ' beforeAll 10'); + await driver.delayMs(3000); + expect(btn2 == null).assertFalse(); + await driver.delayMs(1000); + } catch (err) { + console.info(' captureTest failed, err: ' + err); + } + }) + + it('recordTest', 0, async () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + try { + let driver = Driver.create(); + await driver.delayMs(300); + let btn1 = await driver.findComponent(ON.text('录像', MatchPattern.EQUALS)); + console.info('recordTest btn1' + JSON.stringify(btn1)); + await btn1.click(); + await driver.delayMs(300); + console.info('%{public}s', ' recordTest 8'); + let btn2 = await driver.findComponent(ON.type('Button').id('Capture')); + console.info('recordTest btn2' + JSON.stringify(btn2)); + await btn2.click(); + console.info('%{public}s', ' recordTest 10'); + await driver.delayMs(3000); + let btn3 = await driver.findComponent(ON.type('Button').id('VideoStopButton')); + console.info('recordTest btn3' + JSON.stringify(btn3)); + expect(btn3 == null).assertFalse(); + await btn3.click(); + await driver.delayMs(500); + } catch (err) { + console.info(' recordTest failed, err: ' + err); + } + }) + }) +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/List.test.ets b/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ddd768373279adb3e35fe6af47bc094a16ac155 --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 abilityTest from './Ability.test'; + + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/entry/src/ohosTest/module.json5 b/CameraKit/cameraAnimSample/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..91dbe399e0d9e5b1e516b5cb3746fa4fad50acdd --- /dev/null +++ b/CameraKit/cameraAnimSample/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/CameraKit/cameraAnimSample/hvigor/hvigor-config.json5 b/CameraKit/cameraAnimSample/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9b48b0b336207e38e9b88a466a59f4bba40c1cd9 --- /dev/null +++ b/CameraKit/cameraAnimSample/hvigor/hvigor-config.json5 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 4096 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */ + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/hvigorfile.ts b/CameraKit/cameraAnimSample/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3cb9f1a87a81687554a76283af8df27d8bda775 --- /dev/null +++ b/CameraKit/cameraAnimSample/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/CameraKit/cameraAnimSample/oh-package.json5 b/CameraKit/cameraAnimSample/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e95b72ef1fae88794b0e91095b74ae4e4de1951a --- /dev/null +++ b/CameraKit/cameraAnimSample/oh-package.json5 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +{ + "modelVersion": "5.0.0", + "name": "camerademo", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18" + } +} \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/ohosTest.md b/CameraKit/cameraAnimSample/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..51427de99f632413cf3132bba101f088fb59207c --- /dev/null +++ b/CameraKit/cameraAnimSample/ohosTest.md @@ -0,0 +1,9 @@ +# CustomCamera-master测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|---------------|-----------|---------------------------------------------------------------|-----------|------|------| +| 拉起应用 | 设备正常运行 | | 成功拉起应用 | 是 | Pass | +| 拍照功能 | 进入示例应用 | 1. 点击拍照
2. 点击拍照按钮 | 拍照图片保存至图库 | 是 | Pass | +| 录制功能 | 进入示例应用 | 1. 点击录像
2. 点击录像开始按钮
3. 一段时间后点击录像停止按钮 | 录像视频保存至图库 | 是 | Pass | \ No newline at end of file diff --git a/CameraKit/cameraAnimSample/screenshot/device/cameraSample.png b/CameraKit/cameraAnimSample/screenshot/device/cameraSample.png new file mode 100644 index 0000000000000000000000000000000000000000..eaef4e4467b9022dabb6f328ce7b14b6b83806d2 Binary files /dev/null and b/CameraKit/cameraAnimSample/screenshot/device/cameraSample.png differ diff --git a/CameraKit/preconfig/entry/src/ohosTest/ets/test/Ability.test.ets b/CameraKit/preconfig/entry/src/ohosTest/ets/test/Ability.test.ets index b5f95e765dbee7c650b4f75791aa58eb1009de34..ed08735167a7b807be2e2945d58ada0c78624141 100644 --- a/CameraKit/preconfig/entry/src/ohosTest/ets/test/Ability.test.ets +++ b/CameraKit/preconfig/entry/src/ohosTest/ets/test/Ability.test.ets @@ -52,7 +52,7 @@ export default function abilityTest() { // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. try { let want: Want = { - bundleName: 'com.example.CameraKit', + bundleName: 'com.samples.Preconfig', abilityName: 'EntryAbility' }; let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();