Merge branch 'master' of gitee.com:openharmony/applications_app_samples
2
README_zh.md
Executable file → Normal file
@ -433,7 +433,7 @@
|
||||
<td ></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<![if supportMisalignedColumns]>
|
||||
<![if supportMisalignedColumns]>
|
||||
<tr height="18" style="display:none;">
|
||||
</tr>
|
||||
<![endif]>
|
||||
|
@ -17,6 +17,7 @@ import hilog from '@ohos.hilog';
|
||||
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
import { Driver, ON } from '@ohos.UiTest';
|
||||
import inputMethod from '@ohos.inputMethod';
|
||||
|
||||
const DRIVER = Driver.create();
|
||||
const BUNDLE = 'http';
|
||||
@ -121,6 +122,10 @@ export default function abilityTest() {
|
||||
let ipAddress = await DRIVER.findComponent(ON.id('GET'));
|
||||
await ipAddress.inputText('https://baidu.com');
|
||||
await DRIVER.delayMs(1000);
|
||||
// 停止会话,关闭键盘
|
||||
let inputMethodController = inputMethod.getController();
|
||||
inputMethodController.stopInputSession();
|
||||
await DRIVER.delayMs(1000);
|
||||
let submitBtn = await DRIVER.findComponent(ON.id('submit'));
|
||||
await submitBtn.click();
|
||||
await DRIVER.delayMs(1000);
|
||||
|
@ -98,7 +98,7 @@
|
||||
```
|
||||
git init
|
||||
git config core.sparsecheckout true
|
||||
echo code/BasicFeature/Notification/CustomNotification/ > .git/info/sparse-checkout
|
||||
echo code/BasicFeature/Connectivity/StageSocket/ > .git/info/sparse-checkout
|
||||
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
|
||||
git pull origin master
|
||||
```
|
@ -1,10 +1,14 @@
|
||||
## 上传下载应用服务器使用说明
|
||||
|
||||
### HFS下载地址:
|
||||
|
||||
https://nchc.dl.sourceforge.net/project/hfs/HFS/2.3m/hfs.exe
|
||||
|
||||
### 服务器配置:
|
||||
|
||||
1.测试设备和电脑连接同一个局域网。
|
||||
|
||||
2.电脑打开本目录下`hfs.exe`可执行文件。
|
||||
2.电脑打开下载好的`hfs.exe`可执行文件。
|
||||
|
||||
3.hfs客户端配置:
|
||||
|
||||
|
BIN
code/BasicFeature/Connectivity/UploadAndDownLoad/environment/hfs.exe
(Stored with Git LFS)
@ -17,6 +17,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
import { Component, Driver, ON } from '@ohos.UiTest';
|
||||
import hilog from '@ohos.hilog';
|
||||
import inputMethod from '@ohos.inputMethod';
|
||||
|
||||
const BUNDLE = 'WebSocket'
|
||||
const TAG = '[Sample_WebSocket]'
|
||||
@ -92,6 +93,9 @@ export default function appTest() {
|
||||
let btnBind = await driver.findComponent(ON.id('btn_send'))
|
||||
await btnBind.click()
|
||||
await driver.delayMs(500)
|
||||
// 停止会话,关闭键盘
|
||||
let inputMethodController = inputMethod.getController();
|
||||
inputMethodController.stopInputSession();
|
||||
|
||||
// 校验内容
|
||||
hilog.info(DOMAIN, TAG, BUNDLE + ' sendMessage_001 check message')
|
||||
@ -147,6 +151,9 @@ export default function appTest() {
|
||||
hilog.info(DOMAIN, TAG, BUNDLE + ' sendMessage_002 send')
|
||||
await btnBind.click()
|
||||
await driver.delayMs(500)
|
||||
// 停止会话,关闭键盘
|
||||
let inputMethodController = inputMethod.getController();
|
||||
inputMethodController.stopInputSession();
|
||||
|
||||
// 校验内容
|
||||
hilog.info(DOMAIN, TAG, BUNDLE + ' sendMessage_002 check message')
|
||||
@ -193,6 +200,9 @@ export default function appTest() {
|
||||
let btnBind = await driver.findComponent(ON.id('btn_send'))
|
||||
await btnBind.click()
|
||||
await driver.delayMs(500)
|
||||
// 停止会话,关闭键盘
|
||||
let inputMethodController = inputMethod.getController();
|
||||
inputMethodController.stopInputSession();
|
||||
|
||||
// 校验内容
|
||||
hilog.info(DOMAIN, TAG, BUNDLE + ' sendMessage_003 check message')
|
||||
|
@ -48,9 +48,9 @@
|
||||
|
||||
7.设备状态感知框架:点击按钮分别实现订阅设备状态服务、取消订阅设备状态服务、查询设备状态并显示功能。
|
||||
|
||||
5.热管理:进入热管理页面,展示当前设备热档位信息、设备过热时提示用户。
|
||||
8.热管理:进入热管理页面,展示当前设备热档位信息、设备过热时提示用户。
|
||||
|
||||
6.USB管理:打开监听开关后插入USB设备,会有提示并刷新设备列表,关闭监听后插入USB设备不会提示和自动刷新,下拉设备列表可以手动刷新设备列表。
|
||||
9.USB管理:打开监听开关后插入USB设备,会有提示并刷新设备列表,关闭监听后插入USB设备不会提示和自动刷新,下拉设备列表可以手动刷新设备列表。
|
||||
|
||||
### 工程目录
|
||||
|
||||
|
@ -20,7 +20,7 @@ class BasicDataSource implements IDataSource {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public getData(index: number): ESObject {
|
||||
public getData(index: number): Object | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
{
|
||||
"apiType": 'stageMode',
|
||||
"buildOption": {
|
||||
"compileMode": "esmodule"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
|
@ -37,6 +37,30 @@ export default function abilityTest() {
|
||||
// 删除测试过程中创建的文件
|
||||
afterAll(async () => {
|
||||
})
|
||||
/**
|
||||
* 拉起相机拍照
|
||||
*/
|
||||
it(BUNDLE + 'TakePhoto_001', 0, async (done: Function) => {
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'TakePhoto_001 begin');
|
||||
let driver = Driver.create();
|
||||
let abilityDelegatorRegistry = AbilityDelegatorRegistry.getAbilityDelegator();
|
||||
try {
|
||||
await abilityDelegatorRegistry.startAbility({
|
||||
bundleName: 'com.ohos.camera',
|
||||
abilityName: 'com.ohos.camera.MainAbility'
|
||||
});
|
||||
await driver.delayMs(4000);
|
||||
// 坐标基于rk3568
|
||||
await driver.click(360, 1090);
|
||||
await driver.delayMs(1000);
|
||||
done();
|
||||
} catch (err) {
|
||||
expect(0).assertEqual(err.code);
|
||||
done();
|
||||
}
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'TakePhoto_001 end');
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开应用
|
||||
*/
|
||||
@ -117,21 +141,15 @@ export default function abilityTest() {
|
||||
let btnAlbum = await driver.findComponent(ON.text(await getResourceString($r('app.string.album_camera'))));
|
||||
await btnAlbum.click();
|
||||
await driver.delayMs(3000);
|
||||
let btnItem = await driver.findComponents(ON.type('GridItem'));
|
||||
if (btnItem == null || btnItem.length == 0 || btnItem == undefined) {
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.no_photo'))));
|
||||
} else {
|
||||
await driver.assertComponentExist(ON.type('GridItem'));
|
||||
// 点击图片
|
||||
await driver.click(100, 200);
|
||||
await driver.delayMs(1000);
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_favor')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_favor'))));
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_rename')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_rename'))));
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_delete')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_delete'))));
|
||||
}
|
||||
// 点击图片
|
||||
await driver.click(100, 200);
|
||||
await driver.delayMs(1000);
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_favor')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_favor'))));
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_rename')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_rename'))));
|
||||
HiLog.info(DOMAIN, TAG, BUNDLE + 'check text' + await getResourceString($r('app.string.btn_delete')));
|
||||
await driver.assertComponentExist(ON.text(await getResourceString($r('app.string.btn_delete'))));
|
||||
})
|
||||
/**
|
||||
* 重命名文件
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
| 测试功能 |预置条件|输入|预期输出|是否自动|测试结果|
|
||||
|---------|-------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|
|
||||
| 拉起相机 |设备相机正常|点击拍照|成功拍照|是|Pass|
|
||||
| 拉起应用 |设备正常运行| |成功拉起应用|是|Pass|
|
||||
| 申请权限 |成功拉起应用| |弹出提示框|是|Pass|
|
||||
| 主页展示 |使用相机应用拍照,权限申请成功| |展示系统相册(相机,视频,截屏录屏,我的收藏,最近删除),默认缩略图|否|Pass|
|
||||
|
@ -18,6 +18,7 @@ import { Driver, ON, MatchPattern } from '@ohos.UiTest';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
import I18n from '@ohos.i18n';
|
||||
import Logger from '../Logger';
|
||||
import inputMethod from '@ohos.inputMethod';
|
||||
|
||||
const BUNDLE = "International_";
|
||||
const TAG: string = '[Sample_International]';
|
||||
@ -130,6 +131,11 @@ export default function abilityTest() {
|
||||
// 文本断点处理
|
||||
let longTextInput = await driver.findComponent(ON.id('long_text_input'));
|
||||
await longTextInput.inputText('text processing test');
|
||||
await driver.delayMs(100);
|
||||
// 停止会话,关闭键盘
|
||||
let inputMethodController = inputMethod.getController();
|
||||
inputMethodController.stopInputSession();
|
||||
await driver.delayMs(100);
|
||||
let longTextButton = await driver.findComponent(ON.text(await getResourceString($r('app.string.text_breakpoint'))));
|
||||
await longTextButton.click();
|
||||
await driver.delayMs(100);
|
||||
|
11
code/BasicFeature/Media/AVSession/VideoPlayer/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/local.properties
|
||||
/.idea
|
||||
**/build
|
||||
/.hvigor
|
||||
.cxx
|
||||
/.clangd
|
||||
/.clang-format
|
||||
/.clang-tidy
|
||||
**/.test
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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": {
|
||||
"bundleName": "com.samples.videoplayer",
|
||||
"vendor": "example",
|
||||
"versionCode": 1000000,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:app_icon",
|
||||
"label": "$string:app_name"
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "app_name",
|
||||
"value": "AVCastDemo"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.6 KiB |
116
code/BasicFeature/Media/AVSession/VideoPlayer/README.md
Normal file
@ -0,0 +1,116 @@
|
||||
# 视频播放
|
||||
|
||||
### 介绍
|
||||
|
||||
本示例主要展示了网络视频播放的相关功能。使用[@ohos.multimedia.avsession]等接口实现视频播放的功能。
|
||||
|
||||
### 效果预览
|
||||
|
||||
| 主页 |
|
||||
|-------------------------------- |
|
||||
| ![Index](screenshots/device/index.jpeg) |
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. 点击播放按钮,应用的播放状态发生变化。
|
||||
2. 点击暂停按钮,应用的播放状态开始变化。
|
||||
3. 点击上一个按钮,界面展示播放列表中的上一个视频的信息。
|
||||
4. 点击下一下按钮,界面展示播放列表中的下一个视频的信息。
|
||||
|
||||
### 工程目录
|
||||
|
||||
给出项目中关键的目录结构并描述它们的作用,示例如下:
|
||||
|
||||
```
|
||||
entry/src/main/ets/
|
||||
|---common // 方法封装
|
||||
|---|---AudioFrameworkTest.ets
|
||||
|---|---AudioUtils.ets // 控制器封装
|
||||
|---|---CommonUtils.ets // 格式化时间封装
|
||||
|---|---Constants.ets // 媒体资源信息
|
||||
|---|---PermissionUtils.ets // 权限封装
|
||||
|---entryability
|
||||
|---|---EntryAbility.ets
|
||||
|---pages
|
||||
|---|---Index.ets // 界面实现
|
||||
|---|---components
|
||||
|---|---|---SongItem.ets // 视频列表组件
|
||||
```
|
||||
|
||||
### 具体实现
|
||||
|
||||
* 界面相关的实现都封装在pages/Index.ets下,源码参考:[pages/Index.ets](./entry/src/main/ets/pages/Index.ets)
|
||||
* 使用`@State`来设置与逻辑代码同步更新的变量,当逻辑代码中对应的变量更新时,界面会同步的刷新。
|
||||
|
||||
* 通过引入逻辑代码对应的类,创建出对象,实现对onClick事件的响应,关键代码段:
|
||||
```js
|
||||
|
||||
import media from '@ohos.multimedia.media'; // 引入
|
||||
|
||||
this.avPlayer = await media.createAVPlayer(); // 创建对象
|
||||
|
||||
this.controller = await this.session.getController(); // 通过类的对象来调用逻辑代码
|
||||
|
||||
```
|
||||
|
||||
* 逻辑相关的实现都封装在common/MediaController.ets下,源码参考:[common/AudioUtils.ets](./entry/src/main/ets/common/AudioUtils.ets)
|
||||
|
||||
应用的初始化相关操作
|
||||
|
||||
* 链接变量
|
||||
|
||||
使用`@State`来设置与逻辑代码同步更新,关键代码段:
|
||||
|
||||
```ets
|
||||
@State session: avSession.AVSession = null;
|
||||
@State controller: avSession.AVSessionController = null;
|
||||
private avPlayer: media.AVPlayer;
|
||||
@State @Watch('playInfoUpdated') currentPlayInfo: avSession.AVMediaDescription = undefined;
|
||||
this.currentPlayInfo = temp;
|
||||
this.avPlayer = await this.audioUtils.init();
|
||||
```
|
||||
|
||||
* 获取当前设备中会话并创建Controller
|
||||
|
||||
通过接口`audioUtils.init()`获取当前设备中的媒体会话;
|
||||
|
||||
通过接口`session.getController()`创建媒体会话对应的控制器;
|
||||
|
||||
通过接口`on(play | pause | stop | playNext | playPrevious | seek)`开启对远程以及播控中心提供方发送事件的监听,对事件进行处理;
|
||||
|
||||
应用在运行中相关的操作
|
||||
|
||||
* 从远程以及播控中心获取基础控制命令
|
||||
|
||||
基础控制命令可以通过监听事件`setListenerForMesFromController()`。本示例中,从媒体控制方到媒体提供方的基础控制命令主要包括`play, pause, playPrevious, playNext`。发送命令的参考代码如下:
|
||||
```ets
|
||||
let command : AVSessionManager.AVControlCommand = {
|
||||
command : 'play',
|
||||
parameter : undefined
|
||||
} // 构造AVControlCommand参数
|
||||
async setListenerForMesFromController(); // 媒体会话控制器与媒体会话一一对应
|
||||
```
|
||||
|
||||
* 获取自定义会话数据(以获取视频为例)
|
||||
|
||||
> 说明:
|
||||
>
|
||||
> 本示例中,用户点击“下一个视频”的命令,会在将视频信息更新。
|
||||
|
||||
视频使用接口`switchToNextByLoopMode()`更新视频信息,示例代码如下:
|
||||
```ets
|
||||
this.currentIndex = this.currentIndex === this.songList.length - 1 ? 0 : this.currentIndex + 1;
|
||||
this.updateCurrentPlayInfo(this.songList[this.currentIndex], this.audioType);
|
||||
```
|
||||
|
||||
### 相关权限
|
||||
|
||||
不涉及
|
||||
|
||||
### 约束与限制
|
||||
|
||||
1. 本示例仅支持标准系统上运行。
|
||||
|
||||
2. 本示例为Stage模型,支持API10版本SDK,SDK版本号(API Version 10 Release),镜像版本号(4.0 Release)
|
||||
|
||||
3. 本示例需要使用DevEco Studio 版本号(4.0 Release)及以上版本才可编译运行。
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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": {
|
||||
"signingConfigs": [],
|
||||
"products": [
|
||||
{
|
||||
"name": "default",
|
||||
"signingConfig": "default",
|
||||
"compileSdkVersion": 10,
|
||||
"compatibleSdkVersion": 10,
|
||||
"runtimeOS": "OpenHarmony"
|
||||
}
|
||||
],
|
||||
"buildModeSet": [
|
||||
{
|
||||
"name": "debug",
|
||||
},
|
||||
{
|
||||
"name": "release"
|
||||
}
|
||||
]
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "entry",
|
||||
"srcPath": "./entry",
|
||||
"targets": [
|
||||
{
|
||||
"name": "default",
|
||||
"applyToProducts": [
|
||||
"default"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
6
code/BasicFeature/Media/AVSession/VideoPlayer/entry/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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": {
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"name": "ohosTest",
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
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. */
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
{
|
||||
"license": "",
|
||||
"devDependencies": {},
|
||||
"author": "",
|
||||
"name": "entry",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 fs from '@ohos.file.fs';
|
||||
|
||||
const TAG = "AVCastDemo";
|
||||
|
||||
export default class AudioFrameworkTest {
|
||||
public constructor() {
|
||||
console.log(TAG, 'start audio framework test init');
|
||||
let context = getContext(this);
|
||||
let path = context.filesDir;
|
||||
// 使用沙箱路径获取文件,实际路径为/data/app/el2/100/base/com.samples.videoplayer/haps/entry/files/sample_3s.wav
|
||||
const filePath = path + '/sample_3s.wav';
|
||||
console.log(TAG, 'file path: ' + filePath);
|
||||
fs.stat(filePath, (err, state) => {
|
||||
console.log(TAG, 'get file state succeed: size = ' + state?.size + ', err = ' + err);
|
||||
})
|
||||
console.log(TAG, 'try open file: ' + filePath);
|
||||
let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
|
||||
console.log(TAG, 'try open file succeed: ' + file.fd);
|
||||
fs.close(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 media from '@ohos.multimedia.media';
|
||||
import common from '@ohos.app.ability.common';
|
||||
|
||||
const TAG = 'AVCastDemo';
|
||||
|
||||
export default class AudioUtils {
|
||||
public avPlayer: media.AVPlayer | null = null;
|
||||
public state: string = '';
|
||||
public surfaceId: string = '';
|
||||
private callbackMap: Map<string, Function> = new Map();
|
||||
|
||||
constructor() {
|
||||
console.log(TAG, `AVPlayer seek succeede in `);
|
||||
}
|
||||
|
||||
public async init() {
|
||||
this.avPlayer = await media.createAVPlayer();
|
||||
this.setAVPlayerCallback();
|
||||
return this.avPlayer;
|
||||
}
|
||||
|
||||
setAVPlayerCallback() {
|
||||
if (!this.avPlayer) {
|
||||
console.log(TAG, 'no acPlayer');
|
||||
return;
|
||||
}
|
||||
this.avPlayer.on('seekDone', (seekDOneTime) => {
|
||||
console.log(TAG, `AVPlayer seek succeeded, seek time is ${seekDOneTime}`);
|
||||
})
|
||||
this.avPlayer.on('error', (err) => {
|
||||
console.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
|
||||
this.avPlayer?.reset();
|
||||
})
|
||||
this.avPlayer.on('stateChange', async (state) => {
|
||||
this.state = state;
|
||||
switch (state) {
|
||||
case 'idle':
|
||||
console.log(TAG, 'AVPlayer state idle called.');
|
||||
this.callbackMap = new Map();
|
||||
break;
|
||||
case 'initialized':
|
||||
console.log(TAG, 'AVPlayer state initialized called.');
|
||||
if (this.avPlayer && this.surfaceId) {
|
||||
this.avPlayer.surfaceId = this.surfaceId;
|
||||
}
|
||||
try {
|
||||
this.avPlayer?.prepare().then(() => {
|
||||
console.log(TAG, 'AVPlayer prepare succeeded.');
|
||||
});
|
||||
} catch(err) {
|
||||
console.log(TAG, `Invoke prepare failed, err : ${JSON.stringify(err)}`)
|
||||
}
|
||||
break;
|
||||
case 'prepared':
|
||||
console.log(TAG, 'AVPlayer state prepare called.');
|
||||
if (this.callbackMap.get('prepared')) {
|
||||
this.callbackMap.get('prepared');
|
||||
console.log(TAG, 'AVPlayer state prepare start.');
|
||||
this.callbackMap.set('prepared', null);
|
||||
}
|
||||
break;
|
||||
case 'playing':
|
||||
console.log(TAG, 'AVPlayer state playing called.');
|
||||
break;
|
||||
case 'paused':
|
||||
console.log(TAG, 'AVPlayer state paused called.');
|
||||
break;
|
||||
case 'completed':
|
||||
console.log(TAG, 'AVPlayer state completed called.');
|
||||
if (this.callbackMap.get('completed')) {
|
||||
this.callbackMap.get('completed');
|
||||
console.log(TAG, 'AVPlayer state completed start.');
|
||||
}
|
||||
break;
|
||||
case 'stopped':
|
||||
console.log(TAG, 'AVPlayer state stopped called.');
|
||||
break;
|
||||
case 'released':
|
||||
console.log(TAG, 'AVPlayer state released called.');
|
||||
break;
|
||||
default:
|
||||
console.log(TAG, 'AVPlayer state unknown called.');
|
||||
break;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public on(event: string, callback: Function) {
|
||||
this.callbackMap.set(event, callback)
|
||||
}
|
||||
|
||||
public async loadFromSrcFd(fileDescriptor: media.AVFileDescriptor) {
|
||||
console.log(TAG, 'loadFromSrcFd: ' + JSON.stringify(fileDescriptor));
|
||||
if (!this.avPlayer) {
|
||||
console.log(TAG, 'no acPlayer');
|
||||
return;
|
||||
}
|
||||
if (this.state !== 'idle') {
|
||||
await this.avPlayer.reset();
|
||||
}
|
||||
this.avPlayer.fdSrc = fileDescriptor;
|
||||
return this.avPlayer;
|
||||
}
|
||||
|
||||
public async loadFromRawFile(fileName: string) {
|
||||
console.log(TAG, 'loadFromRawFile: ' + fileName);
|
||||
if (!this.avPlayer) {
|
||||
console.log(TAG, 'no avplayer');
|
||||
return;
|
||||
}
|
||||
if (this.state !== 'idle') {
|
||||
await this.avPlayer.reset();
|
||||
}
|
||||
const context = getContext(this) as common.UIAbilityContext;
|
||||
const fileDescriptor = await context.resourceManager.getRawFd(fileName);
|
||||
console.log(TAG, 'fileDescriptor: ' + fileDescriptor);
|
||||
this.avPlayer.fdSrc = fileDescriptor;
|
||||
return this.avPlayer;
|
||||
}
|
||||
|
||||
public async loadFromNetwork(url: string) {
|
||||
console.log(TAG, 'loadFromNetwork: ' + url);
|
||||
if (!this.avPlayer) {
|
||||
console.log(TAG, 'no avplayer');
|
||||
return;
|
||||
}
|
||||
if (this.state !== 'idle') {
|
||||
await this.avPlayer.reset();
|
||||
}
|
||||
this.avPlayer.url = url;
|
||||
return this.avPlayer;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 default class CommonUtils {
|
||||
public static millSecond2Minutes(time: number) {
|
||||
const min = Math.floor(time / 1000 / 60);
|
||||
const sec = Math.ceil(time / 1000 % 60);
|
||||
return `${CommonUtils.paddingString(min)}:${CommonUtils.paddingString(sec)}`;
|
||||
}
|
||||
|
||||
static paddingString(value: number) {
|
||||
if (value < 10) {
|
||||
return `0${value}`;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 avSession from '@ohos.multimedia.avsession';
|
||||
|
||||
export default class Constants {
|
||||
static THEME_COLOR = '#ffffffff';
|
||||
|
||||
static URL_VIDEO_LIST: Array<avSession.AVMediaDescription> = [
|
||||
{
|
||||
assetId: 'VIDEO-1-h264_1500k_mp4',
|
||||
title: 'h264_1500k_mp4',
|
||||
artist: 'Rain',
|
||||
mediaUri: 'https://mazwai.com/videvo_files/video/free/2015-05/small_watermarked/benjamin_wu--raccoon_come_and_go_preview.webm',
|
||||
mediaType: 'VIDEO',
|
||||
mediaSize: 1000,
|
||||
startPosition: 0,
|
||||
duration: 100000,
|
||||
mediaImage: 'https://cdn.pixabay.com/photo/2023/10/28/09/20/darling-8346954_1280.jpg',
|
||||
albumTitle: ' 《Trxye - EP》 ',
|
||||
appName: 'Spotify',
|
||||
},
|
||||
{
|
||||
assetId: 'VIDEO-2-oceans',
|
||||
title: 'oceans.mp4',
|
||||
artist: 'Rain',
|
||||
mediaUri: 'https://mazwai.com/videvo_files/video/free/2014-08/small_watermarked/clint_melander--a_day_without_rain_preview.webm',
|
||||
mediaType: 'VIDEO',
|
||||
mediaSize: 1000,
|
||||
startPosition: 0,
|
||||
duration: 100000,
|
||||
mediaImage: 'https://cdn.pixabay.com/photo/2023/10/20/13/49/beach-8329531_1280.jpg',
|
||||
albumTitle: ' 《Trxye - EP》 ',
|
||||
appName: 'Spotify',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 type image from '@ohos.multimedia.image';
|
||||
|
||||
export class PixelMapWrapper {
|
||||
pixelMap: image.PixelMap = undefined;
|
||||
width: number = 0;
|
||||
height: number = 0;
|
||||
|
||||
constructor(pixelMap: image.PixelMap, width: number, height: number) {
|
||||
this.pixelMap = pixelMap;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
release(): void {
|
||||
if (this.pixelMap !== null && this.pixelMap !== undefined) {
|
||||
this.pixelMap.release();
|
||||
}
|
||||
this.pixelMap = undefined;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class Size {
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
}
|
||||
|
||||
export enum CircleAngle {
|
||||
ONE_QUARTER_CIRCLE_ANGLE = 90,
|
||||
HALF_CIRCLE_ANGEL = 180,
|
||||
THREE_QUARTER_CIRCLE_ANGLE = 270,
|
||||
CIRCLE_ANGLE = 360
|
||||
}
|
||||
|
||||
export class GlobalContext {
|
||||
private constructor() {};
|
||||
private static instance: GlobalContext;
|
||||
private _objects = new Map<string, Object>();
|
||||
|
||||
public static getContext(): GlobalContext {
|
||||
if (!GlobalContext.instance) {
|
||||
GlobalContext.instance = new GlobalContext();
|
||||
}
|
||||
return GlobalContext.instance;
|
||||
}
|
||||
|
||||
getObject(value: string): Object | undefined {
|
||||
return this._objects.get(value);
|
||||
}
|
||||
|
||||
setObject(key: string, objectClass: Object): void {
|
||||
this._objects.set(key, objectClass);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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, { Permissions } from '@ohos.abilityAccessCtrl';
|
||||
|
||||
const TAG = 'AVPlayer';
|
||||
const permissions: Array<Permissions> = ['ohos.permission.READ_AUDIO'];
|
||||
|
||||
export default class PermissionUtils {
|
||||
public static init(context: Context, callback: Function) {
|
||||
console.log(TAG, 'start init permission');
|
||||
let atManager = abilityAccessCtrl.createAtManager();
|
||||
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
|
||||
|
||||
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
|
||||
let grantStatus: Array<number> = data.authResults;
|
||||
let length: number = grantStatus.length;
|
||||
for (let i = 0; i < length; i ++) {
|
||||
if(grantStatus[i] !== 0) {
|
||||
console.info(TAG, `User reject permission: ${grantStatus[i]}`);
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.info(TAG, `get permission succeed`);
|
||||
callback(true);
|
||||
}).catch((err: Error) => {
|
||||
console.error(TAG, `Failed to request permissions from user. err : ${JSON.stringify(err)}`);
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 AbilityConstant from '@ohos.app.ability.AbilityConstant';
|
||||
import hilog from '@ohos.hilog';
|
||||
import UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import Want from '@ohos.app.ability.Want';
|
||||
import window from '@ohos.window';
|
||||
import Constants from '../common/Constants';
|
||||
import { GlobalContext } from '../common/MediaAssetThumbnailLoader';
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate', JSON.stringify(want), JSON.stringify(launchParam));
|
||||
GlobalContext.getContext().setObject('context', this.context);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
// Main window is created, set main page for this ability
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
|
||||
|
||||
windowStage.loadContent('pages/Index', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
|
||||
});
|
||||
windowStage.getMainWindow().then(windowClass => {
|
||||
try {
|
||||
windowClass.setWindowSystemBarProperties({
|
||||
statusBarColor: Constants.THEME_COLOR,
|
||||
statusBarContentColor: '#000000',
|
||||
}, (err) => {
|
||||
if (err.code) {
|
||||
console.error('Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
|
||||
return;
|
||||
}
|
||||
console.info('Succeeded in setting the system bar properties.');
|
||||
})
|
||||
let orientation = window.Orientation.AUTO_ROTATION;
|
||||
windowClass.setPreferredOrientation(orientation, (err) => {
|
||||
if (err.code) {
|
||||
console.error('Failed to set window orientation. Cause: ' + JSON.stringify(err));
|
||||
return;
|
||||
}
|
||||
console.info('Succeeded in setting window orientation.');
|
||||
})
|
||||
} catch (exception) {
|
||||
console.error('Failed to set system bar to be invisible. Cause: ' + JSON.stringify(exception));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
// Main window is destroyed, release UI related resources
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
// Ability has brought to foreground
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
// Ability has back to background
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
|
||||
}
|
||||
}
|
@ -0,0 +1,970 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 avSession from '@ohos.multimedia.avsession';
|
||||
import media from '@ohos.multimedia.media';
|
||||
import promptAction from '@ohos.promptAction';
|
||||
import CommonUtils from '../common/CommonUtils';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import AudioUtils from '../common/AudioUtils';
|
||||
import connection from '@ohos.net.connection';
|
||||
import wantAgent from '@ohos.app.ability.wantAgent';
|
||||
import Constants from '../common/Constants';
|
||||
import AVCastPicker from '@ohos.multimedia.avCastPicker';
|
||||
import audio from '@ohos.multimedia.audio';
|
||||
import fs from '@ohos.file.fs';
|
||||
import util from '@ohos.util';
|
||||
import image from '@ohos.multimedia.image';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
|
||||
const TAG = 'AVCastDemo';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@State message: string = 'Hello World';
|
||||
@State outputDevice: avSession.OutputDeviceInfo = {devices: []};
|
||||
@State outputDeviceInfo: avSession.OutputDeviceInfo = {devices: []};
|
||||
@State castController: avSession.AVCastController | undefined = undefined;
|
||||
@State castControllerSession: avSession.AVSessionController | undefined = undefined;
|
||||
@State session: avSession.AVSession | undefined = undefined;
|
||||
@State controller: avSession.AVSessionController | undefined = undefined;
|
||||
@State albumImage: image.PixelMap | undefined = undefined;
|
||||
@State playType: 'local' | 'cast' = 'local';
|
||||
@State playState: number = -1;
|
||||
@State isFavorMap: Map<string, boolean> = new Map();
|
||||
@State volume: number = 0 ;
|
||||
@State seedPosition: number = 0;
|
||||
@State duration: number = 0;
|
||||
@State private currentIndex: number = 0;
|
||||
@State @Watch('playInfoUpdated') currentPlayInfo: avSession.AVMediaDescription | undefined = undefined;
|
||||
@State currentMediaId: string = '';
|
||||
@State currentLoopMode: number = 2;
|
||||
@State hasNetwork: boolean = false;
|
||||
@State isProgressSliding: boolean = false;
|
||||
@State audioType: 'url' | 'rawfile' | 'scan' | 'video' = 'url';
|
||||
private audioUtils: AudioUtils = new AudioUtils();
|
||||
private avPlayer: media.AVPlayer | undefined = undefined;
|
||||
private audioVolumeGroupManager: audio.AudioVolumeGroupManager | undefined = undefined;
|
||||
private localAudioRation = 1;
|
||||
private audioManager: audio.AudioManager | undefined = undefined;
|
||||
private netCon?: connection.NetConnection;
|
||||
private sliderTimer?: number;
|
||||
private mXComponentController: XComponentController = new XComponentController();
|
||||
@State private songList: Array<avSession.AVMediaDescription> = [];
|
||||
private urlVideoList: Array<avSession.AVMediaDescription> = Constants.URL_VIDEO_LIST;
|
||||
|
||||
async aboutToAppear() {
|
||||
console.log(TAG, 'about to appear');
|
||||
this.songList = this.urlVideoList;
|
||||
this.audioType = 'video';
|
||||
this.currentPlayInfo = this.urlVideoList[0];
|
||||
this.avPlayer = await this.audioUtils.init();
|
||||
this.avPlayer?.on('audioInterrupt', (info: audio.InterruptEvent) => {
|
||||
console.info(TAG, 'audioInterrupt success, and InterruptEvent info is: ' + info);
|
||||
if (this.avPlayer?.state === 'playing') {
|
||||
console.info(TAG, 'audio interrupt, start pause');
|
||||
this.avPlayer?.pause();
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
|
||||
promptAction.showToast({ message: 'audio interrupt, pause done' });
|
||||
}
|
||||
})
|
||||
this.avPlayer?.on('timeUpdate', (time: number) => {
|
||||
console.info(TAG, 'timeUpdate time: ' + time);
|
||||
if (!this.isProgressSliding) {
|
||||
if (this.duration == 0) {
|
||||
this.seedPosition = 0;
|
||||
} else {
|
||||
this.seedPosition = time / this.duration * 100;
|
||||
}
|
||||
const params: avSession.AVPlaybackState = {
|
||||
position: {
|
||||
elapsedTime: time,
|
||||
updateTime: new Date().getTime()
|
||||
},
|
||||
};
|
||||
this.session?.setAVPlaybackState(params);
|
||||
}
|
||||
})
|
||||
this.avPlayer?.on('durationUpdate', (duration: number) => {
|
||||
console.info(TAG, 'durationUpdate duration: ' + duration);
|
||||
this.duration = duration;
|
||||
if (this.duration !== 0) {
|
||||
const playMetaData: avSession.AVMetadata = {
|
||||
assetId: this.currentPlayInfo?.assetId as string, // origin assetId
|
||||
title: this.currentPlayInfo?.title as string,
|
||||
artist: this.currentPlayInfo?.artist as string,
|
||||
mediaImage: this.albumImage, // origin mediaImage
|
||||
album: this.currentPlayInfo?.albumTitle as string,
|
||||
duration: this.duration
|
||||
}
|
||||
this.session?.setAVMetadata(playMetaData);
|
||||
}
|
||||
|
||||
})
|
||||
this.avPlayer?.on('videoSizeChange', (width: number, height: number) => {
|
||||
console.info(TAG, 'videoSizeChange success, and width is: ' + width + ', height is: ' + height);
|
||||
})
|
||||
await this.setAudioManager();
|
||||
await this.autoStartAll(false);
|
||||
this.addNetworkListener();
|
||||
this.readLRCFile();
|
||||
console.info(TAG, 'about to appear done: ' + !!this.avPlayer);
|
||||
}
|
||||
|
||||
readLRCFile(): void {
|
||||
const context = getContext(this) as common.UIAbilityContext;
|
||||
context.resourceManager.getRawFileContent('test.lrc', (error: BusinessError, value: Uint8Array) => {
|
||||
if (error != null) {
|
||||
console.log(TAG, 'error is: ' + error);
|
||||
} else {
|
||||
let rawFile = value;
|
||||
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true });
|
||||
let retStr = textDecoder.decodeWithStream(rawFile, { stream: false });
|
||||
console.log(TAG, 'get lrc file: ' + retStr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addNetworkListener(): void {
|
||||
console.log(TAG, 'start add Network Listener');
|
||||
this.netCon = connection.createNetConnection();
|
||||
this.netCon?.register((error: BusinessError) => {
|
||||
console.error(TAG, 'network error: ' + JSON.stringify(error));
|
||||
})
|
||||
connection.getAllNets().then(data => {
|
||||
console.log(TAG, 'get all network: ' + JSON.stringify(data));
|
||||
this.hasNetwork = data?.length > 0;
|
||||
})
|
||||
this.netCon?.on('netAvailable', data => {
|
||||
console.log(TAG, 'network Available: ' + JSON.stringify(data));
|
||||
this.hasNetwork = true;
|
||||
})
|
||||
this.netCon?.on('netLost', data => {
|
||||
console.log(TAG, 'network Lost: ' + JSON.stringify(data));
|
||||
connection.getAllNets().then(data => {
|
||||
console.log(TAG, 'get all network: ' + JSON.stringify(data));
|
||||
this.hasNetwork = data?.length > 0;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
console.log(TAG, 'indexPage onPAgeHide in.');
|
||||
}
|
||||
|
||||
async playInfoUpdated() {
|
||||
console.log(TAG, 'playInfoUpdated: ' + JSON.stringify(this.currentPlayInfo));
|
||||
this.currentMediaId = this.currentPlayInfo?.assetId as string;
|
||||
this.albumImage = this.currentPlayInfo?.mediaImage as image.PixelMap
|
||||
if (this.playType === 'local') {
|
||||
await this.setLocalMediaInfo();
|
||||
} else {
|
||||
await this.setRemoteMediaInfo();
|
||||
}
|
||||
console.log(TAG, 'playInfoUpdate: done')
|
||||
}
|
||||
|
||||
async setRemoteMediaInfo() {
|
||||
console.log(TAG, 'set remote media info: ' + JSON.stringify(this.currentPlayInfo) + ', ' + this.currentIndex);
|
||||
let queueItem: avSession.AVQueueItem = {
|
||||
itemId: this.currentIndex,
|
||||
description: this.currentPlayInfo
|
||||
};
|
||||
|
||||
await this.castController?.prepare(queueItem);
|
||||
const isPlaying = this.playState === avSession.PlaybackState.PLAYBACK_STATE_PLAY;
|
||||
if (isPlaying) {
|
||||
await this.castController?.start(queueItem);
|
||||
await this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
}
|
||||
if (this.audioType === 'scan') {
|
||||
const playMetaData: avSession.AVMetadata = {
|
||||
assetId: this.currentPlayInfo?.assetId as string, // origin assetId
|
||||
title: this.currentPlayInfo?.title as string,
|
||||
artist: this.currentPlayInfo?.artist as string,
|
||||
mediaImage: this.albumImage, // origin mediaImage
|
||||
album: this.currentPlayInfo?.albumTitle as string,
|
||||
duration: this.duration,
|
||||
};
|
||||
console.log(TAG, 'try set AV Metadata while cast for scan: ' + JSON.stringify(playMetaData));
|
||||
this.session?.setAVMetadata(playMetaData);
|
||||
}
|
||||
console.log(TAG, 'set remote media info done');
|
||||
}
|
||||
|
||||
async setLocalMediaInfo() {
|
||||
console.log(TAG, 'set local media info: ' + JSON.stringify(this.currentPlayInfo));
|
||||
if (!this.session) {
|
||||
console.log(TAG, 'set local media info: no session');
|
||||
return;
|
||||
}
|
||||
if (this.audioUtils) {
|
||||
const isPlaying = this.playState === avSession.PlaybackState.PLAYBACK_STATE_PLAY;
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE, this.currentPlayInfo?.assetId,
|
||||
!!this.isFavorMap[this.currentPlayInfo?.assetId as string]);
|
||||
console.log(TAG, 'set play state pause');
|
||||
if (this.audioType === 'url' || this.audioType === 'video') {
|
||||
await this.audioUtils.loadFromNetwork(this.currentPlayInfo?.mediaUri as string);
|
||||
} else if (this.audioType === 'rawfile') {
|
||||
await this.audioUtils.loadFromNetwork(this.currentPlayInfo?.mediaUri as string);
|
||||
} else if (this.audioType === 'scan') {
|
||||
await this.audioUtils.loadFromSrcFd(this.currentPlayInfo?.fdSrc as media.AVFileDescriptor);
|
||||
}
|
||||
console.log(TAG, 'local local audio done: ' + isPlaying + ', ' + this.playType + ', ' + this.avPlayer?.state);
|
||||
if (isPlaying) {
|
||||
this.audioUtils.on('prepared', () => {
|
||||
console.log(TAG, 'AVPlayer state prepare, state play');
|
||||
if (this.playType === 'local') {
|
||||
this.localPlayOrPause();
|
||||
} else {
|
||||
this.remotePlayOrPause();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.audioUtils.on('completed', () => {
|
||||
console.log(TAG, 'AVPlayer state completed, state to stop');
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
|
||||
});
|
||||
} else {
|
||||
console.log(TAG, 'set local media fail: no audioUtils');
|
||||
}
|
||||
this.albumImage = this.currentPlayInfo?.mediaImage as image.PixelMap;
|
||||
const playMetaData: avSession.AVMetadata = {
|
||||
assetId: this.currentPlayInfo?.assetId as string, // origin assetId
|
||||
title: this.currentPlayInfo?.title as string,
|
||||
artist: this.currentPlayInfo?.artist as string,
|
||||
mediaImage: this.albumImage, // origin mediaImage
|
||||
album: this.currentPlayInfo?.albumTitle as string,
|
||||
duration: this.duration,
|
||||
};
|
||||
console.log(TAG, 'try set AV Metadata: ' + JSON.stringify(playMetaData));
|
||||
this.session?.setAVMetadata(playMetaData);
|
||||
console.log(TAG, 'set AV Metadata: ');
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
console.log(TAG, 'about to disappear');
|
||||
if (this.controller) {
|
||||
this.controller.off('outputDeviceChange');
|
||||
this.controller.destroy();
|
||||
}
|
||||
if (this.castController) {
|
||||
this.castController?.off('playbackStateChange');
|
||||
this.castController?.off('error');
|
||||
this.castController?.off('playPrevious');
|
||||
this.castController?.off('playNext');
|
||||
}
|
||||
try {
|
||||
if (this.session) {
|
||||
this.session?.stopCasting();
|
||||
this.session?.destroy();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(TAG, err);
|
||||
}
|
||||
if (this.avPlayer) {
|
||||
this.avPlayer?.release();
|
||||
}
|
||||
// 使用unregister接口取消订阅
|
||||
this.netCon?.unregister((error) => {
|
||||
console.log(JSON.stringify(error));
|
||||
})
|
||||
}
|
||||
|
||||
async setAudioManager() {
|
||||
console.log(TAG, 'try get audio manger');
|
||||
const audioManager = audio.getAudioManager();
|
||||
if (!audioManager) {
|
||||
console.error(TAG, 'get audio manager fail: fail get audioManager');
|
||||
return;
|
||||
}
|
||||
this.audioManager = audioManager;
|
||||
const volumeManager = audioManager.getVolumeManager();
|
||||
if (!volumeManager) {
|
||||
console.error(TAG, 'get audio manager fail: fail get volumeManager');
|
||||
return;
|
||||
}
|
||||
volumeManager.on('volumeChange', (volumeEvent) => {
|
||||
console.info(`VolumeType of stream : ${JSON.stringify(volumeEvent)}`);
|
||||
let type: audio.AudioVolumeType = volumeEvent.volumeType;
|
||||
let num: number = volumeEvent.volume;
|
||||
if(type == audio.AudioVolumeType.MEDIA && this.playType === 'local') {
|
||||
this.volume = num / this.localAudioRation;
|
||||
}
|
||||
});
|
||||
this.audioVolumeGroupManager = await volumeManager.getVolumeGroupManager(audio.DEFAULT_VOLUME_GROUP_ID);
|
||||
if (!this.audioVolumeGroupManager) {
|
||||
console.error(TAG, 'get audio manager fail: fail get audioVolumeGroupManager');
|
||||
return;
|
||||
}
|
||||
const maxVolume = await this.audioVolumeGroupManager.getVolume(audio.AudioVolumeType.MEDIA);
|
||||
const minVolume = await this.audioVolumeGroupManager.getVolume(audio.AudioVolumeType.MEDIA);
|
||||
const volume = await this.audioVolumeGroupManager.getVolume(audio.AudioVolumeType.MEDIA);
|
||||
this.localAudioRation = (maxVolume - minVolume) / 100;
|
||||
this.volume = volume / this.localAudioRation;
|
||||
console.log(TAG, 'get audio manager done, ' + maxVolume + ', ' + minVolume + ', ' + this.localAudioRation);
|
||||
}
|
||||
|
||||
async setPlayState(state?: number, id?: string, favor?: boolean, elapsedTime?: number) {
|
||||
if (!this.session) {
|
||||
console.log(TAG, 'fail set state, session undefined');
|
||||
promptAction.showToast({ message: 'No Session' });
|
||||
return null;
|
||||
}
|
||||
const params: avSession.AVPlaybackState = {};
|
||||
if (typeof state !== 'undefined') {
|
||||
this.playState = state;
|
||||
params.state = state;
|
||||
}
|
||||
if (typeof id !== 'undefined') {
|
||||
this.isFavorMap[id] = favor;
|
||||
params.isFavorite = favor;
|
||||
}
|
||||
// 更新播放进度
|
||||
if (elapsedTime !== undefined) {
|
||||
params.position = {
|
||||
elapsedTime: elapsedTime,
|
||||
updateTime: new Date().getTime(),
|
||||
}
|
||||
}
|
||||
this.session?.setAVPlaybackState(params);
|
||||
console.log(TAG, 'params test ' + JSON.stringify(params));
|
||||
console.log(TAG, 'isFavorMap test, ' + id + ', ' + JSON.stringify(this.isFavorMap));
|
||||
return this.session?.setAVPlaybackState(params);
|
||||
}
|
||||
|
||||
async setListenerForMesFromController() {
|
||||
console.info(TAG, 'setListenerForMesFromController');
|
||||
this.session?.on('play', () => {
|
||||
console.info(TAG, 'on play, do play test');
|
||||
if (this.avPlayer) {
|
||||
this.avPlayer?.play();
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
}
|
||||
});
|
||||
this.session?.on('pause', () => {
|
||||
console.info(TAG, 'on pause, do pause test');
|
||||
if (this.avPlayer) {
|
||||
this.avPlayer?.pause();
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
|
||||
}
|
||||
});
|
||||
this.session?.on('stop', () => {
|
||||
console.info(TAG, 'on stop, do stop test');
|
||||
if (this.avPlayer) {
|
||||
this.avPlayer?.stop();
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_STOP);
|
||||
}
|
||||
});
|
||||
this.session?.on('playPrevious', () => {
|
||||
console.info(TAG, 'on playPrevious, do playPrevious test');
|
||||
this.switchToPreviousByLoopMode();
|
||||
});
|
||||
this.session?.on('playNext', () => {
|
||||
console.info(TAG, 'on playNext, do playNext test');
|
||||
this.switchToNextByLoopMode();
|
||||
});
|
||||
this.session?.on('toggleFavorite', (id) => {
|
||||
console.info(TAG, 'on toggleFavorite session, do toggleFavorite test: ' + id);
|
||||
this.setPlayState(undefined, id, !this.isFavorMap[id]);
|
||||
});
|
||||
// 注册播放快退命令监听
|
||||
this.session?.on('rewind', (time?: number) => {
|
||||
time = time ? time : 0;
|
||||
let currentTime = this.avPlayer ? this.avPlayer.currentTime : 0;
|
||||
let timeMs: number = currentTime - time * 1000 <= 0 ? 0 : currentTime - time *1000;
|
||||
this.avPlayer?.seek(timeMs);
|
||||
console.info(TAG, 'rewind currentTime ' + timeMs);
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
});
|
||||
// 注册播放快进命令监听
|
||||
this.session?.on('fastForward', (time?: number) => {
|
||||
time = time ? time : 0;
|
||||
let currentTime = this.avPlayer ? this.avPlayer.currentTime : 0;
|
||||
let timeMs: number = time * 1000 + currentTime > this.duration ? this.duration : time *1000 + currentTime;
|
||||
if (time * 1000 + currentTime > this.duration) {
|
||||
this.switchToNextByLoopMode();
|
||||
} else {
|
||||
this.avPlayer?.seek(timeMs);
|
||||
this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
}
|
||||
});
|
||||
this.session?.on('seek', (position) => {
|
||||
console.info(TAG, 'on seek: seek test: ' + position);
|
||||
// 修改播放进度
|
||||
this.avPlayer?.seek(position);
|
||||
// 重新设置播放进度
|
||||
const params: avSession.AVPlaybackState = {
|
||||
position: {
|
||||
elapsedTime: position,
|
||||
updateTime: new Date().getTime(),
|
||||
},
|
||||
};
|
||||
this.session?.setAVPlaybackState(params);
|
||||
});
|
||||
}
|
||||
|
||||
async unregisterSessionListener() {
|
||||
if (this.session) {
|
||||
this.session?.off('play');
|
||||
this.session?.off('pause');
|
||||
this.session?.off('stop');
|
||||
this.session?.off('playNext');
|
||||
this.session?.off('playPrevious');
|
||||
this.session?.off('seek');
|
||||
// 主动销毁已创建的session
|
||||
this.session?.destroy((err) => {
|
||||
if (err) {
|
||||
console.info(TAG, `Destroy BusinessError: code: ${err.code}, message: ${err.message}`);
|
||||
} else {
|
||||
console.info(TAG, 'Destroy: SUCCESS ');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateVolume(value: number) {
|
||||
console.info(TAG, 'update volume: ' + this.playType + ', ' + value);
|
||||
if (this.volume === value) {
|
||||
console.info(TAG, 'update volume: volume not change');
|
||||
return;
|
||||
}
|
||||
this.volume = value;
|
||||
if (this.playType === 'cast' && this.castController) {
|
||||
this.castController?.sendControlCommand({
|
||||
command: 'setVolume',
|
||||
parameter: value,
|
||||
});
|
||||
}
|
||||
if (this.playType === 'local' && this.audioManager) {
|
||||
console.info(TAG, 'update local volume: ' + value);
|
||||
this.audioManager.setVolume(audio.AudioVolumeType.MEDIA, value * this.localAudioRation);
|
||||
}
|
||||
}
|
||||
|
||||
async localPlayOrPause() {
|
||||
console.info(TAG, 'start local play or pause', this.avPlayer?.state);
|
||||
if (!this.avPlayer) {
|
||||
console.error(TAG, 'no avplayer');
|
||||
return;
|
||||
}
|
||||
if (this.avPlayer?.state === 'playing') {
|
||||
console.info(TAG, 'start pause');
|
||||
await this.avPlayer?.pause();
|
||||
await this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
|
||||
promptAction.showToast({message: 'pause done'});
|
||||
} else if (this.avPlayer?.state === 'stopped') {
|
||||
console.info(TAG, 'start play from stopped');
|
||||
await this.avPlayer?.prepare();
|
||||
await this.avPlayer?.play();
|
||||
await this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
promptAction.showToast({message: 'play done'});
|
||||
} else {
|
||||
console.info(TAG, 'start play from stopped');
|
||||
await this.avPlayer?.play();
|
||||
await this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
|
||||
promptAction.showToast({message: 'play done'});
|
||||
console.info(TAG, 'start play done');
|
||||
}
|
||||
}
|
||||
|
||||
async remotePlayOrPause() {
|
||||
console.info(TAG, 'start remote play or pause', this.playState);
|
||||
if (!this.castController) {
|
||||
console.error(TAG, 'no castController found');
|
||||
return;
|
||||
}
|
||||
if (this.playState === avSession.PlaybackState.PLAYBACK_STATE_INITIAL
|
||||
|| this.playState === avSession.PlaybackState.PLAYBACK_STATE_PREPARE) {
|
||||
console.info(TAG, 'start');
|
||||
let queueItem: avSession.AVQueueItem = {
|
||||
itemId: 0,
|
||||
description: this.currentPlayInfo
|
||||
};
|
||||
|
||||
await this.castController?.start(queueItem);
|
||||
this.playState = avSession.PlaybackState.PLAYBACK_STATE_PLAY;
|
||||
} else if (this.playState === avSession.PlaybackState.PLAYBACK_STATE_PLAY) {
|
||||
console.info(TAG, 'pause');
|
||||
this.castController?.sendControlCommand({
|
||||
command: 'pause',
|
||||
})
|
||||
this.playState = avSession.PlaybackState.PLAYBACK_STATE_PAUSE;
|
||||
} else {
|
||||
console.info(TAG, 'play');
|
||||
this.castController?.sendControlCommand({
|
||||
command: 'play',
|
||||
})
|
||||
this.playState = avSession.PlaybackState.PLAYBACK_STATE_PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
async autoStartAll(needStart: boolean){
|
||||
console.info(TAG, 'try auto start all');
|
||||
console.info(TAG, 'create session');
|
||||
this.session = await avSession.createAVSession(getContext(), 'audiotestr', 'video');
|
||||
this.session?.setExtras({
|
||||
requireAbilityList: ['url-cast'],
|
||||
});
|
||||
const params: avSession.AVPlaybackState = {
|
||||
position: {
|
||||
elapsedTime: 0,
|
||||
updateTime: new Date().getTime()
|
||||
},
|
||||
};
|
||||
console.info(TAG, 'try SET SESSION PLAYSTATE');
|
||||
this.session?.setAVPlaybackState(params);
|
||||
console.info(TAG, 'create session res: ' + JSON.stringify(this.session));
|
||||
if (!this.session) {
|
||||
console.error(TAG, 'fail to create session');
|
||||
return;
|
||||
}
|
||||
console.info(TAG, 'create controller: ' + this.session?.sessionId);
|
||||
this.controller = await this.session?.getController();
|
||||
if (!this.controller) {
|
||||
console.error(TAG, 'fail to create controller');
|
||||
return;
|
||||
}
|
||||
console.info(TAG, 'create controller done: ' + this.controller.sessionId);
|
||||
|
||||
console.info(TAG, 'add outputDeviceChange listener');
|
||||
this.controller.on('outputDeviceChange', async (connectState: avSession.ConnectionState,
|
||||
device: avSession.OutputDeviceInfo) => {
|
||||
this.outputDeviceInfo = device;
|
||||
promptAction.showToast({ message: 'output device changed: ' + connectState });
|
||||
if (connectState === avSession.ConnectionState.STATE_CONNECTING) {
|
||||
console.info(TAG, 'connecting');
|
||||
return;
|
||||
}
|
||||
const isPlaying = this.avPlayer && this.avPlayer?.state === 'playing';
|
||||
console.info(TAG, 'outputDeviceChange res: ' + JSON.stringify(device) + '|' + connectState + ',' + isPlaying);
|
||||
await this.processDeviceChange(connectState, device);
|
||||
console.info(TAG, `process Device Change done, ${this.playType}, ${!!this.castController}`);
|
||||
if (this.playType === 'cast' && this.castController) {
|
||||
console.info(TAG, 'prepare remote audio info ' + ', ' + isPlaying);
|
||||
const queueItem: avSession.AVQueueItem = {
|
||||
itemId: this.currentIndex,
|
||||
description: this.currentPlayInfo
|
||||
};
|
||||
console.info(TAG, `try prepare info, ${JSON.stringify(queueItem)}`);
|
||||
|
||||
await this.castController?.prepare(queueItem);
|
||||
if (isPlaying) {
|
||||
|
||||
await this.castController?.start(queueItem);
|
||||
}
|
||||
await this.castController?.sendControlCommand({
|
||||
command: 'setLoopMode',
|
||||
parameter: this.currentLoopMode,
|
||||
});
|
||||
}
|
||||
console.info(TAG, 'output device change processing finished');
|
||||
})
|
||||
console.info(TAG, 'add outputDeviceChange Listener done');
|
||||
console.info(TAG, 'try prepare local audio: ' + this.session?.sessionId);
|
||||
this.setListenerForMesFromController();
|
||||
await this.session?.activate();
|
||||
await this.setLocalMediaInfo();
|
||||
await this.setPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
|
||||
if (needStart) {
|
||||
console.info(TAG, 'start play local');
|
||||
setTimeout(() => {
|
||||
this.localPlayOrPause();
|
||||
}, 100);
|
||||
}
|
||||
wantAgent.getWantAgent({
|
||||
wants: [
|
||||
{
|
||||
bundleName: 'com.samples.videoplayer',
|
||||
abilityName: 'com.samples.videoplayer.EntryAbility'
|
||||
}
|
||||
],
|
||||
operationType: wantAgent.OperationType.START_ABILITIES,
|
||||
requestCode: 0,
|
||||
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
|
||||
}).then((agent) => {
|
||||
this.session?.setLaunchAbility(agent);
|
||||
})
|
||||
promptAction.showToast({ message: 'auto start done' });
|
||||
}
|
||||
|
||||
async switchToPreviousByLoopMode(){
|
||||
console.info(TAG, 'switch to previous by loop mode: ' + this.currentLoopMode);
|
||||
if (this.currentLoopMode === avSession.LoopMode.LOOP_MODE_SINGLE) {
|
||||
this.playInfoUpdated();
|
||||
return;
|
||||
}
|
||||
if (this.currentLoopMode === avSession.LoopMode.LOOP_MODE_SINGLE) {
|
||||
const random = Math.floor((Math.random() * 100) + 1);
|
||||
const target = random % this.songList.length;
|
||||
if (target === this.currentIndex) {
|
||||
this.currentIndex = target === 0 ? this.songList.length - 1 : target - 1;
|
||||
} else {
|
||||
this.currentIndex = target;
|
||||
}
|
||||
this.updateCurrentPlayInfo(this.songList[this.currentIndex], this.audioType);
|
||||
return;
|
||||
}
|
||||
this.currentIndex = this.currentIndex === 0 ? this.songList.length - 1 : this.currentIndex - 1;
|
||||
this.updateCurrentPlayInfo(this.songList[this.currentIndex], this.audioType);
|
||||
}
|
||||
|
||||
async switchToNextByLoopMode(){
|
||||
console.info(TAG, 'switch to next by loop mode: ' + this.currentLoopMode);
|
||||
if (this.currentLoopMode === avSession.LoopMode.LOOP_MODE_SINGLE) {
|
||||
this.playInfoUpdated();
|
||||
return;
|
||||
}
|
||||
if (this.currentLoopMode === avSession.LoopMode.LOOP_MODE_SHUFFLE) {
|
||||
const random = Math.floor((Math.random() * 100) + 1);
|
||||
const target = random % this.songList.length;
|
||||
if (target === this.currentIndex) {
|
||||
this.currentIndex = target === this.songList.length - 1 ? 0 : target + 1;
|
||||
} else {
|
||||
this.currentIndex = target;
|
||||
}
|
||||
this.updateCurrentPlayInfo(this.songList[this.currentIndex], this.audioType);
|
||||
return;
|
||||
}
|
||||
this.currentIndex = this.currentIndex === this.songList.length - 1 ? 0 : this.currentIndex + 1;
|
||||
this.updateCurrentPlayInfo(this.songList[this.currentIndex], this.audioType);
|
||||
}
|
||||
|
||||
async updateCurrentPlayInfo(item: avSession.AVMediaDescription, audioType: string){
|
||||
const temp: avSession.AVMediaDescription = {
|
||||
assetId: item.assetId,
|
||||
title: item.title,
|
||||
artist: item.artist,
|
||||
mediaType: item.mediaType,
|
||||
mediaSize: item.mediaSize,
|
||||
startPosition: item.startPosition,
|
||||
duration: item.duration,
|
||||
mediaImage: item.mediaImage,
|
||||
albumTitle: item.albumTitle,
|
||||
appName: item.appName,
|
||||
};
|
||||
if (audioType === 'scan') {
|
||||
let fd = 0;
|
||||
await fs.open(item.mediaUri).then(async (file) => {
|
||||
console.info(TAG, 'fs res: ' + file?.fd);
|
||||
fd = file?.fd
|
||||
if (fd != -1 && fd) {
|
||||
console.info(TAG, 'open fd suc: '+ fd);
|
||||
temp.fdSrc = {
|
||||
fd,
|
||||
};
|
||||
}
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(TAG, 'start local file cast: ' + JSON.stringify(err));
|
||||
})
|
||||
} else {
|
||||
temp.mediaUri = item.mediaUri;
|
||||
}
|
||||
this.currentPlayInfo = temp;
|
||||
}
|
||||
|
||||
async processDeviceChange(connectState: avSession.ConnectionState, device: avSession.OutputDeviceInfo){
|
||||
if (device?.devices?.[0].castCategory === 0 || connectState === avSession.ConnectionState.STATE_DISCONNECTED) {
|
||||
this.playType = 'local';
|
||||
this.playState = avSession.PlaybackState.PLAYBACK_STATE_PAUSE;
|
||||
if (this.audioVolumeGroupManager) {
|
||||
const volume = await this.audioVolumeGroupManager.getVolume(audio.AudioVolumeType.MEDIA);
|
||||
this.volume = volume / this.localAudioRation;
|
||||
}
|
||||
await this.setLocalMediaInfo();
|
||||
return;
|
||||
}
|
||||
this.playType = 'cast';
|
||||
const isRefresh = !!this.castController;
|
||||
this.castController = await this.session?.getAVCastController();
|
||||
if (!this.castController) {
|
||||
console.error(TAG, 'fail to get cast controller');
|
||||
return;
|
||||
}
|
||||
let avPlaybackState = await this.castController?.getAVPlaybackState();
|
||||
this.playState = avPlaybackState.state || 0;
|
||||
if (typeof avPlaybackState?.volume !== 'undefined' && avPlaybackState?.volume >= 0) {
|
||||
this.volume = avPlaybackState?.volume;
|
||||
}
|
||||
if (typeof avPlaybackState?.loopMode !== 'undefined') {
|
||||
this.currentLoopMode = avPlaybackState?.loopMode;
|
||||
}
|
||||
console.info(TAG, 'get AVPlaybackState res: ' + JSON.stringify(avPlaybackState) + ', ' + isRefresh);
|
||||
if (this.avPlayer && this.avPlayer?.state === 'playing') {
|
||||
console.info(TAG, 'stop avplayer');
|
||||
this.avPlayer?.stop();
|
||||
}
|
||||
console.info(TAG, 'set on playbackStateChange listener: ' + connectState);
|
||||
this.castController?.on('playbackStateChange', 'all', (state) => {
|
||||
console.info(TAG, 'play state change: ' + JSON.stringify(state));
|
||||
if (typeof state?.state !== 'undefined') {
|
||||
this.playState = state?.state;
|
||||
}
|
||||
if (typeof state?.volume !== 'undefined') {
|
||||
this.volume = state?.volume;
|
||||
}
|
||||
if (typeof state?.loopMode !== 'undefined') {
|
||||
this.currentLoopMode = state?.loopMode;
|
||||
}
|
||||
if (typeof state?.extras?.duration !== 'undefined') {
|
||||
this.duration = state?.extras?.duration as number;
|
||||
}
|
||||
if (typeof state?.position?.elapsedTime !== 'undefined' && !this.isProgressSliding) {
|
||||
this.seedPosition = (state?.position?.elapsedTime / this.duration) * 100;
|
||||
}
|
||||
});
|
||||
this.castController?.on('playPrevious', async (state) => {
|
||||
console.info(TAG, 'playPrevious: ' + JSON.stringify(state));
|
||||
this.switchToPreviousByLoopMode()
|
||||
});
|
||||
this.castController?.on('playNext', async (state) => {
|
||||
console.info(TAG, 'playNext: ' + JSON.stringify(state));
|
||||
this.switchToNextByLoopMode()
|
||||
});
|
||||
this.castController?.on('error', (err) => {
|
||||
console.info(TAG, 'on command error: ' + JSON.stringify(err));
|
||||
promptAction.showToast({ message: 'error: ' + JSON.stringify(err) });
|
||||
});
|
||||
console.info(TAG, 'set on playbackStateChange listener done')
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Flex({
|
||||
direction: FlexDirection.Column,
|
||||
justifyContent: FlexAlign.SpaceBetween,
|
||||
alignItems: ItemAlign.Center
|
||||
}) {
|
||||
// title
|
||||
Column() {
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceBetween,
|
||||
alignItems: ItemAlign.Center
|
||||
}) {
|
||||
Column() {
|
||||
Text($r('app.string.EntryAbility_title'))
|
||||
.fontWeight(FontWeight.Normal)
|
||||
.fontSize(24)
|
||||
.textAlign(TextAlign.Start)
|
||||
.width("100%")
|
||||
.fontColor(Color.White)
|
||||
}
|
||||
.width('70%')
|
||||
.height(24)
|
||||
|
||||
Button() {
|
||||
AVCastPicker()
|
||||
.size({ height: '100%', width: '100%' })
|
||||
.backgroundColor(Color.White)
|
||||
.align(Alignment.Center);
|
||||
}
|
||||
.width(24)
|
||||
.height(24)
|
||||
.backgroundColor(Color.Black)
|
||||
}
|
||||
.margin({ left: 24, right: 24, top: 12 })
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor(Color.Transparent)
|
||||
|
||||
// video
|
||||
if (this.playType === 'local') {
|
||||
Row() {
|
||||
Stack({ alignContent: Alignment.Bottom }) {
|
||||
XComponent({ id: '', type: 'surface', controller: this.mXComponentController })
|
||||
.onLoad(() => {
|
||||
const surfaceId = this.mXComponentController.getXComponentSurfaceId();
|
||||
console.log(TAG, 'XComponent onLoad, surfaceId = ' + surfaceId);
|
||||
this.audioUtils.surfaceId = surfaceId;
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height(200)
|
||||
}
|
||||
.flexShrink(0)
|
||||
.width('100%')
|
||||
} else {
|
||||
Row() {
|
||||
Stack({ alignContent: Alignment.Center }) {
|
||||
Text($r('app.string.EntryAbility_sink'))
|
||||
.fontColor(Color.White)
|
||||
.fontSize(28)
|
||||
}
|
||||
.width('100%')
|
||||
.height(200)
|
||||
.backgroundColor(Color.Grey)
|
||||
}
|
||||
.flexShrink(0)
|
||||
.width('100%')
|
||||
}
|
||||
|
||||
// control
|
||||
Row() {
|
||||
Flex({
|
||||
direction: FlexDirection.Column,
|
||||
justifyContent: FlexAlign.SpaceAround,
|
||||
alignItems: ItemAlign.Center
|
||||
}){
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceEvenly,
|
||||
alignItems: ItemAlign.Center
|
||||
})
|
||||
{
|
||||
Button() {
|
||||
Image($r('app.media.music_last'))
|
||||
.size({ width: '24vp', height: '24vp' })
|
||||
.fillColor(Color.White)
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
.size({
|
||||
width: '48vp',
|
||||
height: '48vp'
|
||||
})
|
||||
.backgroundColor(Color.Black)
|
||||
.onClick(() => {
|
||||
console.info(TAG, 'click play next');
|
||||
this.switchToPreviousByLoopMode();
|
||||
})
|
||||
.key('music_last')
|
||||
|
||||
Button() {
|
||||
Image(this.playState === 2 ? $r('app.media.music_stop') : $r('app.media.music_play'))
|
||||
.size({ width: '24vp', height: '24vp' })
|
||||
.fillColor($r('sys.color.ohos_id_color_primary'))
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
.size({
|
||||
width: '48vp',
|
||||
height: '48vp'
|
||||
})
|
||||
.backgroundColor(Color.Transparent)
|
||||
.onClick(() => {
|
||||
console.info(TAG, `click play/pause: ${this.playType} ,${this.session}, ${this.controller}`);
|
||||
if (!this.session && !this.controller) {
|
||||
this.autoStartAll(true);
|
||||
} else if (this.playType === 'local') {
|
||||
this.localPlayOrPause();
|
||||
} else {
|
||||
this.remotePlayOrPause();
|
||||
}
|
||||
})
|
||||
.key('music_play_or_pause')
|
||||
|
||||
Button() {
|
||||
Image($r('app.media.music_next'))
|
||||
.size({ width: '24vp', height: '24vp' })
|
||||
.fillColor($r('sys.color.ohos_id_color_primary'))
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
.size({
|
||||
width: '48vp',
|
||||
height: '48vp'
|
||||
})
|
||||
.backgroundColor(Color.Transparent)
|
||||
.onClick(() => {
|
||||
console.info(TAG, 'click play next');
|
||||
this.switchToNextByLoopMode();
|
||||
})
|
||||
.key('music_next')
|
||||
}
|
||||
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceEvenly,
|
||||
alignItems: ItemAlign.Center
|
||||
})
|
||||
{
|
||||
Text(`${CommonUtils.millSecond2Minutes(this.seedPosition / 100 * this.duration)}`)
|
||||
.fontWeight(FontWeight.Normal)
|
||||
.fontSize(12)
|
||||
.textAlign(TextAlign.Start)
|
||||
.fontColor('rgba(255,255,255,0.9)')
|
||||
|
||||
Slider({
|
||||
value: this.seedPosition,
|
||||
min: 0,
|
||||
max: 100,
|
||||
style: SliderStyle.OutSet
|
||||
})
|
||||
.trackThickness(this.isProgressSliding ? 8 : 4)
|
||||
.blockColor('rgba(255,255,255,1)')
|
||||
.trackColor('rgba(255,255,255,0.3)')
|
||||
.selectedColor('rgba(255,255,255,0.9)')
|
||||
.showSteps(false)
|
||||
.showTips(false)
|
||||
.onChange((value: number, mode: SliderChangeMode) => {
|
||||
console.info(TAG, 'value: ' + value + 'mode: ' + mode.toString() )
|
||||
if (mode === SliderChangeMode.End) {
|
||||
if (this.playType === 'local') {
|
||||
this.avPlayer?.seek(value / 100 * this.duration);
|
||||
const params: avSession.AVPlaybackState = {
|
||||
position: {
|
||||
elapsedTime: Math.floor(value / 100 * this.duration),
|
||||
updateTime: new Date().getTime(),
|
||||
},
|
||||
};
|
||||
this.session?.setAVPlaybackState(params);
|
||||
console.info(TAG, 'params.position + ' + JSON.stringify((params.position)))
|
||||
} else {
|
||||
this.castController?.sendControlCommand({
|
||||
command: 'seek',
|
||||
parameter: value / 100 * this.duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.seedPosition = value;
|
||||
})
|
||||
.width('70%')
|
||||
.height(2)
|
||||
.opacity(1)
|
||||
.onTouch((event: TouchEvent) => {
|
||||
console.info(TAG, 'progress touch: ' + event.type)
|
||||
if (event.type === TouchType.Up) {
|
||||
this.sliderTimer = setTimeout(() => {
|
||||
this.isProgressSliding = false;
|
||||
}, 200);
|
||||
} else {
|
||||
clearTimeout(this.sliderTimer);
|
||||
this.isProgressSliding = true;
|
||||
}
|
||||
})
|
||||
Text(`${CommonUtils.millSecond2Minutes(this.duration)}`)
|
||||
.fontWeight(FontWeight.Normal)
|
||||
.fontSize(12)
|
||||
.textAlign(TextAlign.Start)
|
||||
.fontColor('rgba(255,255,255,0.9)')
|
||||
}
|
||||
.width('100%')
|
||||
.height(50)
|
||||
.padding({ left: 10, right: 10 })
|
||||
}
|
||||
.padding({ top: 8 })
|
||||
}
|
||||
.width('100%')
|
||||
.height(150)
|
||||
.padding({ bottom: 20 })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor(Color.Black)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 avSession from '@ohos.multimedia.avsession';
|
||||
|
||||
@Component
|
||||
export struct SongItem {
|
||||
@State index: number = -1;
|
||||
@State item: avSession.AVMediaDescription | null = null;
|
||||
@Link currentPlayInfo: avSession.AVMediaDescription;
|
||||
|
||||
build(){
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceBetween,
|
||||
alignItems: ItemAlign.Center
|
||||
}) {
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceBetween,
|
||||
alignItems: ItemAlign.Center
|
||||
}){
|
||||
Text('' + (this.index + 1)).fontSize(12)
|
||||
.fontColor(this.currentPlayInfo.assetId === this.item?.assetId ? '#0A59F7' : Color.Black)
|
||||
.opacity(0.3)
|
||||
Flex({
|
||||
direction: FlexDirection.Row,
|
||||
justifyContent: FlexAlign.SpaceBetween,
|
||||
alignItems: ItemAlign.Center
|
||||
}){
|
||||
Text(this.item?.title)
|
||||
.fontSize(14)
|
||||
.opacity(1)
|
||||
.maxLines(1)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.fontColor(this.currentPlayInfo.assetId === this.item?.assetId ? '#0A59F7' : Color.Black)
|
||||
Text(this.item?.artist)
|
||||
.margin({ top: 8 })
|
||||
.fontSize(12)
|
||||
.opacity(1)
|
||||
.fontWeight(FontWeight.Regular)
|
||||
.fontColor(this.currentPlayInfo.assetId === this.item?.assetId ? '#0A59F7' : Color.Black)
|
||||
}.margin({ left: 12 })
|
||||
}
|
||||
.margin({ left: 0 })
|
||||
|
||||
Image($r('app.media.icon'))
|
||||
.size({ width: '24vp', height: '24vp' })
|
||||
.fillColor('#0A59F7')
|
||||
.backgroundColor(Color.Transparent)
|
||||
.margin({ left: 4 })
|
||||
.opacity(1)
|
||||
.visibility(this.currentPlayInfo.assetId === this.item?.assetId ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
.width('100%')
|
||||
.height(56)
|
||||
.padding({ right: 24 })
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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",
|
||||
"tablet"
|
||||
],
|
||||
"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:icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_title",
|
||||
"value": "Video playback"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_sink",
|
||||
"value": "Casting"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 822 B |
@ -0,0 +1,5 @@
|
||||
{
|
||||
"src": [
|
||||
"pages/Index"
|
||||
]
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "module description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_title",
|
||||
"value": "Video playback"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_sink",
|
||||
"value": "Casting"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_desc",
|
||||
"value": "模块描述"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_desc",
|
||||
"value": "description"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_label",
|
||||
"value": "label"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_title",
|
||||
"value": "视频播放"
|
||||
},
|
||||
{
|
||||
"name": "EntryAbility_sink",
|
||||
"value": "投播中"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 '@ohos.hilog';
|
||||
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
|
||||
import {Driver, ON, MatchPattern} from '@ohos.UiTest';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'
|
||||
|
||||
const TAG: string = 'SampleText';
|
||||
const abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
|
||||
const BUNDLE = 'Sample_';
|
||||
|
||||
export default function abilityTest() {
|
||||
|
||||
describe('ActsAbilityTest', () => {
|
||||
|
||||
/**
|
||||
* open_sample
|
||||
*/
|
||||
it(BUNDLE +'StartAbility_001', 0, async (done: Function) => {
|
||||
hilog.info(0x0, TAG, 'StartAbility_001 start');
|
||||
await abilityDelegator.startAbility({
|
||||
bundleName: 'com.samples.videoplayer',
|
||||
abilityName: 'EntryAbility'
|
||||
})
|
||||
hilog.info(0x0, TAG, 'StartAbility_001 end');
|
||||
done();
|
||||
})
|
||||
|
||||
/**
|
||||
* music_last
|
||||
*/
|
||||
it(BUNDLE + 'music_last', 0, async (done: Function) => {
|
||||
hilog.info(0x0, TAG, 'music_last start');
|
||||
let driver = await Driver.create();
|
||||
await driver.delayMs(1000);
|
||||
await driver.assertComponentExist(ON.id('music_last'));
|
||||
let lastButton = await driver.findComponent(ON.id('music_last'));
|
||||
await lastButton.click();
|
||||
await driver.delayMs(1000);
|
||||
|
||||
await driver.assertComponentExist(ON.id('music_play_or_pause'));
|
||||
let playOrPause = await driver.findComponent(ON.id('music_play_or_pause'));
|
||||
await playOrPause.click();
|
||||
await driver.delayMs(1000);
|
||||
await playOrPause.click();
|
||||
await driver.delayMs(1000);
|
||||
|
||||
await driver.assertComponentExist(ON.id('music_next'));
|
||||
let nextButton = await driver.findComponent(ON.id('music_next'));
|
||||
await nextButton.click();
|
||||
await driver.delayMs(1000);
|
||||
hilog.info(0x0, TAG, 'music_last end');
|
||||
done();
|
||||
})
|
||||
})
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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()
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
import hilog from '@ohos.hilog';
|
||||
import { Hypium } from '@ohos/hypium';
|
||||
import testsuite from '../test/List.test';
|
||||
import window from '@ohos.window';
|
||||
import Want from '@ohos.app.ability.Want';
|
||||
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
|
||||
|
||||
export default class TestAbility extends UIAbility {
|
||||
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
|
||||
let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
|
||||
let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs = AbilityDelegatorRegistry.getArguments()
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
|
||||
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
|
||||
windowStage.loadContent('testability/pages/Index', (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',JSON.stringify(data) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 '@ohos.hilog';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
@State message: string = 'Hello World';
|
||||
|
||||
aboutToAppear() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Column() {
|
||||
Text(this.message)
|
||||
.fontSize(50)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
Button() {
|
||||
Text('next page')
|
||||
.fontSize(20)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
}.type(ButtonType.Capsule)
|
||||
.margin({ top: 20 })
|
||||
.backgroundColor('#0D9FFB')
|
||||
.width('35%')
|
||||
.height('5%')
|
||||
.onClick(()=>{
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
.height('100%')
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import hilog from '@ohos.hilog';
|
||||
import TestRunner from '@ohos.application.testRunner';
|
||||
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
|
||||
|
||||
var abilityDelegator = undefined
|
||||
var abilityDelegatorArguments = undefined
|
||||
|
||||
async function onAbilityCreateCallback() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
|
||||
}
|
||||
|
||||
async function addAbilityMonitorCallback(err: any) {
|
||||
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
|
||||
}
|
||||
|
||||
export default class OpenHarmonyTestRunner implements TestRunner {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
onPrepare() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
|
||||
}
|
||||
|
||||
async onRun() {
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
|
||||
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
|
||||
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
|
||||
const bundleName = abilityDelegatorArguments.bundleName;
|
||||
const testAbilityName = 'TestAbility';
|
||||
let lMonitor = {
|
||||
abilityName: testAbilityName,
|
||||
onAbilityCreate: onAbilityCreateCallback,
|
||||
};
|
||||
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
|
||||
const want = {
|
||||
bundleName: bundleName,
|
||||
abilityName: testAbilityName
|
||||
};
|
||||
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
|
||||
abilityDelegator.startAbility(want, (err : any, data : any) => {
|
||||
hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
|
||||
hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? '');
|
||||
})
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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",
|
||||
"tablet"
|
||||
],
|
||||
"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:icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"exported": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.INTERNET"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"color": [
|
||||
{
|
||||
"name": "start_window_background",
|
||||
"value": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "module_test_desc",
|
||||
"value": "test ability description"
|
||||
},
|
||||
{
|
||||
"name": "TestAbility_desc",
|
||||
"value": "the test ability"
|
||||
},
|
||||
{
|
||||
"name": "TestAbility_label",
|
||||
"value": "test label"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.6 KiB |
@ -0,0 +1,5 @@
|
||||
{
|
||||
"src": [
|
||||
"testability/pages/Index"
|
||||
]
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"hvigorVersion": "3.0.9",
|
||||
"dependencies": {
|
||||
"@ohos/hvigor-ohos-plugin": "3.0.9"
|
||||
},
|
||||
"execution": {
|
||||
// "daemon": true, /* Enable daemon compilation. Default: true */
|
||||
// "incremental": true, /* Enable incremental compilation. Default: true */
|
||||
// "parallel": true, /* Enable parallel compilation. Default: true */
|
||||
// "typeCheck": false, /* Enable typeCheck. Default: false */
|
||||
},
|
||||
"logging": {
|
||||
// "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
|
||||
},
|
||||
"debugging": {
|
||||
// "stacktrace": false /* Disable stacktrace compilation. Default: false */
|
||||
}
|
||||
}
|
@ -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. */
|
||||
}
|
48
code/BasicFeature/Media/AVSession/VideoPlayer/hvigorw
Normal file
@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Hvigor startup script, version 1.0.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# NODE_HOME - location of a Node home dir
|
||||
# or
|
||||
# Add /usr/local/nodejs/bin to the PATH environment variable
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
HVIGOR_APP_HOME="`pwd -P`"
|
||||
HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js
|
||||
warn() {
|
||||
echo ""
|
||||
echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo ""
|
||||
echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
|
||||
}
|
||||
|
||||
fail() {
|
||||
error "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Determine node to start hvigor wrapper script
|
||||
if [ -n "${NODE_HOME}" ];then
|
||||
EXECUTABLE_NODE="${NODE_HOME}/bin/node"
|
||||
if [ ! -x "$EXECUTABLE_NODE" ];then
|
||||
fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed"
|
||||
fi
|
||||
else
|
||||
EXECUTABLE_NODE="node"
|
||||
which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path"
|
||||
fi
|
||||
|
||||
# Check hvigor wrapper script
|
||||
if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then
|
||||
fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}"
|
||||
fi
|
||||
|
||||
# start hvigor-wrapper script
|
||||
exec "${EXECUTABLE_NODE}" \
|
||||
"${HVIGOR_WRAPPER_SCRIPT}" "$@"
|
64
code/BasicFeature/Media/AVSession/VideoPlayer/hvigorw.bat
Normal file
@ -0,0 +1,64 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Hvigor startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js
|
||||
set NODE_EXE=node.exe
|
||||
|
||||
goto start
|
||||
|
||||
:start
|
||||
@rem Find node.exe
|
||||
if defined NODE_HOME goto findNodeFromNodeHome
|
||||
|
||||
%NODE_EXE% --version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the NODE_HOME variable in your environment to match the
|
||||
echo location of your NodeJs installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findNodeFromNodeHome
|
||||
set NODE_HOME=%NODE_HOME:"=%
|
||||
set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE%
|
||||
|
||||
if exist "%NODE_EXE_PATH%" goto execute
|
||||
echo.
|
||||
echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the NODE_HOME variable in your environment to match the
|
||||
echo location of your NodeJs installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Execute hvigor
|
||||
"%NODE_EXE%" "%WRAPPER_MODULE_PATH%" %*
|
||||
|
||||
if "%ERRORLEVEL%" == "0" goto hvigorwEnd
|
||||
|
||||
:fail
|
||||
exit /b 1
|
||||
|
||||
:hvigorwEnd
|
||||
if "%OS%" == "Windows_NT" endlocal
|
||||
|
||||
:end
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
{
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@ohos/hypium": "1.0.6"
|
||||
},
|
||||
"author": "",
|
||||
"name": "avcastdemo",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
14
code/BasicFeature/Media/AVSession/VideoPlayer/ohosTest.md
Normal file
@ -0,0 +1,14 @@
|
||||
# VideoPlayer 测试用例归档
|
||||
|
||||
## 用例表
|
||||
|
||||
|测试功能|预置条件|输入|预期输出|测试结果|
|
||||
|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|
|
||||
|拉起应用| 设备正常运行| |成功拉起应用|Pass|
|
||||
|主页信息展示| 位于主页| |展示当前正在播放的视频信息|Pass|
|
||||
|主页按钮展示| 位于主页| |展示上一个、下一个、播放暂停按钮|Pass|
|
||||
|主页进度条展示| 位于主页| |展示播放当前位置时间、视频总时间、进度条|Pass|
|
||||
|播放| 位于主页| 点击播放按钮| 应用进入播放状态|Pass|
|
||||
|暂停| 位于主页| 点击暂停按钮| 应用进入暂停状态|Pass|
|
||||
|上一个| 位于主页| 点击上一个按钮| 开始播放上一个视频|Pass|
|
||||
|下一个| 位于主页| 点击下一个按钮| 开始播放下一个视频|Pass|
|
After Width: | Height: | Size: 22 KiB |
26
code/BasicFeature/Media/Camera/AppScope/app.json5
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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": {
|
||||
"bundleName": "com.samples.camera",
|
||||
"vendor": "example",
|
||||
"versionCode": 1000000,
|
||||
"versionName": "1.0.0",
|
||||
"icon": "$media:app_icon",
|
||||
"label": "$string:app_name",
|
||||
"distributedNotificationEnabled": true
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "app_name",
|
||||
"value": "CameraSample"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.6 KiB |
140
code/BasicFeature/Media/Camera/README.md
Normal file
@ -0,0 +1,140 @@
|
||||
# CameraSample
|
||||
|
||||
### 介绍
|
||||
|
||||
本示例主要展示了相机的相关功能,使用[libohcamera.so]
|
||||
当前版本sample仅用作联调,最新sdk未带相机c接口头文件。
|
||||
接口实现相机的预览、拍照、录像、前后置摄像头切换进行拍照、录像,以及闪光灯、变焦、对焦、曝光等控制类功能。
|
||||
|
||||
### 效果预览
|
||||
|
||||
| 相机权限 | 麦克风权限 | 文件权限 | 预览界面 |
|
||||
|----------------------------|----------------------------|----------------------------|---------------------------------|
|
||||
| ![auth](app_pic/auth1.jpg) | ![auth](app_pic/auth2.jpg) | ![auth](app_pic/auth3.jpg) | ![preview](app_pic/preview.jpg) |
|
||||
|
||||
使用说明
|
||||
(因RK3568设备硬件能力,现仅支持验证1、2、3、9、10、13)
|
||||
1. 弹出是否允许“CameraSample”使用相机?点击“允许”
|
||||
2. 弹出是否允许“CameraSample”使用麦克风?点击“允许”
|
||||
3. 弹出是否允许“CameraSample”访问文件?点击“允许”
|
||||
4. 进入预览界面,预览正常,滑动变焦按钮,同一画面远近变焦效果明显
|
||||
5. 进入预览界面,预览正常,点击画面模糊处,点击处画面会变得清晰,对焦效果明显
|
||||
6. 进入预览界面,预览正常,上下滑动屏幕,屏幕场景亮度发生变化,曝光效果明显
|
||||
7. 进入预览界面,预览正常,点击闪光灯按钮,打开闪光灯,闪光灯正常打开
|
||||
8. 进入预览界面,预览正常,点击闪光灯按钮,关闭闪光灯,闪光灯关闭
|
||||
9. 进入预览界面,预览正常,进入拍照模式,点击拍照按钮,拍照正常,左下角会生成照片缩略图,点击左下角缩略图,能够跳转到图库,图片保存正常,打开图片显示正常
|
||||
10. 进入预览界面,预览正常,切换到录像模式,点击录像,开始录像,再点击停止录像按钮,录像成功,左下角会生成视频缩略图,点击左下角缩略图,能够跳转到图库,录像文件保存正常,播放录像文件正常
|
||||
11. 进入预览界面,预览正常,切换到前置摄像头,点击拍照按钮,拍照正常,左下角会生成照片缩略图,点击左下角缩略图,能够跳转到图库,图片保存正常,打开图片显示正常
|
||||
12. 进入预览界面,预览正常,切换到前置摄像头,切换到录像模式,点击录像,开始录像,再点击停止录像按钮,录像成功,左下角会生成视频缩略图,点击左下角缩略图,能够跳转到图库,录像文件保存正常,播放录像文件正常
|
||||
13. 点击设置按钮,会弹出设置页面。
|
||||
|
||||
### 工程目录
|
||||
|
||||
```
|
||||
entry/src/main
|
||||
|-- cpp
|
||||
| |-- CMakeLists.txt // Cmake打包配置文件,编译工程动态库脚本,依赖头文件、cpp以及相关依赖
|
||||
| |-- camera_manager.cpp // 相机基本功能接口定义cpp实现侧
|
||||
| |-- camera_manager.h // 相机基本功能接口定义头文件
|
||||
| |-- main.cpp // NAPI实现JS与C++通信的接口
|
||||
| |-- types
|
||||
| `-- libentry
|
||||
| |-- index.d.ts // 导入NAPI接口供JS调用
|
||||
| `-- oh-package.json5 // 接口注册配置文件
|
||||
|-- ets
|
||||
| |-- Dialog
|
||||
| | |-- mainDialog.ets // 打开相机APP弹出的网络权限设置
|
||||
| | `-- settingDialog.ets // 相机APP设置界面布局
|
||||
| |-- MainAbility
|
||||
| | `-- MainAbility.ts // 对Ability生命周期管理
|
||||
| |-- common
|
||||
| | |-- Constants.ts // 基本参数枚举:纵横比、设备类型、视频帧数
|
||||
| | |-- DisplayCalculator.ts // 计算界面宽高显示数值
|
||||
| | |-- settingItem.ets // 设置栏
|
||||
| | |-- settingPublicLayout.ets // 设置栏公共区域
|
||||
| | `-- settingRightLayout.ets // 设置栏右边区域
|
||||
| |-- entryability
|
||||
| | `-- EntryAbility.ts // Ability的生命周期回调内容
|
||||
| |-- model
|
||||
| | |-- DateTimeUtil.ts // 日期工具
|
||||
| | |-- Logger.ts // 日志工具
|
||||
| | `-- MediaUtils.ts // 媒体工具
|
||||
| |-- pages
|
||||
| | `-- Index.ets // Ability实现的应用的入口页面,相机APP首页
|
||||
| `-- views
|
||||
| |-- CountdownPage.ets // 倒计时UI页面布局
|
||||
| |-- FlashingLightPage.ets // 闪光灯UI界面布局
|
||||
| |-- SlidePage.ets // 滑动滑块UI界面布局
|
||||
| |-- dividerPage.ets // 分割线UI布局
|
||||
| |-- focusAreaPage.ets // 对焦区域设置(焦点、侧光点)、单指竖直方向拖动触发曝光补偿设置
|
||||
| |-- focusPage.ets // 变焦、对焦、曝光、刻度的图标设置、值的设置
|
||||
| `-- modeSwitchPage.ets // 相机功能模式切换,开启预览、拍照、录像
|
||||
|
||||
```
|
||||
|
||||
### 具体实现
|
||||
* 相机功能接口实现在CameraManager.cpp中,源码参考:[CameraManager.cpp](entry/src/main/cpp/CameraManager.cpp)
|
||||
* 在NDKCamera构造函数里完成一个相机生命周期初始化的过程,包括调用OH_Camera_GetCameraMananger获取CameraMananger,调用OH_CameraManager_CreateCaptureSession创建CaptureSession,调用CaptureSessionRegisterCallback创建CaptureSession注册回调,调用GetSupportedCameras获取支持的camera设备,调用GetSupportedOutputCapability获取支持的camera设备能力集,调用CreatePreviewOutput创建预览输出,调用CreateCameraInput创建相机输入,调用CameraInputOpen打开相机输入,调用CameraManagerRegisterCallback创建CameraManager注册回调,最后调用SessionFlowFn开启Session。
|
||||
* 其中SessionFlowFn是一个开启预览的动作,主要流程包括:调用OH_CaptureSession_BeginConfig开始配置会话,调用OH_CaptureSession_AddInput把CameraInput加入到会话,调用OH_CaptureSession_AddPreviewOutput把previewOutput加入到会话,调用OH_CaptureSession_CommitConfig提交配置信息,调用OH_CaptureSession_Start开始会话工作,还有一步是在开启预览的同时调用IsFocusMode启动对焦功能,这边后面会涉及到。
|
||||
* 在NDKCamera析构函数里完成对相机生命周期释放的过程,调用OH_CameraManager_DeleteSupportedCameras删除支持的camera设备,调用OH_CameraManager_DeleteSupportedCameraOutputCapability删除支持的camera设备能力集,调用OH_Camera_DeleteCameraMananger删除camera manager。
|
||||
* 拍照功能相关接口封装在StartPhoto接口中,主要包含以下流程:调用SessionStop关闭session,调用SessionBegin做session的一个预置动作,调用CreatePhotoOutput创建相机输出,调用OH_CaptureSession_AddPhotoOutput将hotoOutput添加至session中,调用SessionCommitConfig提交session,在调用SessionStart开启session,最后调用TakePicture接口开启拍照动作。
|
||||
* 录像功能相关接口封装在StartVideo接口中,主要包含以下流程:调用SessionStop关闭session,调用SessionBegin做session的一个预置动作,调用OH_CaptureSession_RemovePhotoOutput移除相机拍照输出,再调用CreatePhotoOutput创建相机输出,调用AddPhotoOutput将相机输出添加至session中,调用CreateVideoOutput创建录像输出,调用AddVideoOutput将录像输出添加至session中,然后再调用SessionCommitConfig、SessionStart对session进行提交和开启,最后调用VideoOutputRegisterCallback对VideoOutput注册回调。
|
||||
* 闪光灯功能相关接口封装在HasFlashFn接口中,主要包含以下流程:调用OH_CaptureSession_HasFlash检测是否支持闪光灯设备,再调用OH_CaptureSession_IsFlashModeSupported检测闪光灯模式是否支持,然后调用OH_CaptureSession_SetFlashMode设置闪光灯模式,最后调用OH_CaptureSession_GetFlashMode获取当前设备的闪光灯模式。
|
||||
* 变焦功能相关接口封装在setZoomRatioFn接口中,主要包含以下流程:调用OH_CaptureSession_GetZoomRatioRange获取支持的变焦范围,调用OH_CaptureSession_SetZoomRatio设置变焦,调用OH_CaptureSession_GetZoomRatio获取当前设备的变焦值。
|
||||
* 曝光功能相关接口封装在IsExposureModeSupportedFn接口中,主要包含以下流程:调用OH_CaptureSession_IsExposureModeSupported判断是否支持曝光模式,然后调用OH_CaptureSession_SetExposureMode设置曝光模式,调用OH_CaptureSession_GetExposureMode获取设置后的曝光模式。调用IsExposureBiasRange接口获取曝光补偿,其中包含调用OH_CaptureSession_GetExposureBiasRange获取曝光补偿的范围,调用OH_CaptureSession_SetExposureBias设置曝光点,调用OH_CaptureSession_GetExposureBias获取曝光点。
|
||||
* 对焦功能相关接口封装在IsFocusMode接口中,主要包含以下流程:调用OH_CaptureSession_IsFocusModeSupported判断是否支持对焦模式,调用OH_CaptureSession_SetFocusMode设置对焦模式,调用OH_CaptureSession_GetFocusMode获取设置后的对焦模式。调用IsFocusPoint接口获取对焦点,其中包括调用OH_CaptureSession_SetFocusPoint获取JS侧下发来的对焦点位,然后调用OH_CaptureSession_GetFocusPoint获取设置后的对焦点位。
|
||||
* 视频防抖功能相关接口封装在IsVideoStabilizationModeSupportedFn接口中,主要包含以下流程:调用OH_CaptureSession_IsVideoStabilizationModeSupported接口查询是否支持指定的视频防抖模式,调用OH_CaptureSession_SetVideoStabilizationMode设置视频防抖,调用OH_CaptureSession_GetVideoStabilizationMode获取设置后的视频防抖模式。
|
||||
|
||||
* 回调接口设置:
|
||||
* CameraManagerRegisterCallback:监听相机状态回调,在打开、退出相机,相机摄像头切换时会触发
|
||||
* CameraInputRegisterCallback:相机输入发生错误时触发回调
|
||||
* PreviewOutputRegisterCallback:开启预览流时触发回调
|
||||
* PhotoOutputRegisterCallback:开启拍照时触发回调
|
||||
* VideoOutputRegisterCallback:开启录像模式时触发回调
|
||||
* MetadataOutputRegisterCallback:有metadata流输出时触发回调
|
||||
* CaptureSessionRegisterCallback:session出现异常时以及开启对焦模式时触发回调
|
||||
|
||||
|
||||
* 相机预览、拍照、录像功能、前后置切换功能实现调用侧位于tableIndex.ets,modeSwitchPage.ets,main.cpp中,源码参考:[tableIndex.ets](entry/src/main/cpp/ets/pages/tableIndex.ets),[modeSwitchPage.ets](entry/src/main/cpp/ets/views/modeSwitchPage.ets),[main.cpp](entry/src/main/cpp/main.cpp)
|
||||
* 预览:开启预览位于tableIndex.ets下的onPageShow接口,其中调用cameraDemo.initCamera接口,将预览的surfaceId,对焦模式的值,以及是前置还是后置摄像头设备作为入参啊传下去,实际调用的是main.cpp下的InitCamera接口,InitCamera接口将JS侧拿到的参数进行转换再传入cameraManager.cpp中的构造函数里去,完成开启相机的操作,开启预览并设置好对焦模式。
|
||||
* 拍照和录像:开启拍照位于modeSwitchPage.ets下的isVideoPhotoFn接口,通过判断modelBagCol的值是photo还是video,将modelBagCol的值,videoId,拍照的surfaceID或者录像的surfaceId传入接口startPhotoOrVideo。如果是拍照模式,则通过modeSwitchPage.ets下的getPhotoSurfaceID接口获取photo surfaceId,跳转到main.cpp中的StartPhotoOrVideo接口,将传下来的参数进行格式转换,再调用CameraManager对象下的StartPhoto接口开启拍照操作;如果是录像模式,则通过modeSwitchPage.ets下的getVideoSurfaceID接口获取video surfaceId,跳转到main.cpp中的StartPhotoOrVideo接口,将传下来的参数进行格式转换,再调用CameraManager对象下的StartVideo接口开启录像操作
|
||||
* 前后置切换:前后置摄像头切换接口位于modeSwitchPage.ets,切换cameraDeviceIndex,将先前的session配置释放,调用cameraDemo.releaseSession接口,实际上是main.cpp下的ReleaseSession接口,最终调用到CameraMangaer.cpp下的ReleaseSession接口。然后将预览的surfaceId,对焦模式的值以及cameraDeviceIndex传入cameraDemo.initCamera接口中,逻辑和预览一致。
|
||||
|
||||
|
||||
* 相机闪光灯、变焦、对焦、曝光功能实现调用侧位于FlashingLightPage.ets,SlidePage.ets,focusAreaPage.ets中,源码参考:[FlashingLightPage.ets](entry/src/main/cpp/ets/views/FlashingLightPage.ets),[SlidePage.ets](entry/src/main/cpp/ets/views/SlidePage.ets),[focusAreaPage.ets](entry/src/main/cpp/ets/views/focusAreaPage.ets),[main.cpp](entry/src/main/cpp/main.cpp)
|
||||
* 闪光灯:闪光灯功能位于FlashingLightPage.ets,getImageDefault接口用作在点击闪光灯图标之后选择闪光灯模式,0代表关闭,1代表打开,2是自动,3是常亮。然后在build中通过cameraDemo.hasFlash接口调用到main.cpp中的HasFlash接口,最终调到CameraManager.cpp中的HasFlashFn接口,完成闪光灯功能的实现。
|
||||
* 变焦:变焦功能位于SlidePage.ets,通过调用slideChange接口设置slide滑块的值,目前只支持1-6.然后调用cameraDemo.setZoomRatio接口调用到main.cpp中的SetZoomRatio接口,最终调到CameraManager.cpp中的setZoomRatioFn接口,完成变焦功能的实现。
|
||||
* 对焦:对焦功能位于focusAreaPage.ets,通过在build中将对焦焦点下发到cpp侧,在CameraManager.cpp文件中的SessionFlowFn函数中,会调用IsFocusMode接口来判断是否支持对焦模式,然后通过onTouch的方式将对焦点位通过cameraDemo.isFocusPoint接口下发到main.cpp侧的IsFocusPoint接口,最终调到CameraManager.cpp中的IsFocusPoint接口。以及调用OH_CaptureSession_SetFocusMode拿到对焦点位来设置对焦模式,最后调用OH_CaptureSession_GetFocusMode来获取对焦模式,完成对焦功能实现。
|
||||
* 曝光:曝光功能位于focusAreaPage.ets,通过在build中将侧光点位下发到cpp侧,然后通过onTouch的方式将对焦点位以及侧光点位通过cameraDemo.isFocusPoint接口下发到main.cpp侧的isMeteringPoint接口,最终调到CameraManager.cpp中的IsMeteringPoint接口。然后设置曝光补偿,单指竖直方向拖动触发该手势事件,调用gesture中的cameraDemo.isExposureBiasRange接口将曝光值下发到main.cpp中的IsExposureBiasRange,然后经过napi转换后将值传到CameraManager.cpp中的IsExposureBiasRange接口,之后从native侧发到曝光补偿的范围,再调用OH_CaptureSession_SetExposureBias设置曝光值,最后调用OH_CaptureSession_GetExposureBias接口获取曝光值,完成曝光功能。
|
||||
|
||||
### 相关权限
|
||||
|
||||
[ohos.permission.CAMERA](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md)
|
||||
|
||||
[ohos.permission.MICROPHONE](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md)
|
||||
|
||||
[ohos.permission.READ_MEDIA](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md)
|
||||
|
||||
[ohos.permission.WRITE_MEDIA](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md)
|
||||
|
||||
### 依赖
|
||||
|
||||
不涉及
|
||||
|
||||
### 约束与限制
|
||||
|
||||
1. 本示例支持标准系统上运行,支持设备:RK3568;
|
||||
2. 本示例为Stage模型,已适配API version 9版本SDK,SDK版本号(API Version 9 Release),镜像版本号(4.0Release);
|
||||
3. 本示例需要使用DevEco Studio 版本号(3.1Release)及以上版本才可编译运行。
|
||||
|
||||
### 下载
|
||||
|
||||
如需单独下载本工程,执行如下命令:
|
||||
|
||||
```
|
||||
git init
|
||||
git config core.sparsecheckout true
|
||||
echo Camera/Camera/ > .git/info/sparse-checkout
|
||||
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
|
||||
git pull origin master
|
||||
```
|
BIN
code/BasicFeature/Media/Camera/app_pic/auth1.jpg
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
code/BasicFeature/Media/Camera/app_pic/auth2.jpg
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
code/BasicFeature/Media/Camera/app_pic/auth3.jpg
Normal file
After Width: | Height: | Size: 179 KiB |
BIN
code/BasicFeature/Media/Camera/app_pic/preview.jpg
Normal file
After Width: | Height: | Size: 176 KiB |
41
code/BasicFeature/Media/Camera/build-profile.json5
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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": {
|
||||
"compileSdkVersion": 9,
|
||||
"compatibleSdkVersion": 9,
|
||||
"products": [
|
||||
{
|
||||
"name": "default",
|
||||
"signingConfig": "default",
|
||||
}
|
||||
]
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"name": "entry",
|
||||
"srcPath": "./entry",
|
||||
"targets": [
|
||||
{
|
||||
"name": "default",
|
||||
"applyToProducts": [
|
||||
"default"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
4
code/BasicFeature/Media/Camera/entry/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
37
code/BasicFeature/Media/Camera/entry/build-profile.json5
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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": {
|
||||
"externalNativeOptions": {
|
||||
"path": "./src/main/cpp/CMakeLists.txt",
|
||||
"arguments": "-v",
|
||||
"abiFilters": [
|
||||
"armeabi-v7a"
|
||||
],
|
||||
"cppFlags": "",
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"name": "default",
|
||||
"runtimeOS": "OpenHarmony"
|
||||
},
|
||||
{
|
||||
"name": "ohosTest",
|
||||
}
|
||||
]
|
||||
}
|
2
code/BasicFeature/Media/Camera/entry/hvigorfile.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
|
||||
export { hapTasks } from '@ohos/hvigor-ohos-plugin';
|
11
code/BasicFeature/Media/Camera/entry/oh-package.json5
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/libentry.so": "file:./src/main/cpp/types/libentry"
|
||||
},
|
||||
"name": "entry",
|
||||
"description": "example description",
|
||||
"repository": {},
|
||||
"version": "1.0.0",
|
||||
"dependencies": {}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
# the minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
project(CameraSample)
|
||||
|
||||
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include_directories(${NATIVERENDER_ROOT_PATH}
|
||||
${NATIVERENDER_ROOT_PATH}/include)
|
||||
|
||||
add_library(entry SHARED main.cpp camera_manager.cpp)
|
||||
target_link_libraries(entry PUBLIC libohcamera.so libace_napi.z.so libnative_buffer.so libhilog_ndk.z.so librawfile.z.so)
|
1017
code/BasicFeature/Media/Camera/entry/src/main/cpp/camera_manager.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
#ifndef CAMERA_NDK_CAMERA_H
|
||||
#define CAMERA_NDK_CAMERA_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <cstdio>
|
||||
#include <fcntl.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <native_buffer/native_buffer.h>
|
||||
#include "iostream"
|
||||
#include "mutex"
|
||||
|
||||
#include "hilog/log.h"
|
||||
#include "multimedia/camera_framework/camera.h"
|
||||
#include "multimedia/camera_framework/camera_input.h"
|
||||
#include "multimedia/camera_framework/capture_session.h"
|
||||
#include "multimedia/camera_framework/photo_output.h"
|
||||
#include "multimedia/camera_framework/preview_output.h"
|
||||
#include "multimedia/camera_framework/video_output.h"
|
||||
#include "napi/native_api.h"
|
||||
#include "multimedia/camera_framework/camera_manager.h"
|
||||
|
||||
class NDKCamera {
|
||||
public:
|
||||
~NDKCamera();
|
||||
NDKCamera(char *str, uint32_t focusMode, uint32_t cameraDeviceIndex);
|
||||
|
||||
static void Destroy()
|
||||
{
|
||||
if (ndkCamera_ != nullptr) {
|
||||
delete ndkCamera_;
|
||||
ndkCamera_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Camera_ErrorCode CreateCameraInput(void);
|
||||
Camera_ErrorCode CameraInputOpen(void);
|
||||
Camera_ErrorCode CameraInputClose(void);
|
||||
Camera_ErrorCode CameraInputRelease(void);
|
||||
Camera_ErrorCode GetSupportedCameras(void);
|
||||
Camera_ErrorCode GetSupportedOutputCapability(void);
|
||||
Camera_ErrorCode CreatePreviewOutput(void);
|
||||
Camera_ErrorCode CreatePhotoOutput(char* photoId);
|
||||
Camera_ErrorCode CreateVideoOutput(char* videoId);
|
||||
Camera_ErrorCode CreateMetadataOutput(void);
|
||||
Camera_ErrorCode IsCameraMuted(void);
|
||||
Camera_ErrorCode PreviewOutputStop(void);
|
||||
Camera_ErrorCode PreviewOutputRelease(void);
|
||||
Camera_ErrorCode PhotoOutputRelease(void);
|
||||
Camera_ErrorCode HasFlashFn(uint32_t mode);
|
||||
Camera_ErrorCode IsVideoStabilizationModeSupportedFn(uint32_t mode);
|
||||
Camera_ErrorCode setZoomRatioFn(uint32_t zoomRatio);
|
||||
Camera_ErrorCode SessionFlowFn(void);
|
||||
Camera_ErrorCode SessionBegin(void);
|
||||
Camera_ErrorCode SessionCommitConfig(void);
|
||||
Camera_ErrorCode SessionStart(void);
|
||||
Camera_ErrorCode SessionStop(void);
|
||||
Camera_ErrorCode StartVideo(char* videoId, char* photoId);
|
||||
Camera_ErrorCode AddVideoOutput(void);
|
||||
Camera_ErrorCode AddPhotoOutput();
|
||||
Camera_ErrorCode VideoOutputStart(void);
|
||||
Camera_ErrorCode StartPhoto(char* mSurfaceId);
|
||||
Camera_ErrorCode IsExposureModeSupportedFn(uint32_t mode);
|
||||
Camera_ErrorCode IsMeteringPoint(int x, int y);
|
||||
Camera_ErrorCode IsExposureBiasRange(int exposureBias);
|
||||
Camera_ErrorCode IsFocusMode(uint32_t mode);
|
||||
Camera_ErrorCode IsFocusPoint(float x, float y);
|
||||
Camera_ErrorCode IsFocusModeSupported(uint32_t mode);
|
||||
Camera_ErrorCode ReleaseCamera(void);
|
||||
Camera_ErrorCode SessionRealese(void);
|
||||
Camera_ErrorCode ReleaseSession(void);
|
||||
int32_t GetVideoFrameWidth(void);
|
||||
int32_t GetVideoFrameHeight(void);
|
||||
int32_t GetVideoFrameRate(void);
|
||||
Camera_ErrorCode VideoOutputStop(void);
|
||||
Camera_ErrorCode VideoOutputRelease(void);
|
||||
Camera_ErrorCode TakePicture(void);
|
||||
Camera_ErrorCode TakePictureWithPhotoSettings(Camera_PhotoCaptureSetting photoSetting);
|
||||
// callback
|
||||
Camera_ErrorCode CameraManagerRegisterCallback(void);
|
||||
Camera_ErrorCode CameraInputRegisterCallback(void);
|
||||
Camera_ErrorCode PreviewOutputRegisterCallback(void);
|
||||
Camera_ErrorCode PhotoOutputRegisterCallback(void);
|
||||
Camera_ErrorCode VideoOutputRegisterCallback(void);
|
||||
Camera_ErrorCode MetadataOutputRegisterCallback(void);
|
||||
Camera_ErrorCode CaptureSessionRegisterCallback(void);
|
||||
|
||||
// Get callback
|
||||
CameraManager_Callbacks* GetCameraManagerListener(void);
|
||||
CameraInput_Callbacks* GetCameraInputListener(void);
|
||||
PreviewOutput_Callbacks* GetPreviewOutputListener(void);
|
||||
PhotoOutput_Callbacks* GetPhotoOutputListener(void);
|
||||
VideoOutput_Callbacks* GetVideoOutputListener(void);
|
||||
MetadataOutput_Callbacks* GetMetadataOutputListener(void);
|
||||
CaptureSession_Callbacks* GetCaptureSessionRegister(void);
|
||||
|
||||
private:
|
||||
NDKCamera(const NDKCamera&) = delete;
|
||||
NDKCamera& operator = (const NDKCamera&) = delete;
|
||||
uint32_t cameraDeviceIndex_;
|
||||
Camera_Manager* cameraManager_;
|
||||
Camera_CaptureSession* captureSession_;
|
||||
Camera_Device* cameras_;
|
||||
uint32_t size_;
|
||||
Camera_OutputCapability* cameraOutputCapability_;
|
||||
const Camera_Profile* profile_;
|
||||
const Camera_VideoProfile* videoProfile_;
|
||||
Camera_PreviewOutput* previewOutput_;
|
||||
Camera_PhotoOutput* photoOutput_;
|
||||
Camera_VideoOutput* videoOutput_;
|
||||
const Camera_MetadataObjectType* metaDataObjectType_;
|
||||
Camera_MetadataOutput* metadataOutput_;
|
||||
Camera_Input* cameraInput_;
|
||||
bool* isCameraMuted_;
|
||||
Camera_Position position_;
|
||||
Camera_Type type_;
|
||||
char* previewSurfaceId_;
|
||||
char* photoSurfaceId_;
|
||||
Camera_ErrorCode ret_;
|
||||
uint32_t takePictureTimes = 0;
|
||||
Camera_ExposureMode exposureMode_;
|
||||
bool isExposureModeSupported_;
|
||||
bool isFocusModeSupported_;
|
||||
float minExposureBias_;
|
||||
float maxExposureBias_;
|
||||
float step_;
|
||||
uint32_t focusMode_;
|
||||
|
||||
static NDKCamera* ndkCamera_;
|
||||
static std::mutex mtx_;
|
||||
volatile bool valid_;
|
||||
};
|
||||
|
||||
#endif // CAMERA_NDK_CAMERA_H
|
523
code/BasicFeature/Media/Camera/entry/src/main/cpp/main.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
#include <js_native_api.h>
|
||||
#include "camera_manager.h"
|
||||
|
||||
#define LOG_TAG "DEMO:"
|
||||
#define LOG_DOMAIN 0x3200
|
||||
static NDKCamera* ndkCamera_ = nullptr;
|
||||
const int32_t ARGS_TWO = 2;
|
||||
struct Capture_Setting {
|
||||
int32_t quality;
|
||||
int32_t rotation;
|
||||
int32_t location;
|
||||
bool mirror;
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
int32_t altitude;
|
||||
};
|
||||
|
||||
static napi_value SetZoomRatio(napi_env env, napi_callback_info info)
|
||||
{
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int32_t zoomRatio;
|
||||
napi_get_value_int32(env, args[0], &zoomRatio);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "SetZoomRatio : %{public}d", zoomRatio);
|
||||
|
||||
ndkCamera_->setZoomRatioFn(zoomRatio);
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value HasFlash(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "HasFlash");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int32_t flashMode;
|
||||
napi_get_value_int32(env, args[0], &flashMode);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "HasFlash flashMode : %{public}d", flashMode);
|
||||
|
||||
ndkCamera_->HasFlashFn(flashMode);
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsVideoStabilizationModeSupported(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "IsVideoStabilizationModeSupportedFn");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int32_t videoMode;
|
||||
napi_get_value_int32(env, args[0], &videoMode);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "IsVideoStabilizationModeSupportedFn videoMode : %{public}d", videoMode);
|
||||
|
||||
ndkCamera_->IsVideoStabilizationModeSupportedFn(videoMode);
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value InitCamera(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "InitCamera Start");
|
||||
size_t requireArgc = 3;
|
||||
size_t argc = 3;
|
||||
napi_value args[3] = {nullptr};
|
||||
napi_value result;
|
||||
size_t typeLen = 0;
|
||||
char* surfaceId = nullptr;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_get_value_string_utf8(env, args[0], nullptr, 0, &typeLen);
|
||||
surfaceId = new char[typeLen + 1];
|
||||
napi_get_value_string_utf8(env, args[0], surfaceId, typeLen + 1, &typeLen);
|
||||
|
||||
napi_valuetype valuetype1;
|
||||
napi_typeof(env, args[1], &valuetype1);
|
||||
|
||||
int32_t focusMode;
|
||||
napi_get_value_int32(env, args[1], &focusMode);
|
||||
|
||||
uint32_t cameraDeviceIndex;
|
||||
napi_get_value_uint32(env, args[ARGS_TWO], &cameraDeviceIndex);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "InitCamera focusMode : %{public}d", focusMode);
|
||||
OH_LOG_ERROR(LOG_APP, "InitCamera surfaceId : %{public}s", surfaceId);
|
||||
OH_LOG_ERROR(LOG_APP, "InitCamera cameraDeviceIndex : %{public}d", cameraDeviceIndex);
|
||||
|
||||
if (ndkCamera_) {
|
||||
OH_LOG_ERROR(LOG_APP, "ndkCamera_ is not null");
|
||||
delete ndkCamera_;
|
||||
ndkCamera_ = nullptr;
|
||||
}
|
||||
ndkCamera_ = new NDKCamera(surfaceId, focusMode, cameraDeviceIndex);
|
||||
OH_LOG_ERROR(LOG_APP, "InitCamera End");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value ReleaseCamera(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "ReleaseCamera Start");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
size_t typeLen = 0;
|
||||
char* surfaceId = nullptr;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
ndkCamera_->ReleaseCamera();
|
||||
if (ndkCamera_) {
|
||||
OH_LOG_ERROR(LOG_APP, "ndkCamera_ is not null");
|
||||
delete ndkCamera_;
|
||||
ndkCamera_ = nullptr;
|
||||
}
|
||||
OH_LOG_ERROR(LOG_APP, "ReleaseCamera End");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
static napi_value ReleaseSession(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "ReleaseCamera Start");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
size_t typeLen = 0;
|
||||
char* surfaceId = nullptr;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
ndkCamera_->ReleaseSession();
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "ReleaseCamera End");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
static napi_value StartPhotoOrVideo(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "StartPhotoOrVideo Start");
|
||||
Camera_ErrorCode ret = CAMERA_OK;
|
||||
size_t requireArgc = 3;
|
||||
size_t argc = 3;
|
||||
napi_value args[3] = {nullptr};
|
||||
napi_value result;
|
||||
size_t typeLen = 0;
|
||||
size_t videoIdLen = 0;
|
||||
size_t photoIdLen = 0;
|
||||
char* modeFlag = nullptr;
|
||||
char* videoId = nullptr;
|
||||
char* photoId = nullptr;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_get_value_string_utf8(env, args[0], nullptr, 0, &typeLen);
|
||||
modeFlag = new char[typeLen + 1];
|
||||
napi_get_value_string_utf8(env, args[0], modeFlag, typeLen + 1, &typeLen);
|
||||
|
||||
napi_get_value_string_utf8(env, args[1], nullptr, 0, &videoIdLen);
|
||||
videoId = new char[videoIdLen + 1];
|
||||
napi_get_value_string_utf8(env, args[1], videoId, videoIdLen + 1, &videoIdLen);
|
||||
|
||||
napi_get_value_string_utf8(env, args[ARGS_TWO], nullptr, 0, &photoIdLen);
|
||||
photoId = new char[photoIdLen + 1];
|
||||
napi_get_value_string_utf8(env, args[ARGS_TWO], photoId, photoIdLen + 1, &photoIdLen);
|
||||
|
||||
if (!strcmp(modeFlag, "photo")) {
|
||||
OH_LOG_ERROR(LOG_APP, "StartPhoto surfaceId %{public}s", photoId);
|
||||
ret = ndkCamera_->StartPhoto(photoId);
|
||||
} else if (!strcmp(modeFlag, "video")) {
|
||||
ret = ndkCamera_->StartVideo(videoId, photoId);
|
||||
OH_LOG_ERROR(LOG_APP, "StartPhotoOrVideo %{public}s, %{public}s", videoId, photoId);
|
||||
}
|
||||
napi_create_int32(env, ret, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value VideoOutputStart(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "VideoOutputStart Start");
|
||||
napi_value result;
|
||||
Camera_ErrorCode ret = ndkCamera_->VideoOutputStart();
|
||||
napi_create_int32(env, ret, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsExposureModeSupported(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "IsExposureModeSupported exposureMode start.");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int32_t exposureMode;
|
||||
napi_get_value_int32(env, args[0], &exposureMode);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "IsExposureModeSupported exposureMode : %{public}d", exposureMode);
|
||||
|
||||
ndkCamera_->IsExposureModeSupportedFn(exposureMode);
|
||||
OH_LOG_INFO(LOG_APP, "IsExposureModeSupported exposureMode end.");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsMeteringPoint(napi_env env, napi_callback_info info)
|
||||
{
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
int x;
|
||||
napi_get_value_int32(env, args[0], &x);
|
||||
|
||||
napi_valuetype valuetype1;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
int y;
|
||||
napi_get_value_int32(env, args[1], &y);
|
||||
ndkCamera_->IsMeteringPoint(x, y);
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsExposureBiasRange(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "IsExposureBiasRange start.");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int exposureBiasValue;
|
||||
napi_get_value_int32(env, args[0], &exposureBiasValue);
|
||||
ndkCamera_->IsExposureBiasRange(exposureBiasValue);
|
||||
OH_LOG_INFO(LOG_APP, "IsExposureBiasRange end.");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsFocusModeSupported(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "IsFocusModeSupported start.");
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
|
||||
int32_t focusMode;
|
||||
napi_get_value_int32(env, args[0], &focusMode);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "IsFocusModeSupportedFn videoMode : %{public}d", focusMode);
|
||||
|
||||
ndkCamera_->IsFocusModeSupported(focusMode);
|
||||
OH_LOG_INFO(LOG_APP, "IsFocusModeSupported end.");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value IsFocusPoint(napi_env env, napi_callback_info info)
|
||||
{
|
||||
size_t requireArgc = 2;
|
||||
size_t argc = 2;
|
||||
napi_value args[2] = {nullptr};
|
||||
napi_value result;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_valuetype valuetype0;
|
||||
napi_typeof(env, args[0], &valuetype0);
|
||||
double x;
|
||||
napi_get_value_double(env, args[0], &x);
|
||||
|
||||
napi_valuetype valuetype1;
|
||||
napi_typeof(env, args[1], &valuetype1);
|
||||
double y;
|
||||
napi_get_value_double(env, args[1], &y);
|
||||
|
||||
float focusPointX = static_cast<float>(x);
|
||||
float focusPointY = static_cast<float>(y);
|
||||
ndkCamera_->IsFocusPoint(focusPointX, focusPointY);
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value GetVideoFrameWidth(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameWidth Start");
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {nullptr};
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_value result = nullptr;
|
||||
napi_create_int32(env, ndkCamera_->GetVideoFrameWidth(), &result);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameWidth End");
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value GetVideoFrameHeight(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameHeight Start");
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {nullptr};
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_value result = nullptr;
|
||||
napi_create_int32(env, ndkCamera_->GetVideoFrameHeight(), &result);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameHeight End");
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value GetVideoFrameRate(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameRate Start");
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {nullptr};
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_value result = nullptr;
|
||||
napi_create_int32(env, ndkCamera_->GetVideoFrameRate(), &result);
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "GetVideoFrameRate End");
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value VideoOutputStopAndRelease(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_ERROR(LOG_APP, "VideoOutputStopAndRelease Start");
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {nullptr};
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
|
||||
napi_value result = nullptr;
|
||||
ndkCamera_->VideoOutputStop();
|
||||
ndkCamera_->VideoOutputRelease();
|
||||
|
||||
OH_LOG_ERROR(LOG_APP, "VideoOutputStopAndRelease End");
|
||||
napi_create_int32(env, argc, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value TakePicture(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "TakePicture Start");
|
||||
napi_value result;
|
||||
Camera_ErrorCode ret = ndkCamera_->TakePicture();
|
||||
OH_LOG_ERROR(LOG_APP, "TakePicture result is %{public}d", ret);
|
||||
napi_create_int32(env, ret, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static napi_value GetCaptureParam(napi_env env, napi_value captureConfigValue, Capture_Setting *config)
|
||||
{
|
||||
napi_value value = nullptr;
|
||||
napi_get_named_property(env, captureConfigValue, "quality", &value);
|
||||
napi_get_value_int32(env, value, &config->quality);
|
||||
|
||||
napi_get_named_property(env, captureConfigValue, "rotation", &value);
|
||||
napi_get_value_int32(env, value, &config->rotation);
|
||||
|
||||
napi_get_named_property(env, captureConfigValue, "mirror", &value);
|
||||
napi_get_value_bool(env, value, &config->mirror);
|
||||
|
||||
napi_get_named_property(env, captureConfigValue, "latitude", &value);
|
||||
napi_get_value_int32(env, value, &config->latitude);
|
||||
|
||||
napi_get_named_property(env, captureConfigValue, "longitude", &value);
|
||||
napi_get_value_int32(env, value, &config->longitude);
|
||||
|
||||
napi_get_named_property(env, captureConfigValue, "altitude", &value);
|
||||
napi_get_value_int32(env, value, &config->altitude);
|
||||
|
||||
OH_LOG_INFO(LOG_APP, "get quality %{public}d, rotation %{public}d, mirror %{public}d, latitude "
|
||||
"%{public}d, longitude %{public}d, altitude %{public}d", config->quality, config->rotation,
|
||||
config->mirror, config->latitude, config->longitude, config->altitude);
|
||||
return 0;
|
||||
}
|
||||
static void SetConfig(Capture_Setting settings, Camera_PhotoCaptureSetting* photoSetting, Camera_Location* location)
|
||||
{
|
||||
if (photoSetting == nullptr || location == nullptr) {
|
||||
OH_LOG_INFO(LOG_APP, "photoSetting is null");
|
||||
}
|
||||
photoSetting->quality = static_cast<Camera_QualityLevel>(settings.quality);
|
||||
photoSetting->rotation = static_cast<Camera_ImageRotation>(settings.rotation);
|
||||
photoSetting->mirror = settings.mirror;
|
||||
location->altitude = settings.altitude;
|
||||
location->latitude = settings.latitude;
|
||||
location->longitude = settings.longitude;
|
||||
photoSetting->location = location;
|
||||
}
|
||||
|
||||
static napi_value TakePictureWithSettings(napi_env env, napi_callback_info info)
|
||||
{
|
||||
OH_LOG_INFO(LOG_APP, "TakePictureWithSettings Start");
|
||||
size_t requireArgc = 1;
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {nullptr};
|
||||
Camera_PhotoCaptureSetting photoSetting;
|
||||
Capture_Setting setting_inner;
|
||||
Camera_Location* location = new Camera_Location;
|
||||
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
GetCaptureParam(env, args[0], &setting_inner);
|
||||
SetConfig(setting_inner, &photoSetting, location);
|
||||
|
||||
napi_value result;
|
||||
Camera_ErrorCode ret = ndkCamera_->TakePictureWithPhotoSettings(photoSetting);
|
||||
OH_LOG_ERROR(LOG_APP, "TakePictureWithSettings result is %{public}d", ret);
|
||||
napi_create_int32(env, ret, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
EXTERN_C_START
|
||||
static napi_value Init(napi_env env, napi_value exports)
|
||||
{
|
||||
napi_property_descriptor desc[] = {
|
||||
{ "initCamera", nullptr, InitCamera, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "startPhotoOrVideo", nullptr, StartPhotoOrVideo, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "videoOutputStart", nullptr, VideoOutputStart, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "setZoomRatio", nullptr, SetZoomRatio, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "hasFlash", nullptr, HasFlash, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "isVideoStabilizationModeSupported", nullptr, IsVideoStabilizationModeSupported,
|
||||
nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "isExposureModeSupported", nullptr, IsExposureModeSupported,
|
||||
nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "isMeteringPoint", nullptr, IsMeteringPoint, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "isExposureBiasRange", nullptr, IsExposureBiasRange, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "IsFocusModeSupported", nullptr, IsFocusModeSupported, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "isFocusPoint", nullptr, IsFocusPoint, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "getVideoFrameWidth", nullptr, GetVideoFrameWidth, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "getVideoFrameHeight", nullptr, GetVideoFrameHeight, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "getVideoFrameRate", nullptr, GetVideoFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "videoOutputStopAndRelease", nullptr, VideoOutputStopAndRelease,
|
||||
nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "takePicture", nullptr, TakePicture, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "takePictureWithSettings", nullptr, TakePictureWithSettings, nullptr, nullptr, nullptr,
|
||||
napi_default, nullptr },
|
||||
{ "releaseSession", nullptr, ReleaseSession, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "releaseCamera", nullptr, ReleaseCamera, nullptr, nullptr, nullptr, napi_default, nullptr }
|
||||
};
|
||||
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
|
||||
return exports;
|
||||
}
|
||||
EXTERN_C_END
|
||||
|
||||
static napi_module demoModule = {
|
||||
.nm_version =1,
|
||||
.nm_flags = 0,
|
||||
.nm_filename = nullptr,
|
||||
.nm_register_func = Init,
|
||||
.nm_modname = "entry",
|
||||
.nm_priv = ((void*)0),
|
||||
.reserved = { 0 },
|
||||
};
|
||||
|
||||
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
|
||||
{
|
||||
napi_module_register(&demoModule);
|
||||
}
|
29
code/BasicFeature/Media/Camera/entry/src/main/cpp/types/libentry/index.d.ts
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
export const initCamera:(surfaceId: string, focusMode: number, cameraDeviceIndex: number) => number;
|
||||
export const startPhotoOrVideo: (modeFlag: string, videoId: string, photoId: string) => number;
|
||||
export const videoOutputStart: () => number;
|
||||
export const setZoomRatio: (a: number) => number;
|
||||
export const takePicture: () => number;
|
||||
export const takePictureWithSettings: (setting: Capture_Setting) => number;
|
||||
export const hasFlash: (a: number) => number;
|
||||
export const isVideoStabilizationModeSupported: (a: number) => number;
|
||||
export const isExposureModeSupported:(a: number) => number;
|
||||
export const isMeteringPoint: (a: number, b: number) => number;
|
||||
export const isExposureBiasRange: (a: number) => number;
|
||||
export const isFocusModeSupported: (a: number) => number;
|
||||
export const isFocusPoint: (a: number, b: number) => number;
|
||||
export const getVideoFrameWidth: () => number;
|
||||
export const getVideoFrameHeight: () => number;
|
||||
export const getVideoFrameRate: () => number;
|
||||
export const videoOutputStopAndRelease: () => number;
|
||||
export const releaseCamera: () => number;
|
||||
export const releaseSession: () => number;
|
||||
|
||||
|
||||
interface Capture_Setting {
|
||||
quality: number;
|
||||
rotation: number;
|
||||
mirror: boolean;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
altitude: number;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"types": "./index.d.ts",
|
||||
"name": "libentry.so",
|
||||
"description": "",
|
||||
"version": ""
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
@CustomDialog
|
||||
export struct mainDialog {
|
||||
controller: CustomDialogController;
|
||||
|
||||
async aboutToAppear() {
|
||||
console.info(`Camera aboutToAppear entry`);
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Column() {
|
||||
Text('允许“相机”使用无线数据?')
|
||||
.fontSize(16)
|
||||
.fontFamily('HarmonyHeiTi')
|
||||
.fontColor('#182431')
|
||||
.width('100%')
|
||||
.fontWeight(500)
|
||||
.textAlign(TextAlign.Start)
|
||||
Text('关闭无线数据时,部分功能可能无法使用。')
|
||||
.fontSize(10)
|
||||
.opacity(0.6)
|
||||
.textAlign(TextAlign.Start)
|
||||
.width('100%')
|
||||
.fontFamily('HarmonyHeiTi')
|
||||
.fontColor('#182431')
|
||||
.fontWeight(400)
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
|
||||
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Auto }) {
|
||||
Button() {
|
||||
Text('无线局域网与蜂窝网络')
|
||||
.fontColor($r('app.color.theme_color'))
|
||||
.fontSize(14)
|
||||
.width('100%')
|
||||
.fontWeight(500)
|
||||
.height('30%')
|
||||
.textAlign(TextAlign.Center)
|
||||
}
|
||||
.backgroundColor('#FFF')
|
||||
.onClick(() => {
|
||||
this.controller.close();
|
||||
})
|
||||
|
||||
Button() {
|
||||
Text('仅限无限局域网')
|
||||
.fontColor($r('app.color.theme_color'))
|
||||
.fontSize(14)
|
||||
.fontWeight(500)
|
||||
.width('100%')
|
||||
.height('30%')
|
||||
.textAlign(TextAlign.Center)
|
||||
}
|
||||
.backgroundColor('#FFF')
|
||||
.onClick(() => {
|
||||
this.controller.close();
|
||||
})
|
||||
|
||||
Button() {
|
||||
Text('不允许')
|
||||
.fontColor($r('app.color.theme_color'))
|
||||
.fontSize(14)
|
||||
.fontWeight(500)
|
||||
.width('100%')
|
||||
.height('30%')
|
||||
.textAlign(TextAlign.Center)
|
||||
}
|
||||
.backgroundColor('#FFF')
|
||||
.onClick(() => {
|
||||
this.controller.close();
|
||||
})
|
||||
}
|
||||
.margin({ top: 10 })
|
||||
.height('80%')
|
||||
.backgroundColor('#FFF')
|
||||
}
|
||||
.width('750px')
|
||||
.height('700px')
|
||||
.padding({
|
||||
top: '3%',
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%'
|
||||
})
|
||||
.borderRadius(24)
|
||||
.backgroundColor('#FFF')
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 { settingPublicLayout } from '../common/SettingPublicLayout';
|
||||
|
||||
import { settingRightLayout } from '../common/SettingRightLayout';
|
||||
|
||||
@CustomDialog
|
||||
export struct settingDialog {
|
||||
private controller: CustomDialogController;
|
||||
@Prop surfaceId: string;
|
||||
@Prop cameraDeviceIndex: number;
|
||||
@Link referenceLineBol: boolean;
|
||||
// Index of a certain setting clicked on
|
||||
@State leftSliderIndex: number = 1;
|
||||
|
||||
// Mirror persistence, enter again, confirm if the switch is turned on
|
||||
getMirrorBol(bol) {
|
||||
globalThis.settingDataObj.mirrorBol = bol;
|
||||
}
|
||||
// Persistent geographical location, re-enter to determine if the switch is turned on
|
||||
getLocationBol(bol) {
|
||||
globalThis.settingDataObj.locationBol = bol;
|
||||
}
|
||||
// Persist the reference line, enter again to determine if the switch is turned on
|
||||
getReferenceLineBol(bol) {
|
||||
globalThis.settingDataObj.referenceLineBol = bol;
|
||||
this.referenceLineBol = bol;
|
||||
}
|
||||
|
||||
onPageShow() {
|
||||
console.info('globalThis onPageShow:' + JSON.stringify(globalThis.settingDataObj));
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
console.info('globalThis onPageHide:' + JSON.stringify(globalThis.settingDataObj));
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('设置')
|
||||
.fontSize(26)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.textAlign(TextAlign.Start)
|
||||
.width('96%')
|
||||
.onClick(() => {
|
||||
this.controller.close()
|
||||
})
|
||||
}.margin({ top: '100px', bottom: '20px' })
|
||||
|
||||
settingPublicLayout({
|
||||
icon: $r('app.media.ic_camera_set__mirror'),
|
||||
isModeBol: true,
|
||||
borderBol: false,
|
||||
iconModeBol: true,
|
||||
modeMessage: '自拍镜像',
|
||||
backNum: 1,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
setModeBol: globalThis.settingDataObj.mirrorBol,
|
||||
getModeBol: this.getMirrorBol.bind(this)
|
||||
})
|
||||
Column() {
|
||||
settingPublicLayout({
|
||||
icon: $r('app.media.ic_camera_set__antishake'),
|
||||
isModeBol: true,
|
||||
borderBol: true,
|
||||
borderBole: true,
|
||||
backNum: 2,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
modeMessage: '视频防抖'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 3,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_exposure'),
|
||||
modeMessage: '曝光模式'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 4,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_af'),
|
||||
modeMessage: '对焦模式'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 5,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_quality'),
|
||||
modeMessage: '拍摄质量'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 6,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_location'),
|
||||
isModeBol: true,
|
||||
borderBol: true,
|
||||
borderBole: false,
|
||||
iconModeBol: true,
|
||||
modeMessage: '拍摄时显示地理位置',
|
||||
setModeBol: globalThis.settingDataObj.locationBol,
|
||||
getModeBol: this.getLocationBol.bind(this)
|
||||
})
|
||||
|
||||
}
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(16)
|
||||
.margin({ top: 15 })
|
||||
|
||||
Column() {
|
||||
settingPublicLayout({
|
||||
backNum: 7,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_format'),
|
||||
modeMessage: '照片格式',
|
||||
isModeBol: true,
|
||||
borderBol: true,
|
||||
borderBole: true,
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 8,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_class'),
|
||||
modeMessage: '照片方向配置'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 9,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_pic_resolution'),
|
||||
modeMessage: '照片分辨率'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 10,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_video_resolution'),
|
||||
modeMessage: '视频分辨率'
|
||||
})
|
||||
Divider().width(420).margin({ left: 20 })
|
||||
settingPublicLayout({
|
||||
backNum: 11,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_video_rate'),
|
||||
modeMessage: '录像帧率',
|
||||
isModeBol: true,
|
||||
borderBol: true,
|
||||
borderBole: false,
|
||||
})
|
||||
}
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(16)
|
||||
.margin({ top: 15 })
|
||||
|
||||
settingPublicLayout({
|
||||
backNum: 12,
|
||||
leftSliderIndex: $leftSliderIndex,
|
||||
icon: $r('app.media.ic_camera_set_line'),
|
||||
modeMessage: '参考线',
|
||||
isModeBol: true,
|
||||
borderBol: false,
|
||||
iconModeBol: true,
|
||||
setModeBol: globalThis.settingDataObj.referenceLineBol,
|
||||
getModeBol: this.getReferenceLineBol.bind(this)
|
||||
})
|
||||
.margin({ top: 15 })
|
||||
}
|
||||
.width('38%')
|
||||
|
||||
// Set Right Selection List
|
||||
Column() {
|
||||
settingRightLayout({ settingMessageNum: this.leftSliderIndex });
|
||||
}
|
||||
.width('58%')
|
||||
}
|
||||
.height('100%')
|
||||
.width('96%')
|
||||
|
||||
}
|
||||
// page display
|
||||
.onAppear(() => {
|
||||
})
|
||||
// Page disappears
|
||||
.onDisAppear(async () => {
|
||||
console.info('globalThis onPageHide:' + JSON.stringify(globalThis.settingDataObj));
|
||||
})
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#F1F3F5')
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 '@ohos.hilog';
|
||||
import Ability from '@ohos.app.ability.UIAbility'
|
||||
import Window from '@ohos.window'
|
||||
|
||||
import deviceInfo from '@ohos.deviceInfo'
|
||||
|
||||
export default class MainAbility extends Ability {
|
||||
onCreate(want, launchParam) {
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
|
||||
globalThis.abilityContext = this.context
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
|
||||
}
|
||||
onWindowStageCreate(windowStage: Window.WindowStage) {
|
||||
// Main window is created, set main page for this ability
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
|
||||
windowStage.getMainWindow().then((win) => {
|
||||
win.setLayoutFullScreen(true).then(() => {
|
||||
win.setSystemBarEnable(['navigation']).then(() => {
|
||||
})
|
||||
})
|
||||
win.setSystemBarProperties({
|
||||
navigationBarColor: '#00000000',
|
||||
navigationBarContentColor: '#B3B3B3'
|
||||
}).then(() => {
|
||||
})
|
||||
})
|
||||
this.onLoadContent(windowStage, 'pages/Index');
|
||||
}
|
||||
|
||||
onLoadContent(windowStage, page) {
|
||||
windowStage.loadContent(page, (err, data) => {
|
||||
if (err.code) {
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.ERROR);
|
||||
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
|
||||
return;
|
||||
}
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
// Main window is destroyed, release UI related resources
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
// Ability has brought to foreground
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
// Ability has back to background
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 {
|
||||
// aspect ratio: width/height
|
||||
static readonly MIN_ASPECT_RATIO = 4 / 3;
|
||||
static readonly MAX_ASPECT_RATIO = 16 / 9;
|
||||
|
||||
static readonly VIDEO_MAX_WIDTH = 2048;
|
||||
static readonly PHOTO_MAX_WIDTH = 2048;
|
||||
static readonly SURFACE_BOTTOM_MARGIN = 50;
|
||||
|
||||
// device type
|
||||
static readonly TABLET = 'tablet';
|
||||
static readonly DEFAULT = 'default';
|
||||
static readonly PHONE = 'phone';
|
||||
|
||||
// video frame
|
||||
static readonly VIDEO_FRAME_30 = 30;
|
||||
static readonly VIDEO_FRAME_15 = 15;
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 { Constants } from '../common/Constants';
|
||||
|
||||
export default class DisplayCalculator {
|
||||
public static calcSurfaceDisplaySize(screenWidth: number, screenHeight: number, defaultAspectRatio: number): {
|
||||
width: number,
|
||||
height: number
|
||||
} {
|
||||
const displaySize = {
|
||||
width: 1920, height: 1080
|
||||
};
|
||||
// @ts-ignore
|
||||
if (AppStorage.get<string>('deviceType') === Constants.TABLET || screenWidth > screenHeight) {
|
||||
if (screenWidth / screenHeight > defaultAspectRatio) {
|
||||
displaySize.width = Math.floor(screenHeight * defaultAspectRatio);
|
||||
displaySize.height = Math.floor(screenHeight);
|
||||
} else {
|
||||
displaySize.width = Math.floor(screenWidth);
|
||||
displaySize.height = Math.floor(screenWidth / defaultAspectRatio);
|
||||
}
|
||||
} else {
|
||||
if (screenWidth / screenHeight > defaultAspectRatio) {
|
||||
displaySize.width = Math.floor(screenHeight / defaultAspectRatio);
|
||||
displaySize.height = Math.floor(screenHeight);
|
||||
} else {
|
||||
displaySize.width = Math.floor(screenWidth);
|
||||
displaySize.height = Math.floor(screenWidth * defaultAspectRatio);
|
||||
}
|
||||
}
|
||||
return displaySize;
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 cameraDemo from 'libentry.so';
|
||||
|
||||
@Component
|
||||
export struct settingItem {
|
||||
private itemData: string; // The name of the selected mode
|
||||
@Prop settingMessageNum: number; // Incoming click settings
|
||||
private index: number; // Index value of the selected mode
|
||||
@Link @Watch('onChangeFn') isIndex: number; // Which icon is selected by default
|
||||
@State isBol: boolean = false; // Hide Display Icons
|
||||
// Corresponding to the selected setting parameters in the click mode setting
|
||||
selectMode() {
|
||||
switch (this.settingMessageNum) {
|
||||
case 2:
|
||||
globalThis.settingDataObj.videoStabilizationMode = this.index;
|
||||
cameraDemo.isVideoStabilizationModeSupported(globalThis.settingDataObj.videoStabilizationMode);
|
||||
break
|
||||
case 3:
|
||||
globalThis.settingDataObj.exposureMode = this.index;
|
||||
cameraDemo.isExposureModeSupported(globalThis.settingDataObj.exposureMode);
|
||||
break;
|
||||
case 4:
|
||||
globalThis.settingDataObj.focusMode = this.index;
|
||||
cameraDemo.isFocusModeSupported(globalThis.settingDataObj.focusMode);
|
||||
break;
|
||||
case 5:
|
||||
globalThis.settingDataObj.photoQuality = this.index;
|
||||
break;
|
||||
case 7:
|
||||
globalThis.settingDataObj.photoFormat = this.index;
|
||||
break;
|
||||
case 8:
|
||||
globalThis.settingDataObj.photoOrientation = this.index;
|
||||
break;
|
||||
case 9:
|
||||
// Photo resolution
|
||||
globalThis.settingDataObj.photoResolution = this.index;
|
||||
let ind = this.itemData.indexOf('*');
|
||||
globalThis.photoResolutionWidth = Number(this.itemData.substring(0, ind));
|
||||
globalThis.photoResolutionHeight = Number(this.itemData.substring(ind + 1));
|
||||
break;
|
||||
case 10:
|
||||
// Video resolution
|
||||
globalThis.settingDataObj.videoResolution = this.index;
|
||||
let index = this.itemData.indexOf('*');
|
||||
globalThis.videoResolutionWidth = Number(this.itemData.substring(0, index));
|
||||
globalThis.videoResolutionHeight = Number(this.itemData.substring(index + 1));
|
||||
break;
|
||||
case 11:
|
||||
globalThis.settingDataObj.videoFrame = this.index;
|
||||
globalThis.videoFrame = this.itemData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onChangeFn() {
|
||||
if (this.index === this.isIndex) {
|
||||
this.isBol = true;
|
||||
} else {
|
||||
this.isBol = false;
|
||||
}
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
this.onChangeFn();
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(this.itemData)
|
||||
.fontColor('#182431')
|
||||
.fontSize(16)
|
||||
.fontWeight(600)
|
||||
.textAlign(TextAlign.Start)
|
||||
.width('90%')
|
||||
if (this.isBol) {
|
||||
Image($r('app.media.ic_camera_set_checked')).width(24).height(24);
|
||||
} else {
|
||||
Image('').width(24).height(24).backgroundColor(Color.White);
|
||||
}
|
||||
}
|
||||
.justifyContent(FlexAlign.SpaceBetween)
|
||||
.height(65)
|
||||
.onClick(() => {
|
||||
this.isIndex = this.index;
|
||||
this.selectMode();
|
||||
})
|
||||
|
||||
Divider().width(680).margin({ left: 10 })
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 settingPublicLayout {
|
||||
private icon: Resource;
|
||||
private modeMessage: string;
|
||||
private getModeBol: (mirrorBol: boolean) => void;
|
||||
private setModeBol: boolean;
|
||||
private isModeBol: boolean = false;
|
||||
private borderBol: boolean = false;
|
||||
private borderBole: boolean = false;
|
||||
private iconModeBol: boolean = false;
|
||||
private backNum: number = 1;
|
||||
@State backClBol: boolean = false;
|
||||
@Link @Watch('leftSliderChange') leftSliderIndex: number;
|
||||
|
||||
leftSliderChange() {
|
||||
if (this.backNum == this.leftSliderIndex) {
|
||||
this.backClBol = true;
|
||||
} else {
|
||||
this.backClBol = false;
|
||||
}
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
this.leftSliderChange();
|
||||
}
|
||||
|
||||
isBorderFn() {
|
||||
if (this.isModeBol) {
|
||||
if (this.borderBol) {
|
||||
if (this.borderBole) {
|
||||
return { topLeft: 16, topRight: 16 };
|
||||
} else {
|
||||
return { bottomLeft: 16, bottomRight: 16 };
|
||||
}
|
||||
} else {
|
||||
return { topLeft: 16, topRight: 16, bottomLeft: 16, bottomRight: 16 };
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Row() {
|
||||
Row() {
|
||||
Image(this.icon).width(20).height(20).margin({ left: '15px' });
|
||||
Text(this.modeMessage).margin({ left: '20px' })
|
||||
}
|
||||
|
||||
if (this.iconModeBol) {
|
||||
Toggle({ type: ToggleType.Switch, isOn: this.setModeBol })
|
||||
.selectedColor($r('app.color.theme_color'))
|
||||
.switchPointColor('#FFFFFF')
|
||||
.onChange((isOn: boolean) => {
|
||||
this.getModeBol(isOn);
|
||||
console.info('Component status:' + isOn)
|
||||
})
|
||||
} else {
|
||||
Image($r('app.media.ic_camera_set_arrow')).width(20).height(20).margin({ right: 10 })
|
||||
}
|
||||
}
|
||||
.onClick(() => {
|
||||
this.leftSliderIndex = this.backNum;
|
||||
console.info('leftSliderIndex status:' + this.leftSliderIndex);
|
||||
console.info('backNum status:' + this.backNum);
|
||||
})
|
||||
.justifyContent(FlexAlign.SpaceBetween)
|
||||
.width('100%')
|
||||
.borderRadius(15)
|
||||
.height('90px')
|
||||
.backgroundColor(this.backClBol ? 'rgba(255,0,52,0.10)' : '')
|
||||
}
|
||||
.padding(2)
|
||||
.width('100%')
|
||||
.height('100px')
|
||||
.borderRadius(this.isBorderFn())
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 { settingItem } from './SettingItem'
|
||||
|
||||
@Component
|
||||
export struct settingRightLayout {
|
||||
@Prop @Watch('onSettingMessageFn') settingMessageNum: number; // Incoming click settings
|
||||
private title: Array<string> = ['', '自拍镜像', '视频防抖', '曝光模式', '对焦模式', '拍摄质量', '拍摄时显示地理位置', '照片格式', '照片方向配置', '照片分辨率', '视频分辨率', '录像帧率', '参考线',];
|
||||
private settingItemDataList: Array<Array<string>> = [
|
||||
[], [],
|
||||
['关闭视频防抖', '基础防抖算法', '一般防抖算法', '最好防抖算法', '自动进行选择'],
|
||||
['锁定曝光模式', '自动曝光模式', '连续自动曝光'],
|
||||
['手动对焦', '连续自动对焦', '自动变焦', '对焦锁定'],
|
||||
['高', '中', '差'],
|
||||
[],
|
||||
['PNG', 'JPG', 'BMP', 'WEBP', 'JPEG'],
|
||||
['0', '90', '180', '270'],
|
||||
['1920*1080', '1280*720', '640*480'],
|
||||
['1920*1080', '1280*720', '640*480'],
|
||||
['15', '30'],
|
||||
];
|
||||
@State isIndex: number = 0;
|
||||
private settingItemNumberArray = [2,3,4,5,7,8,9,10,11];
|
||||
|
||||
onSettingMessageFn() {
|
||||
switch (this.settingMessageNum) {
|
||||
case 2:
|
||||
this.isIndex = globalThis.settingDataObj.videoStabilizationMode;
|
||||
break;
|
||||
case 3:
|
||||
this.isIndex = globalThis.settingDataObj.exposureMode;
|
||||
break;
|
||||
case 4:
|
||||
this.isIndex = globalThis.settingDataObj.focusMode;
|
||||
break;
|
||||
case 5:
|
||||
this.isIndex = globalThis.settingDataObj.photoQuality;
|
||||
break;
|
||||
case 7:
|
||||
this.isIndex = globalThis.settingDataObj.photoFormat;
|
||||
break;
|
||||
case 8:
|
||||
this.isIndex = globalThis.settingDataObj.photoOrientation;
|
||||
break;
|
||||
case 9:
|
||||
this.isIndex = globalThis.settingDataObj.photoResolution;
|
||||
break;
|
||||
case 10:
|
||||
this.isIndex = globalThis.settingDataObj.videoResolution;
|
||||
break;
|
||||
case 11:
|
||||
this.isIndex = globalThis.settingDataObj.videoFrame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getModeIconObj() {
|
||||
if (this.settingMessageNum == 1) {
|
||||
return { icon: $r('app.media.pic_camera_mirror'), message: '自拍镜像功能只能在前置摄像头打开时可使用。' };
|
||||
} else if (this.settingMessageNum == 6) {
|
||||
return { icon: $r('app.media.pic_camera_mirror'), message: '显示地理位置,用于记录照片或视频拍摄地理位置信息。' };
|
||||
} else if (this.settingMessageNum == 12) {
|
||||
return { icon: $r('app.media.pic_camera_line'), message: '打开相机参考线,可以帮你创造出构图更出色的画面。' };
|
||||
} else {
|
||||
return { icon: null, message: null };
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(this.title[this.settingMessageNum])
|
||||
.fontSize(24)
|
||||
.fontWeight(700)
|
||||
.fontColor('#182431')
|
||||
.width('96%')
|
||||
.textAlign(TextAlign.Start)
|
||||
}.margin({ top: 20 })
|
||||
|
||||
if (this.settingMessageNum == 1 || this.settingMessageNum == 6 || this.settingMessageNum == 12) {
|
||||
Column() {
|
||||
Image(this.getModeIconObj().icon).width(450).height(350).objectFit(ImageFit.ScaleDown);
|
||||
Text(this.getModeIconObj().message).fontColor('#182431').fontSize(18).fontWeight(400);
|
||||
}.margin({ top: 90 })
|
||||
} else {
|
||||
Column() {
|
||||
ForEach(this.settingItemDataList[this.settingMessageNum], (item, index) => {
|
||||
settingItem({
|
||||
itemData: item,
|
||||
index: index,
|
||||
isIndex: $isIndex,
|
||||
settingMessageNum: this.settingMessageNum
|
||||
})
|
||||
})
|
||||
}
|
||||
.margin({ top: 20 })
|
||||
.borderRadius(24)
|
||||
.width(720)
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import prompt from '@system.prompt'
|
||||
import window from '@ohos.window';
|
||||
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
|
||||
import { Permissions } from '@ohos.abilityAccessCtrl';
|
||||
import hilog from '@ohos.hilog';
|
||||
|
||||
const TAG: string = "EntryAbility";
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
|
||||
onCreate(want, launchParam) {
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
|
||||
// @ts-ignore
|
||||
globalThis.abilityContext = this.context;
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.isLoggable(0x0000, 'testTag', hilog.LogLevel.INFO);
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
// Main window is created, set main page for this ability
|
||||
console.log("[Demo] MainAbility onWindowStageCreate")
|
||||
windowStage.loadContent("pages/Index", (err, data) => {
|
||||
if (err.code) {
|
||||
console.error('Failed to load the content. Cause:' + JSON.stringify(err));
|
||||
return;
|
||||
}
|
||||
console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data))
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 Date tool
|
||||
*/
|
||||
export default class DateTimeUtil {
|
||||
|
||||
/**
|
||||
* Hour, minute, second
|
||||
*/
|
||||
getTime() {
|
||||
const DATETIME = new Date();
|
||||
return this.concatTime(DATETIME.getHours(), DATETIME.getMinutes(), DATETIME.getSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Year, Month, Day
|
||||
*/
|
||||
getDate() {
|
||||
const DATETIME = new Date();
|
||||
return this.concatDate(DATETIME.getFullYear(), DATETIME.getMonth() + 1, DATETIME.getDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 0 if the date is less than two digits
|
||||
* @param value-data value
|
||||
*/
|
||||
fill(value: number) {
|
||||
return (value > 9 ? '' : '0') + value;
|
||||
}
|
||||
/**
|
||||
* Recording time timer
|
||||
* @param millisecond-data value
|
||||
*/
|
||||
getVideoTime(millisecond: number): string {
|
||||
let minute = Math.floor(millisecond / 60000);
|
||||
let second = Math.floor((millisecond - minute * 60000) / 1000);
|
||||
return `${this.fill(minute)} : ${this.fill(second)}`;
|
||||
}
|
||||
/**
|
||||
* Format modification of year, month, day
|
||||
* @param year
|
||||
* @param month
|
||||
* @param date
|
||||
*/
|
||||
concatDate(year: number, month: number, date: number) {
|
||||
return `${year}${this.fill(month)}${this.fill(date)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time minute second format modification
|
||||
* @param hours
|
||||
* @param minutes
|
||||
* @param seconds
|
||||
*/
|
||||
concatTime(hours: number, minutes: number, seconds: number) {
|
||||
return `${this.fill(hours)}${this.fill(minutes)}${this.fill(seconds)}`;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 '@ohos.hilog';
|
||||
|
||||
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: any[]) {
|
||||
hilog.debug(this.domain, this.prefix, this.format, args);
|
||||
}
|
||||
|
||||
info(...args: any[]) {
|
||||
hilog.info(this.domain, this.prefix, this.format, args);
|
||||
}
|
||||
|
||||
warn(...args: any[]) {
|
||||
hilog.warn(this.domain, this.prefix, this.format, args);
|
||||
}
|
||||
|
||||
error(...args: any[]) {
|
||||
hilog.error(this.domain, this.prefix, this.format, args);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Logger('[Sample_Camera]');
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
|
||||
import DateTimeUtil from '../model/DateTimeUtil';
|
||||
import Logger from '../model/Logger';
|
||||
|
||||
export default class MediaUtils {
|
||||
private tag: string = 'MediaUtils';
|
||||
private mediaTest: mediaLibrary.MediaLibrary = mediaLibrary.getMediaLibrary(globalThis.abilityContext);
|
||||
private static instance: MediaUtils = new MediaUtils();
|
||||
private num: number = 0;
|
||||
|
||||
public static getInstance() {
|
||||
if (this.instance === undefined) {
|
||||
this.instance = new MediaUtils();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
async createAndGetUri(mediaType: number) {
|
||||
let info = this.getInfoFromType(mediaType);
|
||||
let dateTimeUtil = new DateTimeUtil();
|
||||
let name = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;
|
||||
let displayName = `${info.prefix}${name}${info.suffix}`;
|
||||
Logger.info(this.tag, `createAndGetUri displayName = ${displayName},mediaType = ${mediaType}`);
|
||||
let publicPath = await this.mediaTest.getPublicDirectory(info.directory);
|
||||
Logger.info(this.tag, `createAndGetUri publicPath = ${publicPath}`);
|
||||
try {
|
||||
return await this.mediaTest.createAsset(mediaType, displayName, publicPath);
|
||||
} catch {
|
||||
this.num++;
|
||||
displayName = `${info.prefix}${name}_${this.num}${info.suffix}`;
|
||||
return await this.mediaTest.createAsset(mediaType, displayName, publicPath);
|
||||
}
|
||||
}
|
||||
|
||||
async queryFile(dataUri: any) {
|
||||
let fileKeyObj = mediaLibrary.FileKey;
|
||||
if (dataUri !== undefined) {
|
||||
let args = dataUri.id.toString();
|
||||
let fetchOp = {
|
||||
selections: `${fileKeyObj.ID}=?`,
|
||||
selectionArgs: [args],
|
||||
}
|
||||
const fetchFileResult = await this.mediaTest.getFileAssets(fetchOp);
|
||||
Logger.info(this.tag, `fetchFileResult.getCount() = ${fetchFileResult.getCount()}`);
|
||||
const fileAsset = await fetchFileResult.getAllObject();
|
||||
return fileAsset[0];
|
||||
}
|
||||
}
|
||||
|
||||
async getFdPath(fileAsset: any) {
|
||||
let fd = await fileAsset.open('Rw');
|
||||
Logger.info(this.tag, `fd = ${fd}`);
|
||||
return fd;
|
||||
}
|
||||
|
||||
async createFile(mediaType: number) {
|
||||
let dataUri = await this.createAndGetUri(mediaType);
|
||||
if (dataUri) {
|
||||
let fileAsset = await this.queryFile(dataUri);
|
||||
if (fileAsset) {
|
||||
let fd = await this.getFdPath(fileAsset);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFileAssetsFromType(mediaType: number) {
|
||||
Logger.info(this.tag, `getFileAssetsFromType,mediaType = ${mediaType}`);
|
||||
let fileKeyObj = mediaLibrary.FileKey;
|
||||
let fetchOp = {
|
||||
selections: `${fileKeyObj.MEDIA_TYPE}=?`,
|
||||
selectionArgs: [`${mediaType}`],
|
||||
}
|
||||
const fetchFileResult = await this.mediaTest.getFileAssets(fetchOp);
|
||||
Logger.info(this.tag, `getFileAssetsFromType,fetchFileResult.count = ${fetchFileResult.getCount()}`);
|
||||
let fileAssets = [];
|
||||
if (fetchFileResult.getCount() > 0) {
|
||||
fileAssets = await fetchFileResult.getAllObject();
|
||||
}
|
||||
return fileAssets;
|
||||
}
|
||||
|
||||
async getAlbums() {
|
||||
Logger.info(this.tag, 'getAlbums begin');
|
||||
let albums = [];
|
||||
const [ files, images, videos, audios ] = await Promise.all([
|
||||
this.getFileAssetsFromType(mediaLibrary.MediaType.FILE),
|
||||
this.getFileAssetsFromType(mediaLibrary.MediaType.IMAGE),
|
||||
this.getFileAssetsFromType(mediaLibrary.MediaType.VIDEO),
|
||||
this.getFileAssetsFromType(mediaLibrary.MediaType.AUDIO)
|
||||
]);
|
||||
albums.push({
|
||||
albumName: 'Documents', count: files.length, mediaType: mediaLibrary.MediaType.FILE
|
||||
});
|
||||
albums.push({
|
||||
albumName: 'Pictures', count: images.length, mediaType: mediaLibrary.MediaType.IMAGE
|
||||
});
|
||||
albums.push({
|
||||
albumName: 'Camera', count: videos.length, mediaType: mediaLibrary.MediaType.VIDEO
|
||||
});
|
||||
albums.push({
|
||||
albumName: 'Audios', count: audios.length, mediaType: mediaLibrary.MediaType.AUDIO
|
||||
});
|
||||
return albums;
|
||||
}
|
||||
|
||||
deleteFile(media: any) {
|
||||
let uri = media.uri;
|
||||
Logger.info(this.tag, `deleteFile,uri = ${uri}`);
|
||||
return this.mediaTest.deleteAsset(uri);
|
||||
}
|
||||
|
||||
onDateChange(callback: () => void) {
|
||||
this.mediaTest.on('albumChange', () => {
|
||||
Logger.info(this.tag, 'albumChange called');
|
||||
callback();
|
||||
})
|
||||
this.mediaTest.on('imageChange', () => {
|
||||
Logger.info(this.tag, 'imageChange called');
|
||||
callback();
|
||||
})
|
||||
this.mediaTest.on('audioChange', () => {
|
||||
Logger.info(this.tag, 'audioChange called');
|
||||
callback();
|
||||
})
|
||||
this.mediaTest.on('videoChange', () => {
|
||||
Logger.info(this.tag, 'videoChange called');
|
||||
callback();
|
||||
})
|
||||
this.mediaTest.on('fileChange', () => {
|
||||
Logger.info(this.tag, 'fileChange called');
|
||||
callback();
|
||||
})
|
||||
}
|
||||
|
||||
offDateChange() {
|
||||
this.mediaTest.off('albumChange');
|
||||
this.mediaTest.off('imageChange');
|
||||
this.mediaTest.off('audioChange');
|
||||
this.mediaTest.off('videoChange');
|
||||
this.mediaTest.off('fileChange');
|
||||
}
|
||||
// Photo Format
|
||||
onChangePhotoFormat() {
|
||||
if (globalThis.settingDataObj.photoFormat == 0) {
|
||||
return 'png';
|
||||
}
|
||||
if (globalThis.settingDataObj.photoFormat == 1) {
|
||||
return 'jpg';
|
||||
}
|
||||
if (globalThis.settingDataObj.photoFormat == 2) {
|
||||
return 'bmp';
|
||||
}
|
||||
if (globalThis.settingDataObj.photoFormat == 3) {
|
||||
return 'webp';
|
||||
}
|
||||
if (globalThis.settingDataObj.photoFormat == 4) {
|
||||
return 'jpeg';
|
||||
}
|
||||
}
|
||||
|
||||
getInfoFromType(mediaType: number) {
|
||||
let result = {
|
||||
prefix: '', suffix: '', directory: 0
|
||||
};
|
||||
switch (mediaType) {
|
||||
case mediaLibrary.MediaType.FILE:
|
||||
result.prefix = 'FILE_';
|
||||
result.suffix = '.txt';
|
||||
result.directory = mediaLibrary.DirectoryType.DIR_DOCUMENTS;
|
||||
break;
|
||||
case mediaLibrary.MediaType.IMAGE:
|
||||
result.prefix = 'IMG_';
|
||||
result.suffix = `.${this.onChangePhotoFormat()}`;
|
||||
result.directory = mediaLibrary.DirectoryType.DIR_CAMERA;
|
||||
break;
|
||||
case mediaLibrary.MediaType.VIDEO:
|
||||
result.prefix = 'VID_';
|
||||
result.suffix = '.mp4';
|
||||
result.directory = mediaLibrary.DirectoryType.DIR_CAMERA;
|
||||
break;
|
||||
case mediaLibrary.MediaType.AUDIO:
|
||||
result.prefix = 'AUD_';
|
||||
result.suffix = '.wav';
|
||||
result.directory = mediaLibrary.DirectoryType.DIR_AUDIO;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 from '@ohos.abilityAccessCtrl';
|
||||
import cameraDemo from 'libentry.so';
|
||||
import Logger from '../model/Logger';
|
||||
import { mainDialog } from '../Dialog/MainDialog';
|
||||
import { dividerPage } from '../views/DividerPage';
|
||||
import { settingDialog } from '../Dialog/SettingDialog';
|
||||
import { CountdownPage } from '../views/CountdownPage';
|
||||
import { FlashingLightPage } from '../views/FlashingLightPage';
|
||||
import { SlidePage } from '../views/SlidePage';
|
||||
import { modeSwitchPage } from '../views/ModeSwitchPage';
|
||||
import { focusPage } from '../views/FocusPage';
|
||||
import { FocusAreaPage } from '../views/FocusAreaPage';
|
||||
import { Constants } from '../common/Constants';
|
||||
import DisplayCalculator from '../common/DisplayCalculator';
|
||||
import display from '@ohos.display';
|
||||
|
||||
const TAG: string = 'UI indexPage';
|
||||
|
||||
globalThis.settingDataObj = {
|
||||
mirrorBol: false, // Mirror Enable -> Off
|
||||
videoStabilizationMode: 0, // Video Anti Shake -> Off
|
||||
exposureMode: 1, // Exposure mode -> Automatic
|
||||
focusMode: 2, // Focus mode -> Automatic
|
||||
photoQuality: 1, // Photo quality -> medium
|
||||
locationBol: false, // Show Geographic Location -> Off
|
||||
photoFormat: 1, // Photo Format -> JPG
|
||||
photoOrientation: 0, // Photo direction -> 0
|
||||
photoResolution: 0, // Photo resolution -> 1920 * 1080
|
||||
videoResolution: 0, // Photo resolution -> 1920 * 1080
|
||||
videoFrame: 0, // Recording frame rate -> 15
|
||||
referenceLineBol: false // Divider -> Off
|
||||
};
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct Index {
|
||||
// XComponentController
|
||||
private mXComponentController: XComponentController = new XComponentController();
|
||||
// surfaceID value
|
||||
@State surfaceId: string = '';
|
||||
// Entrance confirmation network pop-up
|
||||
private mainDialogController: CustomDialogController = new CustomDialogController({
|
||||
builder: mainDialog(),
|
||||
autoCancel: false,
|
||||
customStyle: true
|
||||
});
|
||||
|
||||
// Select mode
|
||||
@State modelBagCol: string = 'photo';
|
||||
// Exposure area
|
||||
@State focusPointBol: boolean = false;
|
||||
// Finger click coordinates in the exposure area
|
||||
@State focusPointVal: Array<number> = [0, 0];
|
||||
// Display where scale, focal length value, and focus box cannot coexist
|
||||
@State exposureBol: boolean = true;
|
||||
// Exposure value
|
||||
@State exposureNum: number = 0;
|
||||
// Countdown, photography, and video recording
|
||||
@State countdownNum: number = 0;
|
||||
// Front and rear cameras
|
||||
@State cameraDeviceIndex: number = 0;
|
||||
@State xComponentWidth: number = 384;
|
||||
@State xComponentHeight: number = 450;
|
||||
private deviceType: string;
|
||||
private screenHeight: number;
|
||||
private screenWidth: number;
|
||||
|
||||
// Set Popup Box
|
||||
private settingDialogController: CustomDialogController = new CustomDialogController({
|
||||
builder: settingDialog({
|
||||
referenceLineBol: $referenceLineBol,
|
||||
surfaceId: this.surfaceId,
|
||||
cameraDeviceIndex: this.cameraDeviceIndex
|
||||
}),
|
||||
autoCancel: false,
|
||||
customStyle: true
|
||||
});
|
||||
|
||||
// REFERENCE LINE
|
||||
@State referenceLineBol: boolean = false;
|
||||
@StorageLink('defaultAspectRatio') @Watch('initXComponentSize') defaultAspectRatio: number = Constants.MIN_ASPECT_RATIO;
|
||||
@State onShow: boolean = false;
|
||||
atManager = abilityAccessCtrl.createAtManager();
|
||||
// Entry initialization function
|
||||
async aboutToAppear() {
|
||||
await this.requestPermissionsFn();
|
||||
let mDisplay = display.getDefaultDisplaySync();
|
||||
this.screenWidth = px2vp(mDisplay.width);
|
||||
this.screenHeight = px2vp(mDisplay.height);
|
||||
// @ts-ignore
|
||||
this.deviceType = AppStorage.get<string>('deviceType');
|
||||
if (this.deviceType === Constants.TABLET) {
|
||||
this.defaultAspectRatio = Constants.MAX_ASPECT_RATIO;
|
||||
}
|
||||
this.initXComponentSize();
|
||||
}
|
||||
|
||||
initXComponentSize(): void {
|
||||
let defaultSize = DisplayCalculator.calcSurfaceDisplaySize(this.screenWidth, this.screenHeight, this.defaultAspectRatio);
|
||||
this.xComponentWidth = defaultSize.width;
|
||||
this.xComponentHeight = defaultSize.height;
|
||||
}
|
||||
|
||||
async aboutToDisAppear() {
|
||||
await cameraDemo.releaseCamera();
|
||||
}
|
||||
// Obtain permissions
|
||||
async requestPermissionsFn() {
|
||||
Logger.info(TAG, `requestPermissionsFn entry`);
|
||||
try {
|
||||
this.atManager.requestPermissionsFromUser(globalThis.abilityContext, [
|
||||
'ohos.permission.CAMERA',
|
||||
'ohos.permission.MICROPHONE',
|
||||
'ohos.permission.READ_MEDIA',
|
||||
'ohos.permission.WRITE_MEDIA'
|
||||
]).then(() => {
|
||||
Logger.info(TAG, `request Permissions success!`);
|
||||
this.onShow = true;
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.info(TAG, `requestPermissionsFromUser call Failed! error: ${error.code}`);
|
||||
})
|
||||
} catch (err) {
|
||||
Logger.info(TAG, `requestPermissionsFromUser call Failed! error: ${err.code}`);
|
||||
}
|
||||
}
|
||||
|
||||
async onPageShow() {
|
||||
Logger.info(TAG, `onPageShow App`);
|
||||
if (this.surfaceId && this.onShow) {
|
||||
Logger.error(TAG, `initCamera start`);
|
||||
cameraDemo.initCamera(this.surfaceId, globalThis.settingDataObj.focusMode, this.cameraDeviceIndex);
|
||||
Logger.error(TAG, `initCamera end`);
|
||||
}
|
||||
}
|
||||
|
||||
onPageHide() {
|
||||
Logger.info(TAG, `onPageHide App`);
|
||||
cameraDemo.releaseCamera();
|
||||
}
|
||||
|
||||
build() {
|
||||
Stack() {
|
||||
if (this.onShow) {
|
||||
// general appearance of a picture
|
||||
XComponent({
|
||||
id: 'componentId',
|
||||
type: 'surface',
|
||||
controller: this.mXComponentController
|
||||
})
|
||||
.onLoad(async () => {
|
||||
Logger.info(TAG, 'onLoad is called');
|
||||
this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
|
||||
Logger.info(TAG, `onLoad surfaceId: ${this.surfaceId}`);
|
||||
Logger.error(TAG, `initCamera start`);
|
||||
cameraDemo.initCamera(this.surfaceId, globalThis.settingDataObj.focusMode, this.cameraDeviceIndex);
|
||||
globalThis.cameraDeviceIndex = this.cameraDeviceIndex;
|
||||
Logger.error(TAG, `initCamera end`);
|
||||
})
|
||||
.backgroundColor(Color.Blue)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
|
||||
// REFERENCE LINE
|
||||
dividerPage({ referenceLineBol: this.referenceLineBol });
|
||||
// Set icon
|
||||
Button() {
|
||||
Image($r('app.media.icon_camera_setting'))
|
||||
.width('120px').height('120px')
|
||||
}
|
||||
.width('120px')
|
||||
.height('120px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.borderRadius('40px')
|
||||
.position({ x: '80%', y: '3%' })
|
||||
.onClick(() => {
|
||||
// Open the settings pop-up box
|
||||
this.settingDialogController.open()
|
||||
});
|
||||
// Exposure frame and focus frame
|
||||
focusPage({
|
||||
focusPointBol: $focusPointBol,
|
||||
focusPointVal: $focusPointVal,
|
||||
exposureBol: $exposureBol,
|
||||
exposureNum: $exposureNum
|
||||
});
|
||||
// Exposure focusing finger click area
|
||||
FocusAreaPage({
|
||||
focusPointBol: $focusPointBol,
|
||||
focusPointVal: $focusPointVal,
|
||||
exposureBol: $exposureBol,
|
||||
exposureNum: $exposureNum,
|
||||
xComponentWidth: this.xComponentWidth,
|
||||
xComponentHeight: this.xComponentHeight
|
||||
});
|
||||
// CountDown
|
||||
CountdownPage({ countdownNum: $countdownNum });
|
||||
// FlashLight
|
||||
FlashingLightPage();
|
||||
// Slide
|
||||
SlidePage();
|
||||
// Reverse camera_Multiple workstations_Take photos_Video
|
||||
modeSwitchPage({
|
||||
surfaceId: this.surfaceId,
|
||||
cameraDeviceIndex: $cameraDeviceIndex,
|
||||
countdownNum: $countdownNum
|
||||
});
|
||||
}
|
||||
}
|
||||
.height('100%')
|
||||
.width('100%')
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// Countdown page
|
||||
@Component
|
||||
export struct CountdownPage {
|
||||
@Link countdownNum: number; // Countdown value
|
||||
private countdownListData: Array<string> = ['2', '5', '10']; // Loop rendering
|
||||
@State countdownBol: boolean = true;
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
if (this.countdownBol && !this.countdownNum) {
|
||||
Row() {
|
||||
Button() {
|
||||
Image($r('app.media.icon_camera_setting_timer'))
|
||||
.width('60px').height('60px');
|
||||
}
|
||||
.width('80px')
|
||||
.height('80px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.borderRadius('40px')
|
||||
.onClick(() => {
|
||||
this.countdownBol = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.countdownNum && this.countdownBol) {
|
||||
Row() {
|
||||
Image($r('app.media.icon_camera_setting_timer_on')).width('60px').height('60px').margin({ left: 5 });
|
||||
Text(this.countdownNum + '').fontSize(21).fontWeight(500).margin({ left: 5 }).fontColor(Color.White);
|
||||
}
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.borderRadius('40px')
|
||||
.width('140px')
|
||||
.height('80px')
|
||||
.onClick(() => {
|
||||
this.countdownBol = false;
|
||||
})
|
||||
}
|
||||
if (!this.countdownBol) {
|
||||
Row() {
|
||||
Image($r('app.media.icon_camera_setting_timer_on_balk')).width('60px').height('60px').margin({ left: 5 });
|
||||
ForEach(this.countdownListData, (item) => {
|
||||
Text(item).fontSize(21).fontWeight(500).margin({
|
||||
left: 10
|
||||
})
|
||||
.fontColor(this.countdownNum == item ? $r('app.color.theme_color') : '#182431').onClick(() => {
|
||||
this.countdownNum = item
|
||||
this.countdownBol = true
|
||||
})
|
||||
}, item => item)
|
||||
Text('OFF').fontSize(21).fontWeight(500).margin({
|
||||
left: 10
|
||||
})
|
||||
.fontColor(this.countdownNum == 0 ? $r('app.color.theme_color') : '#182431').onClick(() => {
|
||||
this.countdownNum = 0;
|
||||
this.countdownBol = true;
|
||||
})
|
||||
}
|
||||
.backgroundColor('#FFFFFF')
|
||||
.borderRadius('40px')
|
||||
.width('360px')
|
||||
.height('80px')
|
||||
.zIndex(999)
|
||||
}
|
||||
}
|
||||
.position({ x: 30, y: 352 })
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 dividerPage {
|
||||
@Prop referenceLineBol: boolean
|
||||
|
||||
build() {
|
||||
if (this.referenceLineBol) {
|
||||
Stack() {
|
||||
Column() {
|
||||
Divider().color(Color.White);
|
||||
Divider().color(Color.White);
|
||||
}.justifyContent(FlexAlign.SpaceEvenly).height('100%');
|
||||
|
||||
Row() {
|
||||
Divider().vertical(true).color(Color.White);
|
||||
Divider().vertical(true).color(Color.White);
|
||||
}.justifyContent(FlexAlign.SpaceEvenly).width('100%');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// Flash page
|
||||
import cameraDemo from 'libentry.so';
|
||||
@Component
|
||||
export struct FlashingLightPage {
|
||||
// Page judgment
|
||||
@State flashingBol: boolean = true;
|
||||
// Flash mode
|
||||
@State flashingNum: number = 0;
|
||||
// Return to selected image
|
||||
getImageDefault() {
|
||||
if (this.flashingNum == 0) {
|
||||
return $r('app.media.ic_camera_public_flash_off');
|
||||
}
|
||||
if (this.flashingNum == 1) {
|
||||
return $r('app.media.ic_camera_public_flash_on');
|
||||
}
|
||||
if (this.flashingNum == 2) {
|
||||
return $r('app.media.ic_camera_public_flash_auto');
|
||||
}
|
||||
if (this.flashingNum == 3) {
|
||||
return $r('app.media.flash_always_on');
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
if (this.flashingBol) {
|
||||
Row() {
|
||||
Button() {
|
||||
Image(this.getImageDefault())
|
||||
.width('60px').height('60px').fillColor('#FFFFFF');
|
||||
}
|
||||
.width('80px')
|
||||
.height('80px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.borderRadius('40px')
|
||||
.onClick(() => {
|
||||
this.flashingBol = false;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Flex({ justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center }) {
|
||||
Image($r('app.media.ic_camera_public_flash_auto'))
|
||||
.width('60px')
|
||||
.height('60px')
|
||||
.fillColor(this.flashingNum == 2 ? $r('app.color.theme_color') : '')
|
||||
.onClick(() => {
|
||||
this.flashingNum = 2;
|
||||
this.flashingBol = true;
|
||||
cameraDemo.hasFlash(this.flashingNum);
|
||||
});
|
||||
Image($r('app.media.ic_camera_public_flash_off'))
|
||||
.width('60px')
|
||||
.height('60px')
|
||||
.fillColor(this.flashingNum == 0 ? $r('app.color.theme_color') : '')
|
||||
.onClick(() => {
|
||||
this.flashingNum = 0;
|
||||
this.flashingBol = true;
|
||||
cameraDemo.hasFlash(this.flashingNum);
|
||||
});
|
||||
Image($r('app.media.ic_camera_public_flash_on'))
|
||||
.width('60px')
|
||||
.height('60px')
|
||||
.fillColor(this.flashingNum == 1 ? $r('app.color.theme_color') : '')
|
||||
.onClick(() => {
|
||||
this.flashingNum = 1;
|
||||
this.flashingBol = true;
|
||||
cameraDemo.hasFlash(this.flashingNum);
|
||||
});
|
||||
Image($r('app.media.flash_always_on'))
|
||||
.width('50px')
|
||||
.height('50px')
|
||||
.fillColor(this.flashingNum == 3 ? $r('app.color.theme_color') : '')
|
||||
.onClick(() => {
|
||||
this.flashingNum = 3;
|
||||
this.flashingBol = true;
|
||||
cameraDemo.hasFlash(this.flashingNum);
|
||||
});
|
||||
}
|
||||
.backgroundColor('#FFFFFF')
|
||||
.borderRadius('40px')
|
||||
.width('300px')
|
||||
.height('80px')
|
||||
.zIndex(999)
|
||||
}
|
||||
}
|
||||
.position({ x: 30, y: 408 })
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 cameraDemo from 'libentry.so';
|
||||
import Logger from '../model/Logger';
|
||||
|
||||
const TAG: string = 'FocusAreaPage';
|
||||
|
||||
// Focus Area
|
||||
@Component
|
||||
export struct FocusAreaPage {
|
||||
@Link focusPointBol: boolean;
|
||||
@Link focusPointVal: Array<number>;
|
||||
// Display where scale, focal length value, and focus box cannot coexist
|
||||
@Link exposureBol: boolean;
|
||||
// Exposure value
|
||||
@Link exposureNum: number;
|
||||
@Prop xComponentWidth: number;
|
||||
@Prop xComponentHeight: number;
|
||||
// Focusing area display box timer
|
||||
private areaTimer: number = -1;
|
||||
// Sliding Exposure Up and Down
|
||||
private panOption: PanGestureOptions = new PanGestureOptions({
|
||||
direction: PanDirection.Up | PanDirection.Down,
|
||||
fingers: 1
|
||||
});
|
||||
|
||||
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].screenX;
|
||||
this.focusPointVal[1] = e.touches[0].screenY;
|
||||
// Focus point
|
||||
// @ts-ignore
|
||||
cameraDemo.isFocusPoint(
|
||||
e.touches[0].screenX / this.xComponentWidth,
|
||||
e.touches[0].screenY / this.xComponentHeight
|
||||
);
|
||||
// @ts-ignore
|
||||
cameraDemo.isMeteringPoint(
|
||||
e.touches[0].screenX / this.xComponentWidth,
|
||||
e.touches[0].screenY / this.xComponentHeight + 50
|
||||
);
|
||||
}
|
||||
if (e.type === TouchType.Up) {
|
||||
if (this.areaTimer) {
|
||||
clearTimeout(this.areaTimer);
|
||||
}
|
||||
this.areaTimer = setTimeout(() => {
|
||||
this.focusPointBol = false;
|
||||
}, 3500);
|
||||
}
|
||||
})
|
||||
// Trigger this gesture event by dragging vertically with one finger
|
||||
.gesture(
|
||||
PanGesture(this.panOption)
|
||||
.onActionStart(() => {
|
||||
Logger.info(TAG, 'PanGesture onActionStart');
|
||||
this.exposureBol = false;
|
||||
})
|
||||
.onActionUpdate((event: GestureEvent) => {
|
||||
let offset = -event.offsetY;
|
||||
if (offset > 200) {
|
||||
this.exposureNum = 4;
|
||||
}
|
||||
if (offset < -200) {
|
||||
this.exposureNum = -4;
|
||||
}
|
||||
if (offset > -200 && offset < 200) {
|
||||
this.exposureNum = Number((offset / 50).toFixed(1));
|
||||
}
|
||||
// Exposure Compensation -4 +4
|
||||
cameraDemo.isExposureBiasRange(this.exposureNum);
|
||||
Logger.info(TAG, `PanGesture onActionUpdate offset: ${offset}, exposureNum: ${this.exposureNum}`);
|
||||
})
|
||||
.onActionEnd(() => {
|
||||
this.exposureNum = 0;
|
||||
this.exposureBol = true;
|
||||
Logger.info(TAG, 'PanGesture onActionEnd end');
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 focusPage {
|
||||
@Link focusPointBol: boolean;
|
||||
@Link focusPointVal: Array<number>;
|
||||
// Display where scale, focal length value, and focus box cannot coexist
|
||||
@Link exposureBol: boolean;
|
||||
// Exposure value
|
||||
@Link exposureNum: number;
|
||||
|
||||
build() {
|
||||
if (this.focusPointBol) {
|
||||
Row() {
|
||||
if (this.exposureBol) {
|
||||
// Focus frame
|
||||
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
|
||||
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
|
||||
Row() {
|
||||
}.border({
|
||||
width: { left: 1.6, right: 0, top: 1.6, bottom: 0 },
|
||||
color: Color.White,
|
||||
radius: { topLeft: 10, topRight: 0, bottomLeft: 0, bottomRight: 0 }
|
||||
}).size({ width: 15, height: 15 });
|
||||
|
||||
Row() {
|
||||
}.border({
|
||||
width: { left: 0, right: 1.6, top: 1.6, bottom: 0 },
|
||||
color: Color.White,
|
||||
radius: { topLeft: 0, topRight: 10, bottomLeft: 0, bottomRight: 0 }
|
||||
}).size({ width: 15, height: 15 });
|
||||
}
|
||||
|
||||
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
|
||||
Row() {
|
||||
}.border({
|
||||
width: { left: 1.6, right: 0, top: 0, bottom: 1.6 },
|
||||
color: Color.White,
|
||||
radius: { topLeft: 0, topRight: 0, bottomLeft: 10, bottomRight: 0 }
|
||||
}).size({ width: 15, height: 15 });
|
||||
|
||||
Row() {
|
||||
}.border({
|
||||
width: { left: 0, right: 1.6, top: 0, bottom: 1.6 },
|
||||
color: Color.White,
|
||||
radius: { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 10 }
|
||||
}).size({ width: 15, height: 15 });
|
||||
}
|
||||
}
|
||||
.width(50)
|
||||
.height(50)
|
||||
.position({ x: this.focusPointVal[0] - 60, y: this.focusPointVal[1] - 60 });
|
||||
} else {
|
||||
// Focus value
|
||||
Text(this.exposureNum + '').fontSize(60).fontColor(Color.White).fontWeight(400).position({
|
||||
x: this.focusPointVal[0] - 58,
|
||||
y: this.focusPointVal[1] - 30
|
||||
});
|
||||
// Scale value
|
||||
Flex() {
|
||||
Column() {
|
||||
Text('+4').fontColor(Color.White);
|
||||
Text('0').margin({ top: 50, bottom: 50 }).fontColor(Color.White);
|
||||
Text('-4').fontColor(Color.White);
|
||||
}.margin({ right: 9 });
|
||||
// Scale
|
||||
Column() {
|
||||
Text('').height(67).border({
|
||||
width: { left: '0', right: 4, top: '0', bottom: '0' },
|
||||
color: Color.White,
|
||||
radius: 2,
|
||||
style: BorderStyle.Dotted
|
||||
});
|
||||
Text('').height(8).border({
|
||||
width: { left: '0', right: 8, top: '0', bottom: '0' },
|
||||
color: Color.White,
|
||||
radius: 4,
|
||||
style: BorderStyle.Solid
|
||||
}).margin({ top: 4 })
|
||||
Text('').height(67).border({
|
||||
width: { left: '0', right: 4, top: '0', bottom: '0' },
|
||||
color: Color.White,
|
||||
radius: 2,
|
||||
style: BorderStyle.Dotted
|
||||
}).margin({ top: 4 });
|
||||
}
|
||||
}.position({
|
||||
x: this.focusPointVal[0] + 56.6,
|
||||
y: this.focusPointVal[1] - 73
|
||||
});
|
||||
}
|
||||
// Exposure icon
|
||||
Image($r('app.media.ic_public_brightness')).size({ width: 24, height: 24 })
|
||||
.position({
|
||||
x: this.focusPointVal[0] + 10,
|
||||
y: this.focusPointVal[1] - 30
|
||||
});
|
||||
}.zIndex(99)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// Reverse camera_ Multiple workstations_ Take photos_ Video
|
||||
|
||||
import DateTimeUtil from '../model/DateTimeUtil';
|
||||
import Logger from '../model/Logger';
|
||||
import cameraDemo from 'libentry.so';
|
||||
|
||||
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
|
||||
|
||||
import image from '@ohos.multimedia.image';
|
||||
import media from '@ohos.multimedia.media';
|
||||
import MediaUtils from '../model/MediaUtils';
|
||||
import deviceInfo from '@ohos.deviceInfo';
|
||||
import AVRecorder from '@ohos.multimedia.media';
|
||||
import fileio from '@ohos.fileio';
|
||||
|
||||
const CameraSize = {
|
||||
WIDTH: 1280,
|
||||
HEIGHT: 720
|
||||
};
|
||||
|
||||
globalThis.photoSettings = {
|
||||
quality: 0, // Photo quality
|
||||
rotation: 0, // Photo direction
|
||||
mirror: false, // Mirror Enable
|
||||
latitude: 12.9698, // geographic location
|
||||
longitude: 77.7500, // geographic location
|
||||
altitude: 1000 // geographic location
|
||||
};
|
||||
|
||||
@Component
|
||||
export struct modeSwitchPage {
|
||||
private tag: string = 'CAIHF modeSwitchPage:';
|
||||
private mediaUtil = MediaUtils.getInstance();
|
||||
private fileAsset: mediaLibrary.FileAsset = undefined;
|
||||
private fd: number = -1;
|
||||
@State videoId: string = '';
|
||||
@State mSurfaceId: string = '';
|
||||
private mReceiver: image.ImageReceiver = undefined;
|
||||
private videoRecorder: media.AVRecorder = undefined;
|
||||
private videoConfig: media.AVRecorderConfig = {
|
||||
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
|
||||
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
|
||||
profile: {
|
||||
audioBitrate: 48000,
|
||||
audioChannels: 2,
|
||||
audioCodec: media.CodecMimeType.AUDIO_AAC,
|
||||
audioSampleRate: 48000,
|
||||
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
|
||||
videoBitrate: 512000,
|
||||
videoCodec: media.CodecMimeType.VIDEO_AVC,
|
||||
videoFrameWidth: 640,
|
||||
videoFrameHeight: 480,
|
||||
videoFrameRate: 30
|
||||
},
|
||||
url: '',
|
||||
rotation: 0
|
||||
};
|
||||
private photoRotationMap = {
|
||||
rotation0: 0,
|
||||
rotation90: 90,
|
||||
rotation180: 180,
|
||||
rotation270: 270,
|
||||
};
|
||||
// Front and rear cameras
|
||||
@Link cameraDeviceIndex: number;
|
||||
// SurfaceID
|
||||
@Prop surfaceId: string;
|
||||
// Countdown value
|
||||
@Link countdownNum: number;
|
||||
// Countdown timer
|
||||
@State countTimerInt: number = -1;
|
||||
@State countTimerOut: number = -1;
|
||||
// Photo Thumbnails
|
||||
@State imgThumbnail: string = undefined;
|
||||
// Recording time
|
||||
@State videoRecodeTime: number = 0;
|
||||
// Recording time timer
|
||||
@State timer: number = undefined;
|
||||
// Time Manager
|
||||
@State dateTimeUtil: DateTimeUtil = new DateTimeUtil();
|
||||
// Select mode
|
||||
@State modelBagCol: string = 'photo';
|
||||
// Choose camera or capture
|
||||
@State @Watch('onChangeIsModeBol') isModeBol: boolean = true;
|
||||
// Video Thumbnails
|
||||
@State videoThumbnail: image.PixelMap = undefined;
|
||||
// After pausing, click 'stop' to reset the pause to default
|
||||
onChangeIsModeBol() {
|
||||
}
|
||||
// Countdown capture and video
|
||||
countTakeVideoFn() {
|
||||
if (this.countdownNum) {
|
||||
// Clear Countdown
|
||||
if (this.countTimerOut) {
|
||||
clearTimeout(this.countTimerOut);
|
||||
}
|
||||
if (this.countTimerInt) {
|
||||
clearInterval(this.countTimerInt);
|
||||
}
|
||||
// Turn on timer
|
||||
this.countTimerOut = setTimeout(() => {
|
||||
// Determine whether it is in video or photo mode
|
||||
this.isVideoPhotoFn();
|
||||
}, this.countdownNum * 1000)
|
||||
// Turn on timer
|
||||
this.countTimerInt = setInterval(() => {
|
||||
this.countdownNum--;
|
||||
if (this.countdownNum === 0) {
|
||||
clearInterval(this.countTimerInt);
|
||||
}
|
||||
}, 1000)
|
||||
} else {
|
||||
this.isVideoPhotoFn();
|
||||
}
|
||||
}
|
||||
async getVideoSurfaceID(){
|
||||
Logger.info(this.tag, `getVideoSurfaceID`);
|
||||
this.videoRecorder = await media.createAVRecorder();
|
||||
Logger.info(this.tag, `getVideoSurfaceID videoRecorder: ${this.videoRecorder}`);
|
||||
|
||||
this.fileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.VIDEO);
|
||||
Logger.info(this.tag, `getVideoSurfaceID fileAsset: ${this.fileAsset}`);
|
||||
|
||||
this.fd = await this.mediaUtil.getFdPath(this.fileAsset);
|
||||
Logger.info(this.tag, `getVideoSurfaceID fd: ${this.fd}`);
|
||||
|
||||
this.videoConfig.url = `fd://${this.fd}`;
|
||||
Logger.info(this.tag, `getVideoSurfaceID videoConfig.url : ${this.videoConfig.url }`);
|
||||
|
||||
if (deviceInfo.deviceType == 'default') {
|
||||
Logger.info(this.tag, `deviceType = default`);
|
||||
this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES;
|
||||
}
|
||||
if (deviceInfo.deviceType == 'phone') {
|
||||
Logger.info(this.tag, `deviceType = phone`)
|
||||
this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV;
|
||||
this.videoConfig.profile.videoCodec = media.CodecMimeType.VIDEO_MPEG4;
|
||||
if (this.cameraDeviceIndex == 1) {
|
||||
this.videoConfig.rotation = this.photoRotationMap.rotation270;
|
||||
} else {
|
||||
this.videoConfig.rotation = this.photoRotationMap.rotation90;
|
||||
}
|
||||
}
|
||||
if (deviceInfo.deviceType == 'tablet') {
|
||||
Logger.info(this.tag, `deviceType = tablet`);
|
||||
this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV;
|
||||
}
|
||||
|
||||
this.videoConfig.profile.videoFrameWidth = cameraDemo.getVideoFrameWidth();
|
||||
this.videoConfig.profile.videoFrameHeight = cameraDemo.getVideoFrameHeight();
|
||||
this.videoConfig.profile.videoFrameRate = cameraDemo.getVideoFrameRate();
|
||||
|
||||
await this.videoRecorder.prepare(this.videoConfig);
|
||||
this.videoId = await this.videoRecorder.getInputSurface();
|
||||
Logger.info(this.tag, `getVideoSurfaceID videoId: ${this.videoId}`);
|
||||
}
|
||||
|
||||
createImageReceiver() {
|
||||
try {
|
||||
this.mReceiver = image.createImageReceiver(CameraSize.WIDTH, CameraSize.HEIGHT, 2000, 8)
|
||||
Logger.info(this.tag, `createImageReceiver value: ${this.mReceiver} `);
|
||||
this.mReceiver.on('imageArrival', () => {
|
||||
Logger.info(this.tag, 'imageArrival start');
|
||||
this.mReceiver.readNextImage((err, image) => {
|
||||
Logger.info(this.tag, 'readNextImage start');
|
||||
if (err || image === undefined) {
|
||||
Logger.error(this.tag, 'readNextImage failed ');
|
||||
return;
|
||||
}
|
||||
image.getComponent(4, (errMsg, img) => {
|
||||
Logger.info(this.tag, 'getComponent start');
|
||||
if (errMsg || img === undefined) {
|
||||
Logger.info(this.tag, 'getComponent failed ');
|
||||
return;
|
||||
}
|
||||
let buffer
|
||||
if (img.byteBuffer) {
|
||||
buffer = img.byteBuffer;
|
||||
} else {
|
||||
Logger.error(this.tag, 'img.byteBuffer is undefined');
|
||||
}
|
||||
this.savePicture(buffer, image);
|
||||
})
|
||||
})
|
||||
})
|
||||
} catch {
|
||||
Logger.info(this.tag, 'savePicture err');
|
||||
}
|
||||
}
|
||||
|
||||
// Read Image
|
||||
async savePicture(buffer: ArrayBuffer, img: image.Image) {
|
||||
try {
|
||||
Logger.info(this.tag, 'savePicture start');
|
||||
let imgFileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.IMAGE);
|
||||
let imgPhotoUri = imgFileAsset.uri;
|
||||
Logger.info(this.tag, `photoUri = ${imgPhotoUri}`);
|
||||
let imgFd = await this.mediaUtil.getFdPath(imgFileAsset);
|
||||
Logger.info(this.tag, `fd = ${imgFd}`);
|
||||
await fileio.write(imgFd, buffer);
|
||||
await imgFileAsset.close(imgFd);
|
||||
await img.release();
|
||||
Logger.info(this.tag, 'save image End');
|
||||
if (this.handleTakePicture) {
|
||||
this.handleTakePicture(imgPhotoUri);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.info(this.tag, 'savePicture err' + JSON.stringify(err.message));
|
||||
}
|
||||
}
|
||||
|
||||
async getPhotoSurfaceID() {
|
||||
if(this.mReceiver) {
|
||||
Logger.info(this.tag, 'imageReceiver has been created');
|
||||
} else {
|
||||
this.createImageReceiver();
|
||||
}
|
||||
this.mSurfaceId = await this.mReceiver.getReceivingSurfaceId();
|
||||
if(this.mSurfaceId) {
|
||||
Logger.info(this.tag, `createImageReceiver mSurfaceId: ${this.mSurfaceId} `);
|
||||
} else {
|
||||
Logger.info(this.tag, `Get mSurfaceId failed `);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the video or photo mode
|
||||
async isVideoPhotoFn() {
|
||||
await this.getPhotoSurfaceID();
|
||||
|
||||
if (this.modelBagCol == 'photo') {
|
||||
cameraDemo.startPhotoOrVideo(this.modelBagCol, this.videoId, this.mSurfaceId);
|
||||
} else if (this.modelBagCol == 'video') {
|
||||
this.isModeBol = false;
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
// Start record
|
||||
await this.getVideoSurfaceID();
|
||||
cameraDemo.startPhotoOrVideo(this.modelBagCol, this.videoId, this.mSurfaceId);
|
||||
cameraDemo.videoOutputStart();
|
||||
this.videoRecorder.start();
|
||||
}
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
}
|
||||
|
||||
handleTakePicture = (thumbnail: string) => {
|
||||
this.imgThumbnail = thumbnail;
|
||||
Logger.info(this.tag, `takePicture end , thumbnail: ${this.imgThumbnail}`);
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this.isModeBol) {
|
||||
Column() {
|
||||
Text('拍照')
|
||||
.backgroundColor(this.modelBagCol === 'photo' ? $r('app.color.theme_color') : '')
|
||||
.size({ width: 64, height: 28 })
|
||||
.borderRadius(14)
|
||||
.fontSize(14)
|
||||
.fontColor(Color.White)
|
||||
.onClick(() => {
|
||||
this.modelBagCol = 'photo'
|
||||
})
|
||||
}.position({ x: '20%', y: '75%' })
|
||||
|
||||
Column() {
|
||||
Text('录像')
|
||||
.fontSize(14)
|
||||
.fontColor(Color.White)
|
||||
.borderRadius(14)
|
||||
.size({ width: 64, height: 28 })
|
||||
.backgroundColor(this.modelBagCol === 'video' ? $r('app.color.theme_color') : '')
|
||||
.onClick(() => {
|
||||
this.modelBagCol = 'video'
|
||||
})
|
||||
}.position({ x: '40%', y: '75%' })
|
||||
|
||||
// 图库
|
||||
Column() {
|
||||
Row() {
|
||||
if (this.modelBagCol === 'photo') {
|
||||
Image(this.imgThumbnail || $r('app.media.pic_avatar_radio02'))
|
||||
.aspectRatio(1)
|
||||
.objectFit(ImageFit.Fill)
|
||||
.border({ width: 2, color: 0xFFFFFF, radius: 40 })
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
} else {
|
||||
Image(this.videoThumbnail || $r('app.media.pic_avatar_radio02'))
|
||||
.aspectRatio(1)
|
||||
.objectFit(ImageFit.Fill)
|
||||
.border({ width: 2, color: 0xFFFFFF, radius: 40 })
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
}
|
||||
}.onClick(() => {
|
||||
if (deviceInfo.deviceType == 'default') {
|
||||
globalThis.abilityContext.startAbility({
|
||||
bundleName: 'com.ohos.photos',
|
||||
abilityName: 'com.ohos.photos.MainAbility'
|
||||
})
|
||||
} else if (deviceInfo.deviceType == 'phone') {
|
||||
globalThis.abilityContext.startAbility({
|
||||
bundleName: 'com.huawei.hmos.photos',
|
||||
abilityName: 'com.huawei.hmos.photos.MainAbility'
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}.position({ x: '10%', y: '82%' })
|
||||
|
||||
// capture video icon
|
||||
Column() {
|
||||
Row() {
|
||||
Button() {
|
||||
Text()
|
||||
.width('120px')
|
||||
.height('120px')
|
||||
.borderRadius('40px')
|
||||
.backgroundColor(this.modelBagCol == 'video' ? $r('app.color.theme_color') : Color.White)
|
||||
}
|
||||
.border({ width: 3, color: 0xFFFFFF, radius: 70 })
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.onClick(() => {
|
||||
// Countdown camera recording - default camera recording
|
||||
this.countTakeVideoFn();
|
||||
})
|
||||
}
|
||||
}.position({ x: '40%', y: '82%' })
|
||||
|
||||
// Front and rear camera switching
|
||||
Column() {
|
||||
Row() {
|
||||
Button() {
|
||||
Image($r('app.media.switch_camera'))
|
||||
.width('120px').height('120px')
|
||||
}
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.borderRadius('40px')
|
||||
.onClick(async () => {
|
||||
// Switching Cameras
|
||||
this.cameraDeviceIndex ? this.cameraDeviceIndex = 0 : this.cameraDeviceIndex = 1;
|
||||
// Clear configuration
|
||||
cameraDemo.releaseSession();
|
||||
// Start preview
|
||||
cameraDemo.initCamera(this.surfaceId, globalThis.settingDataObj.focusMode, this.cameraDeviceIndex);
|
||||
})
|
||||
}
|
||||
}.position({ x: '70%', y: '82%' })
|
||||
} else {
|
||||
Column() {
|
||||
Row() {
|
||||
Text().size({ width: 12, height: 12 }).backgroundColor($r('app.color.theme_color')).borderRadius(6)
|
||||
Text(this.dateTimeUtil.getVideoTime(this.videoRecodeTime))
|
||||
.fontSize(30)
|
||||
.fontColor(Color.White)
|
||||
.margin({ left: 8 })
|
||||
}.offset({ x: -580, y: -180 })
|
||||
}.position({ x: 120, y: 450 })
|
||||
|
||||
Column() {
|
||||
// Video capture button
|
||||
Button() {
|
||||
Text().width('120px').height('120px').borderRadius('35px').backgroundColor(Color.White)
|
||||
}
|
||||
.border({ width: 2, color: 0xFFFFFF, radius: 45 })
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
.onClick(() => {
|
||||
cameraDemo.takePictureWithSettings(globalThis.photoSettings);
|
||||
})
|
||||
}.position({ x: '10%', y: '82%' })
|
||||
|
||||
Column() {
|
||||
Row() {
|
||||
Column() {
|
||||
// 录像停止键
|
||||
Button() {
|
||||
Image($r('app.media.ic_camera_video_close')).size({ width: 25, height: 25 });
|
||||
}
|
||||
.width('120px')
|
||||
.height('120px')
|
||||
.backgroundColor($r('app.color.theme_color'))
|
||||
.onClick(() => {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
// Stop video
|
||||
this.stopVideo().then(async (fileAsset) => {
|
||||
this.videoRecodeTime = 0;
|
||||
this.isModeBol = true;
|
||||
try {
|
||||
// Get video thumbnail
|
||||
this.videoThumbnail = await fileAsset.getThumbnail();
|
||||
} catch (err) {
|
||||
Logger.info(this.tag, 'videoThumbnail err----------:' + JSON.stringify(err.message));
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
.width('180px')
|
||||
.height('180px')
|
||||
.borderRadius('60px')
|
||||
.backgroundColor($r('app.color.theme_color'))
|
||||
.justifyContent(FlexAlign.SpaceAround)
|
||||
}
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.border({ width: 3, color: 0xFFFFFF, radius: 70 })
|
||||
.width('200px')
|
||||
.height('200px')
|
||||
.backgroundColor('rgba(255,255,255,0.20)')
|
||||
}.position({ x: '40%', y: '82%' })
|
||||
}
|
||||
}
|
||||
|
||||
async stopVideo() {
|
||||
try {
|
||||
if (this.videoRecorder) {
|
||||
await this.videoRecorder.stop();
|
||||
await this.videoRecorder.release();
|
||||
}
|
||||
cameraDemo.videoOutputStopAndRelease();
|
||||
if (this.fileAsset) {
|
||||
await this.fileAsset.close(this.fd);
|
||||
return this.fileAsset;
|
||||
}
|
||||
Logger.info(this.tag, 'stopVideo end');
|
||||
} catch (err) {
|
||||
Logger.info(this.tag, 'stopVideo err: ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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.
|
||||
*/
|
||||
|
||||
// Zoom component
|
||||
// For changes in components - can only be integers 1 to 6
|
||||
import cameraDemo from 'libentry.so'
|
||||
import Logger from '../model/Logger';
|
||||
|
||||
const TAG: string = 'SlidePage';
|
||||
|
||||
@Component
|
||||
export struct SlidePage {
|
||||
// Slide slider
|
||||
@State outSetValueOne: number = 1;
|
||||
// Slide slider movement value
|
||||
@State sliderTextPos: string = '-10';
|
||||
|
||||
slideChange(value) {
|
||||
cameraDemo.setZoomRatio(value);
|
||||
switch (value) {
|
||||
case 1:
|
||||
this.sliderTextPos = '-10';
|
||||
break;
|
||||
case 2:
|
||||
this.sliderTextPos = '70';
|
||||
break;
|
||||
case 3:
|
||||
this.sliderTextPos = '145';
|
||||
break;
|
||||
case 4:
|
||||
this.sliderTextPos = '220';
|
||||
break;
|
||||
case 5:
|
||||
this.sliderTextPos = '300';
|
||||
break;
|
||||
case 6:
|
||||
this.sliderTextPos = '380';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
|
||||
Column() {
|
||||
Row() {
|
||||
Text('1x').fontColor(Color.White);
|
||||
Text('6x').fontColor(Color.White);
|
||||
}.justifyContent(FlexAlign.SpaceBetween).width('95%')
|
||||
|
||||
Text(this.outSetValueOne + 'x')
|
||||
.fontColor('#182431')
|
||||
.width('100px')
|
||||
.height('50px')
|
||||
.borderRadius(25)
|
||||
.backgroundColor(Color.White)
|
||||
.fontSize(14)
|
||||
.textAlign(TextAlign.Center)
|
||||
.position({ x: this.sliderTextPos, y: '-5' })
|
||||
.zIndex(9)
|
||||
|
||||
Row() {
|
||||
Slider({
|
||||
value: this.outSetValueOne,
|
||||
min: 1,
|
||||
max: 6,
|
||||
step: 1,
|
||||
style: SliderStyle.OutSet
|
||||
}).showSteps(false)
|
||||
.trackColor('rgba(255,255,255,0.6)')
|
||||
.selectedColor($r('app.color.theme_color'))
|
||||
.onChange((value: number) => {
|
||||
let val = Number(value.toFixed(2));
|
||||
this.slideChange(val);
|
||||
this.outSetValueOne = val;
|
||||
console.info('value:' + val + 'this.sliderTextPos:' + this.sliderTextPos);
|
||||
})
|
||||
}.width('100%')
|
||||
|
||||
}
|
||||
.height('60')
|
||||
.width('32.5%')
|
||||
.position({ x: '35%', y: '3%' })
|
||||
}
|
||||
}
|
72
code/BasicFeature/Media/Camera/entry/src/main/module.json5
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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": "MainAbility",
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet"
|
||||
],
|
||||
"metadata": [
|
||||
{
|
||||
"name": "ArkTSPartialUpdate",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false,
|
||||
"pages": "$profile:main_pages",
|
||||
"abilities": [
|
||||
{
|
||||
"name": "EntryAbility",
|
||||
"srcEntrance": "./ets/entryability/EntryAbility.ts",
|
||||
"description": "$string:EntryAbility_desc",
|
||||
"icon": "$media:icon",
|
||||
"label": "$string:EntryAbility_label",
|
||||
"startWindowIcon": "$media:icon",
|
||||
"startWindowBackground": "$color:start_window_background",
|
||||
"visible": true,
|
||||
"skills": [
|
||||
{
|
||||
"entities": [
|
||||
"entity.system.home"
|
||||
],
|
||||
"actions": [
|
||||
"action.system.home"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"requestPermissions": [
|
||||
{
|
||||
"name": "ohos.permission.CAMERA"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.MICROPHONE"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.WRITE_MEDIA"
|
||||
},
|
||||
{
|
||||
"name": "ohos.permission.READ_MEDIA"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|