DeviceVirtualization Engine(简称DV Engine)是华为提供的多设备虚拟化平台,通过该平台,将周边的设备或设备器件转换为手机的虚拟器件,能够将周边设备能力作为手机系统通用能力来提供和使用,同时能够实现多虚拟设备能力同步协同使用。
应用开发者通过集成DV Engine,可以便捷使用外部的设备,包括但不限于摄像头、扬声器、显示器、麦克风、穿戴等外部设备能力以及相关器件的能力,并可以支持灵活的控制和切换,充分使用外部设备的资源优势,致力为消费者打造更佳的协同体验。
集成DeviceVirtualization Engine能力,需要完成以下准备工作:
具体操作,请按照《DeviceVirtualization Engine接入准备》中详细说明来完成。
1. 集成DeviceVirtualization Engine。
请依照《DeviceVirtualization Engine接入准备》中第六步完成DeviceVirtualization Engine的集成。
2. 添加appid信息。
在自身应用AndroidManifest.xml中添加开发者联盟创建应用时生成的appid信息,name为"com.huawei.hms.client.appid",value为"appid=******",******用自己注册的appid。DeviceVirtualization Engine接入权限申请,请依照《DeviceVirtualization Engine接入准备》中第四步"权限申请"的详细说明来完成。需要注意的是权限申请通过后才能接入,否则会在连接服务时抛出异常,调用DV Engine接口会返回ERROR_CODE_NO_PERMISSION。
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=xxxxxxxxx"/>
3. 兼容性检查:
MainActivity.java
// 根据当前集成的DV Engine版本来定义CURRENT_KIT_VERSION
static final String CURRENT_KIT_VERSION = "1.0.3.300";
......
private boolean isDvKitSupport() {
boolean isSupport = true;
try {
//获取当前Dv Engine服务的运行版本
String version = DvKit.getVersion();
if (version.compareTo(CURRENT_KIT_VERSION) < 0) {
//当前Dv Engine服务版本不满足应用的运行要求
isSupport = false;
}
} catch (NoClassDefFoundError e) {
//当前运行环境不支持Dv Engine
isSupport = false;
Log.e(TAG, "DvKit not exist", e);
}
return isSupport;
}
4. 应用权限申请。
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="com.huawei.permission.DISTRIBUTED_VIRTUALDEVICE" />
MainActivity.java
String[] permissions = new String[]{Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BODY_SENSORS,
"com.huawei.permission.DISTRIBUTED_VIRTUALDEVICE"};
......
mPermissionList.clear();
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(MainActivity.this, permissions[i])
!= PackageManager.PERMISSION_GRANTED) {
mPermissionList.add(permissions[i]);
}
}
if (mPermissionList.isEmpty()) {
// 未授予的权限为空,表示都授予了
Intent intent = new Intent(MainActivity.this, DvKitDemoActivity.class);
startActivity(intent);
} else {
// 请求权限方法
String[] permissions = mPermissionList.toArray(new String[mPermissionList.size()]);// 将List转为数组
requestPermissions(permissions, PERMISSIONS_REQUEST);
}
......
// 获取到权限后,打开DvKitDemo Activity
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST) {
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
mPermissionList.remove(permissions[i]);
}
}
if (mPermissionList.isEmpty()) {
Intent intent = new Intent(MainActivity.this, DvKitDemoActivity.class);
startActivity(intent);
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
5. 连接DV Engine服务并订阅设备能力状态变化事件。
DvKitDemoActivity.java
DvKit.getInstance().connect(getApplicationContext(), new IDvKitConnectCallback() {
//当Dv Engine服务连接成功时
@Override
public void onConnect(int i) {
addLog("init MSDPService done");
//获取虚拟设备服务实例
mVirtualDeviceManager = (VirtualDeviceManager) DvKit.getInstance().getKitService(VIRTUAL_DEVICE_CLASS);
//订阅虚拟设备能力状态变化事件
mVirtualDeviceManager.subscribe(EnumSet.of(VIRTUALDEVICE), observer);
}
@Override
public void onDisconnect() {
}
});
6. 发现设备
DvKitDemoActivity.java
//设置发现按钮
mStartDiscoveryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mVirtualDeviceManager == null) {
addLog("WechatAdapter is null");
return;
}
//调用发现接口
int ret = mVirtualDeviceManager.startDiscovery(discoveryCallback);
addLog("start discovery result is " + ret);
}
});
......
/**
* 设备发现的回调接口,获取发现到的虚拟设备
*/
private IDiscoveryCallback discoveryCallback = new IDiscoveryCallback() {
@Override
public void onFound(VirtualDevice device, int state) {
if (device == null) {
addLog("onDevice callback but device is null");
} else {
//将发现的设备存储到容器中,并且在界面上显示
if (!mVirtualDeviceMap.containsKey(device.getDeviceId())) {
addLog("onDevice Found: " + device.getDeviceId() + " Name: " + device.getDeviceName() + " Type:"
+ device.getDeviceType());
mVirtualDeviceMap.put(device.getDeviceId(), device);
handler.sendMessage(handler.obtainMessage(DEVICE_ADD, device));
}
}
}
@Override
public void onState(int state) {
}
};
7. 设备列表选择呈现
任何使用"华为DeviceVirtualization Engine"的应用,都必须提供如下界面供用户选择对应的可支持虚拟化的设备,由用户授权选择对应的设备完成能力接续。在发现设备后要弹出设备列表显式让用户选择,UI效果参考下图。
8. 使能设备能力。
DvKitDemoActivity.java
holder.camera_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String deviceId = (String) v.getTag();
VirtualDevice device = mVirtualDeviceMap.get(deviceId);
if (device == null) {
addLog("do not contains " + deviceId);
return;
}
if (!cameraOpend.containsKey(deviceId) || !cameraOpend.get(deviceId)) {
addLog("open camera " + device.getDeviceName());
//使能虚拟Camera
int ret = mVirtualDeviceManager.enableVirtualDevice(deviceId, EnumSet.of(CAMAERA), null);
addLog("open camera result is " + ret);
} else {
......
}
}
});
......
/**
* 设备能力状态变化的观察接口,用于获取设备能力的使能去使能状态变化
*/
private IVirtualDeviceObserver observer = new IVirtualDeviceObserver() {
//设备状态变化时的回调
@Override
public void onDeviceStateChange(VirtualDevice virtualDevice, int returncode) {
}
/**
*设备能力状态变化时的回调
*/
@Override
public void onDeviceCapabilityStateChange(VirtualDevice virtualDevice, Capability capability, int returncode) {
if (returncode == EventType.EVENT_DEVICE_CAPABILITY_ENABLE) {
//当设备能力使能成功时,应用处理
onEnable(virtualDevice, capability);
} else if (returncode == EventType.EVENT_DEVICE_CAPABILITY_DISABLE) {
//设备能力去使能成功时,应用处理逻辑
onDisable(virtualDevice, capability);
} else {
//设备能力异常时,应用处理逻辑
onError(virtualDevice, capability, returncode);
}
}
......
}
9. 通过Android接口使用虚拟设备能力,下面通过两个例子进行举例说明:
DvKitDemoActivity.java
/**
* 当设备能力使能成功时,应用处理
*/
public void onEnable(VirtualDevice device, Capability capability) {
switch (capability) {
case CAMARACAMERA:
//Camera使能成功
addLog("open camera success");
//将camera使能状态设置为true
cameraOpend.put(device.getDeviceId(), true);
//获取cameraID
String data = device.getData(Constants.ANDROID_CAMERAID_FRONT);
addLog("open camera " + device.getDeviceName() + " cameraId " + data);
//打开虚拟camera
Intent intent = new Intent(DvKitDemoActivity.this, RegitsterCameraAPI2.class);
intent.putExtra("cameraId", data);
Log.i(TAG, "cameraId is " + data);
startActivity(intent);
break;
case DISPLAY:
//display使能成功,将display使能状态设置为true
addLog("open display success");
displayOpend.put(device.getDeviceId(), true);
break;
case MIC:
//mic使能成功,将mic使能状态设置为true
addLog("open mic success");
micOpend.put(device.getDeviceId(), true);
break;
case SPEAKER:
//speaker使能成功,将speaker使能状态设置为true
addLog("open speaker success");
speakerOpend.put(device.getDeviceId(), true);
break;
case VIBRATE:
//vibrate使能成功,将vibrate使能状态设置为true
addLog("open vibrate success");
speakerOpend.put(device.getDeviceId(), true);
break;
case SENSOR:
//sensor使能成功,将sensor使能状态设置为true
addLog("open sensor success");
speakerOpend.put(device.getDeviceId(), true);
break;
case NOTIFICATION:
//notification使能成功,将notification使能状态设置为true
addLog("open notification success");
speakerOpend.put(device.getDeviceId(), true);
break;
}
}
本例通过Android Camera API2接口获取虚拟设备Camera预览流:
RegitsterCameraAPI2.java
private void openCamera(int width, int height) {
......
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
String cameraId;
cameraId = chooseCameraId();
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
fps = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
fpsList.clear();
for (int i = fps.length - 1; i >= 0; i--) {
Log.i(TAG, "index: " + i + ", [" + fps[i].getLower() + ", " + fps[i].getUpper() + "]");
fpsList.add(fps[i]);
}
fpsAdapter.notifyDataSetChanged();
mPreviewSize = new Size(1920, 1080);
Log.i(TAG, "openCamera, mPreviewSize width x mPreviewSize height = " + mPreviewSize.getWidth() + " x "
+ mPreviewSize.getHeight());
int orientation = getResources().getConfiguration().orientation;
Log.i(TAG, "orientation is : " + orientation);
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else {
mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
Log.i(TAG, "openCamera, mTextureView width x mTextureView height = " + mTextureView.getWidth() + " x "
+ mTextureView.getHeight());
Log.i(TAG, "openCamera, width x height = " + width + " x " + height);
configureTransform(width, height);
manager.openCamera(cameraId, mStateCallback, null);
......
}
private String chooseCameraId() {
return getIntent().getStringExtra("cameraId");
}
......
本例通过消息推送接口进行穿戴设备消息通知的发送:
NotificationTest.java
DvNotificationService notificationService = (DvNotificationService) HiWearActivity.mDvKit.getKitService(VIRTUAL_NOTIFICATION_SERVICE);
DvNotification notification = new DvNotification();
Map<String, Object> notificaitonProperty = new HashMap<>();
notificaitonProperty.put(DvNotification.KEY_PACKAGE_NAME, "packageName"); // 设置三方应用的包名,用于区分消息的来源
notificaitonProperty.put(DvNotification.KEY_GUIDE_DISTANCE, 500); // 设置导航的距离
notificaitonProperty.put(DvNotification.KEY_DISTANCE_UNIT, "m"); // 设置导航的单位
notificaitonProperty.put(DvNotification.KEY_DIRECTION_INDEX, 1); // 设置导航图标的索引
notificaitonProperty.put(DvNotification.KEY_GUIDE_TEXT, "turn left"); // 设置消息导航的文本
notificaitonProperty.put(DvNotification.KEY_VIBRATE, DvNotificationService.NORMAL_VIBRATION); // 设置消息提醒的震动方式
notification.setNotificationProperty(DvNotificationService.GUIDE_LAYOUT_TEMPLATE, notificaitonProperty); // 通过该接口设置导航消息的属性
int res = notificationService.doNotification(deviceId, notificationId, notification, DvNotificationService.ADD_NOTIFY); // 发送消息通知到设备的接口
10. 去使能虚拟设备能力。应用不想使用虚拟设备能力时,例如切回到手机能力时可以增加去使能操作。
DvKitDemoActivity.java
holder.camera_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String deviceId = (String) v.getTag();
VirtualDevice device = mVirtualDeviceMap.get(deviceId);
if (device == null) {
addLog("do not contains " + deviceId);
return;
}
if (!cameraOpend.containsKey(deviceId) || !cameraOpend.get(deviceId)) {
......
} else {
......
//去使能虚拟Camera
int ret = mVirtualDeviceManager.disableVirtualDevice(deviceId, EnumSet.of(CAMAERA));
......
}
}
});
......
/**
* 设备能力状态变化的观察接口,用于获取设备能力的使能去使能状态变化
*/
private IVirtualDeviceObserver observer = new IVirtualDeviceObserver() {
//设备状态变化时的回调
@Override
public void onDeviceStateChange(VirtualDevice virtualDevice, int returncode) {
}
/**
*设备能力状态变化时的回调
*/
@Override
public void onDeviceCapabilityStateChange(VirtualDevice virtualDevice, Capability capability, int returncode) {
if (returncode == EventType.EVENT_DEVICE_CAPABILITY_ENABLE) {
//当设备能力使能成功时,应用处理
onEnable(virtualDevice, capability);
} else if (returncode == EventType.EVENT_DEVICE_CAPABILITY_DISABLE) {
//设备能力去使能成功时,应用处理逻辑
onDisable(virtualDevice, capability);
} else {
//设备能力异常时,应用处理逻辑
onError(virtualDevice, capability, returncode);
}
}
......
}
11. 断开连接
DvKitDemoActivity.java
public void onDestroy() {
DvKit.getInstance().disConnect();
......
}
干得好,你已经成功完成了Codelab并学到了:
您可以阅读下面链接,了解更多相关的信息。
相关文档
您可以点击下方按钮下载源码。