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并学到了:

您可以阅读下面链接,了解更多相关的信息。
相关文档

您可以点击下方按钮下载源码。

源码下载

已复制代码