本文介绍如何动态获取摄像头和录音权限。
1. Android SDK动态获取权限
1.1. 动态获取摄像头权限
1.1.1. 工程AndroidManifest.xml配置
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:allowBackup="true"
android:debuggable="true"
android:extractNativeLibs="true" //必需
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Aspdemo"
tools:ignore="HardcodedDebugMode">
1.1.2. 云手机实例配置
//禁止SDK start的时候获取本机权限
bundle.putBoolean(StreamView.CONFIG_TAKE_PERMISION_REQ, false);
//或者
IASPEngine engine = mBuilder.takePermissionReq(false).enableRTC(true).build(context);
//禁止SDK默认加载本地摄像头
mStreamView.enableSDKLoadCpdToStart(false);
1.1.3. 监听云手机需要申请本机摄像头权限
mStreamView.getASPEngineDelegate().addDataChannel(new DataChannel("wy_vdagent_default_dc") {
@Override
protected void onConnectStateChanged(DataChannelConnectState state) {
Log.i(TAG, "wy_vdagent_default_dc dc connection state changed to " + state);
}
@Override
protected void onReceiveData(byte[] buf) {
String str = "";
try {
str = new String(buf, "UTF-8");
} catch (UnsupportedEncodingException e) {
str = new String(buf);
}
Log.i(TAG, "wy_vdagent_default_dc dc received " + buf.length + " bytes data:" + str);
CommandUtils.parseCommand(str, new CommandUtils.CommandListener() {
@Override
public void onCameraAuthorize() {
checkStartCpd();
}
@Override
public void onRotation(int rotation) {
//云手机旋转状态
}
@Override
public void onUnknownCommand(String cmd) {
showError("未知命令: " + cmd);
}
});
}
});
package com.example.aspdemo.cloudphone.Util;
import android.util.Log;
import org.json.JSONObject;
public class CommandUtils {
public interface CommandListener {
void onCameraAuthorize();
void onRotation(int rotation);
void onUnknownCommand(String cmd);
}
public static void parseCommand(String data, CommandListener listener) {
try {
// 统一处理前缀
String jsonStr = data.replaceFirst("^data:", "").trim();
JSONObject json = new JSONObject(jsonStr);
if (json.has("action")) {
String action = json.getString("action");
handleCommand(action, json, listener);
}
else if (json.has("cmd")) {
String cmd = json.getString("cmd");
handleCommand(cmd, json, listener);
}
} catch (Exception e) {
Log.e("CommandUtils", "解析失败: " + data, e);
}
}
private static void handleCommand(String cmd, JSONObject json, CommandListener listener) {
if (listener == null) return;
try {
switch (cmd) {
case "authorizeCamera":
listener.onCameraAuthorize();
break;
case "rotation":
listener.onRotation(json.getInt("value"));
break;
// 添加其他命令...
default:
listener.onUnknownCommand(cmd);
}
} catch (Exception e) {
Log.e("handleCommand", "失败: ", e);
}
}
}
1.1.4. 权限申请处理
private void checkStartCpd() {
runOnUiThread(() -> {
if (checkCameraPermission()) {
startCpd();
} else {
requestMultiplePermissions();
}
});
}
private void startCpd() { //加载本地摄像头
Log.i(TAG, "to startCpd");
mStreamView.getEngine().startCpd();
}
private boolean checkCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
private void requestMultiplePermissions() {
Log.i(TAG, "requestMultiplePermissions ");
String[] permissions = {
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
};
ActivityCompat.requestPermissions(
this,
permissions,
MULTI_PERMISSION_REQUEST_CODE
);
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MULTI_PERMISSION_REQUEST_CODE) {
handleMultiPermissionResult(permissions, grantResults);
}
}
private void handleMultiPermissionResult(String[] permissions, int[] grantResults) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
// 所有权限均已授予
startCpd();
} else {
// 有权限被拒绝
handlePermissionDenied();
}
}
// 处理有权限被拒绝的情况
private void handlePermissionDenied() {
// 检查用户是否选择了"不再询问"
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
// 显示引导用户去设置页面的对话框
new AlertDialog.Builder(this)
.setTitle("权限被永久拒绝")
.setMessage("您已永久拒绝相机权限,如需使用此功能,请到设置中手动启用权限")
.setPositiveButton("去设置", (dialog, which) -> openAppSettings())
.setNegativeButton("取消", null)
.create()
.show();
} else {
Toast.makeText(this, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
}
}
// 打开应用设置页面
private void openAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
1.2. 动态获取录音权限
1.2.1. 工程AndroidManifest.xml配置
<uses-permission android:name="android.permission.RECORD_AUDIO" />
1.2.2. 监听云手机需要申请本机录音权限
mStreamView.getASPEngineDelegate().registerSystemPermissionListener(new IRequestSystemPermissionListener() {
@Override
public boolean onRequestSystemPermission(SystemPermission systemPermission) {
Log.i(TAG, "onRequestSystemPermission " + systemPermission);
if (systemPermission == RECORD_AUDIO) {
if (!checkAudioPermission()) {
new Handler(Looper.getMainLooper()).post(() -> {
requestAudioPermission();
});
return false;
}
return true;
}
return false;
}
});
1.2.3. 权限申请处理
private boolean checkAudioPermission() {
return ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
}
private void requestAudioPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.RECORD_AUDIO)) {
// 显示解释对话框
new androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("需要麦克风权限")
.setMessage("此功能需要使用您的麦克风进行录音")
.setPositiveButton("确定", (dialog, which) -> {
// 请求权限
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.RECORD_AUDIO},
AUDIO_PERMISSION_REQUEST_CODE
);
})
.setNegativeButton("取消", null)
.show();
} else {
// 直接请求权限
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.RECORD_AUDIO},
AUDIO_PERMISSION_REQUEST_CODE
);
}
}
2. iOS SDK动态获取摄像头权限
2.1. 工程info.plist配置
录音权限
Privacy - Microphone Usage Description
相机权限
Privacy - Camera Usage Description
2.2. 云手机实例配置
//禁止SDK默认加载本地摄像头
streamView.enableSDKLoadCpdToStart = false;
2.3. 监听云手机需要申请本机摄像头权限
@implementation DemoEDSAgentChannel
- (void)onConnectStateChanged:(ASPDCConnectState)state {
NSLog(@"[DemoEDSAgentChannel] onConnectStateChanged %ld", state);
}
- (void)onReceiveData:(NSData * _Nonnull)buf {
NSString *string = [[NSString alloc] initWithData:buf encoding:NSUTF8StringEncoding];
NSLog(@"[DemoEDSAgentChannel] onConnectStateChanged %@", string);
[CommandUtils parseCommand:string listener:self];
}
#pragma mark - CommandListener
- (void)onCameraAuthorize {
NSLog(@"[DemoEDSAgentChannel] onCameraAuthorize");
dispatch_async(dispatch_get_main_queue(), ^{
[self requestCameraPermission];
});
}
- (void)requestCameraPermission {
if ([AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)]) {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
case AVAuthorizationStatusNotDetermined:
// 用户尚未授权,请求权限
[self requestAccess];
break;
case AVAuthorizationStatusAuthorized:
// 已授权,直接启动摄像头
[self.streamView startCpd];
break;
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
// 被拒绝或受限,显示提示信息
[self showPermissionDeniedAlert];
break;
}
} else {
NSLog(@"DemoEDSAgentChannel 设备不支持摄像头");
[self showNoCameraAlert];
}
}
- (void)requestAccess {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
NSLog(@"DemoEDSAgentChannel 摄像头权限已授予");
[self.streamView startCpd];
} else {
NSLog(@"DemoEDSAgentChannel 摄像头权限被拒绝");
[self showPermissionDeniedAlert];
}
}];
}
3. Web SDK动态获取权限
3.1. 禁止连接使用摄像头
connConfig: {
disableCamera: true,
}
3.2. 监听云手机需要申请本机摄像头权限
session.addHandle('onConnected', (data) => {
session.addDataChannelListener('wy_vdagent_default_dc', 'data', data => {
console.log('data from wy_vdagent_default_dc', data)
parseCommand(data)
});
});
function parseCommand(data) {
try {
// 移除前缀 "data:"
const jsonStr = data.replace(/^data:/, '').trim();
const json = JSON.parse(jsonStr);
if (json.action !== undefined) {
handleCommand(json.action, json);
} else if (json.cmd !== undefined) {
handleCommand(json.cmd, json);
} else {
console.warn("无效命令格式:", jsonStr);
}
} catch (e) {
console.error("解析失败:", data, e);
}
}
function handleCommand(cmd, json) {
try {
switch (cmd) {
case "authorizeCamera":
if (session != null) {
console.log(`setCameraCanUse true`);
session.setLocalConfig('setCameraCanUse', true);
}
break;
case "rotation":
const rotationValue = parseInt(json.value, 10);
break;
default:
break;
}
} catch (e) {
console.error("命令处理失败:", cmd, e);
}
}
4. 相关文档
该文章对您有帮助吗?