与手机一样,访问音频眼镜和显示眼镜上的摄像头和麦克风等敏感硬件需要获得用户的明确同意。这些权限被视为眼镜专用权限,即使应用已在手机上获得相应的权限,也必须在运行时请求这些权限。
在应用的清单中声明权限
在请求权限之前,您必须使用 <uses-permission> 元素在应用的清单中声明这些权限文件。无论权限是用于手机还是眼镜专用功能,此声明都保持不变,但您仍必须针对眼镜专用硬件或功能明确请求该权限。
<manifest ...>
<!-- Only declare permissions that your app actually needs. In this example,
we declare permissions for the camera. -->
<uses-permission android:name="android.permission.CAMERA"/>
<application ...>
...
</application>
</manifest>
在请求眼镜权限之前
查看权限原则和工作流,确保您为用户提供出色的体验,例如检查用户是否已授予应用所需的运行时权限,以及应用是否应在请求眼镜专用权限之前向用户显示理由。
权限情景
在音频眼镜和带屏眼镜上请求危险的运行时权限时,您可能会遇到以下几种不同的情形:
- 从投影的 activity 请求运行时权限
- 从手机 activity 请求运行时权限
如需详细了解每种情况,请参阅本指南中的以下部分。
从投影的 activity 请求和处理运行时权限
如需为无屏眼镜和带屏眼镜请求权限,请先使用 ActivityResultLauncher 和 ProjectedPermissionsResultContract 方法注册权限启动器。当用户对权限请求做出响应后,回调会收到一个权限名称与其授予状态的映射。
// Register the permissions launcher using the ProjectedPermissionsResultContract. private val requestPermissionLauncher: ActivityResultLauncher<List<ProjectedPermissionsRequestParams>> = registerForActivityResult(ProjectedPermissionsResultContract()) { results -> if (results[Manifest.permission.CAMERA] == true) { isPermissionDenied = false initializeGlassesFeatures() } else { // Handle permission denial. isPermissionDenied = true } }
代码要点
- 您需要指定应用所需的权限,例如
Manifest.permission.CAMERA或Manifest.permission.RECORD_AUDIO。 - 您的应用应在启动器的回调中妥善处理授权和拒绝结果。
如需触发权限请求流程,请将 ProjectedPermissionsRequestParams 的列表传递给已注册的权限启动器的 launch 方法。ProjectedPermissionsRequestParams 对象将所请求的清单权限与自定义 rationale 字符串捆绑在一起。rationale 字符串必须清晰简洁地说明应用需要访问眼镜的硬件功能(例如相机或麦克风)的原因。
private fun requestHardwarePermissions() { val params = ProjectedPermissionsRequestParams( permissions = listOf(Manifest.permission.CAMERA), rationale = "Camera access is required to overlay digital content on your physical environment." ) requestPermissionLauncher.launch(listOf(params)) }
代码要点
- 在启动器上调用
launch会触发权限请求用户流程。
了解权限请求用户流程
当您使用 ProjectedPermissionsResultContract 方法启动权限请求时,系统会在眼镜和手机上启动协调的用户流程。
在权限用户流程中,您的应用和用户可以预期以下情况:
在眼镜上:投影设备(眼镜)上显示一条活动,指示用户查看手机以继续操作。
在手机上:同时,在宿主设备(手机)上启动 activity。此界面会显示您提供的理由字符串,并让用户选择继续或取消。
在手机上:如果用户接受了理由,手机上会显示一个经过修改的 Android 系统权限对话框,告知用户他们正在为眼镜(而非手机)授予权限,用户可以正式授予或拒绝授予权限。
接收结果:用户做出最终选择后,手机和眼镜上的活动都会被关闭。然后,系统会调用
ActivityResultLauncher回调,并传入一个包含每项所请求权限的授予状态的映射。
在手机 activity 中请求运行时权限
如果您的应用在手机 activity 中运行,但需要音频或带屏眼镜的权限(例如,让用户将视频流切换到眼镜的摄像头以获得第一人称视角),请使用特定于设备的 Activity#requestPermissions(permissions, requestCode,
deviceId) 方法请求权限。
如需定位音频眼镜和带屏眼镜,请在投影设备上下文上调用 getDeviceId 以获取相应的设备 ID。从手机 activity 请求权限时,请传递此 ID,如以下示例所示:
// Request the projected permission from phone activity requestPermissions( arrayOf(Manifest.permission.CAMERA), // REQUEST_CODE_GLASSES_CAMERA is a developer-defined constant REQUEST_CODE_GLASSES_CAMERA, projectedDeviceId )
处理权限结果
用户在手机上响应权限对话框后,系统会通过调用 onRequestPermissionsResult 回调将用户的决定传递给您的应用。
如需处理响应,请在发起权限请求的 Activity 实例中替换 onRequestPermissionsResult。
以下代码段展示了如何处理回调并验证用户是否已授予所请求的权限:
private companion object { // REQUEST_CODE_GLASSES_CAMERA is a developer-defined constant. const val REQUEST_CODE_GLASSES_CAMERA = 1001 } @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray, deviceId: Int ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults, deviceId) // Handle the result of the permission request if (requestCode == REQUEST_CODE_GLASSES_CAMERA && deviceId == projectedDeviceId) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Proceed with glasses camera features } else { // Handle glasses permission denied } } }
代码要点
- 使用带有
onRequestPermissionsResult回调的deviceId重载,以确保权限状态正确映射到特定情境,例如音频或带屏眼镜,而不是宿主手机。 - 您的应用应妥善处理授权和拒绝结果。