借助 Android SDK,您可以使用 Glass EE2 提供的各种输入源和传感器。 本页面简要介绍了可用功能、实现详情和实用提示。
触摸手势
您可以使用 Android SDK 从 Glass 触控板访问原始数据。这通过手势检测器来实现,该检测器可自动检测 Glass 上的常用手势,例如点按、滑动和滚动。
您还可以在应用中使用此手势检测器,以考虑点按、向前滑动、向后滑动和向下滑动。这与以前的 Glass 设备类似。
最好通过以下方式使用这些手势:
- 点按:确认或进入。
- 向前滑动、向后滑动:浏览卡片和屏幕。
- 向下滑动:返回或退出。
如需了解实现详情,请参阅示例手势检测器。
使用 Android 手势检测器检测手势
借助 Android GestureDetector
,您可以检测简单和复杂的手势,例如使用多个手指或滚动的手势。
检测 activity 级别的手势
仅当界面的哪一部分处于焦点时,才可以在 Activity 级别检测手势。
例如,如果您想在用户点按触控板时调出菜单,而不管视图的焦点是什么,请在 activity 内处理 MotionEvent
。
以下示例展示了 Activity 级手势检测,它使用 GestureDetector
并实现 GestureDetector.OnGestureListener
来处理识别出的手势,然后处理这些手势并将其转换成以下代码:
TAP
SWIPE_FORWARD
SWIPE_BACKWARD
SWIPE_UP
SWIPE_DOWN
class GlassGestureDetector(context: Context, private val onGestureListener: OnGestureListener) :
GestureDetector.OnGestureListener {
private val gestureDetector = GestureDetector(context, this)
enum class Gesture {
TAP,
SWIPE_FORWARD,
SWIPE_BACKWARD,
SWIPE_UP,
SWIPE_DOWN
}
interface OnGestureListener {
fun onGesture(gesture: Gesture): Boolean
}
fun onTouchEvent(motionEvent: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(motionEvent)
}
override fun onDown(e: MotionEvent): Boolean {
return false
}
override fun onShowPress(e: MotionEvent) {}
override fun onSingleTapUp(e: MotionEvent): Boolean {
return onGestureListener.onGesture(Gesture.TAP)
}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
return false
}
override fun onLongPress(e: MotionEvent) {}
/**
* Swipe detection depends on the:
* - movement tan value,
* - movement distance,
* - movement velocity.
*
* To prevent unintentional SWIPE_DOWN and SWIPE_UP gestures, they are detected if movement
* angle is only between 60 and 120 degrees.
* Any other detected swipes, will be considered as SWIPE_FORWARD and SWIPE_BACKWARD, depends
* on deltaX value sign.
*
* ______________________________________________________________
* | \ UP / |
* | \ / |
* | 60 120 |
* | \ / |
* | \ / |
* | BACKWARD <------- 0 ------------ 180 ------> FORWARD |
* | / \ |
* | / \ |
* | 60 120 |
* | / \ |
* | / DOWN \ |
* --------------------------------------------------------------
*/
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val deltaX = e2.x - e1.x
val deltaY = e2.y - e1.y
val tan =
if (deltaX != 0f) abs(deltaY / deltaX).toDouble() else java.lang.Double.MAX_VALUE
return if (tan > TAN_60_DEGREES) {
if (abs(deltaY) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityY) < SWIPE_VELOCITY_THRESHOLD_PX) {
false
} else if (deltaY < 0) {
onGestureListener.onGesture(Gesture.SWIPE_UP)
} else {
onGestureListener.onGesture(Gesture.SWIPE_DOWN)
}
} else {
if (Math.abs(deltaX) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityX) < SWIPE_VELOCITY_THRESHOLD_PX) {
false
} else if (deltaX < 0) {
onGestureListener.onGesture(Gesture.SWIPE_FORWARD)
} else {
onGestureListener.onGesture(Gesture.SWIPE_BACKWARD)
}
}
}
companion object {
private const val SWIPE_DISTANCE_THRESHOLD_PX = 100
private const val SWIPE_VELOCITY_THRESHOLD_PX = 100
private val TAN_60_DEGREES = tan(Math.toRadians(60.0))
}
}
public class GlassGestureDetector implements GestureDetector.OnGestureListener {
enum Gesture {
TAP,
SWIPE_FORWARD,
SWIPE_BACKWARD,
SWIPE_UP,
SWIPE_DOWN,
}
interface OnGestureListener {
boolean onGesture(Gesture gesture);
}
private static final int SWIPE_DISTANCE_THRESHOLD_PX = 100;
private static final int SWIPE_VELOCITY_THRESHOLD_PX = 100;
private static final double TAN_60_DEGREES = Math.tan(Math.toRadians(60));
private GestureDetector gestureDetector;
private OnGestureListener onGestureListener;
public GlassGestureDetector(Context context, OnGestureListener onGestureListener) {
gestureDetector = new GestureDetector(context, this);
this.onGestureListener = onGestureListener;
}
public boolean onTouchEvent(MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return onGestureListener.onGesture(Gesture.TAP);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
/**
* Swipe detection depends on the:
* - movement tan value,
* - movement distance,
* - movement velocity.
*
* To prevent unintentional SWIPE_DOWN and SWIPE_UP gestures, they are detected if movement
* angle is only between 60 and 120 degrees.
* Any other detected swipes, will be considered as SWIPE_FORWARD and SWIPE_BACKWARD, depends
* on deltaX value sign.
*
* ______________________________________________________________
* | \ UP / |
* | \ / |
* | 60 120 |
* | \ / |
* | \ / |
* | BACKWARD <------- 0 ------------ 180 ------> FORWARD |
* | / \ |
* | / \ |
* | 60 120 |
* | / \ |
* | / DOWN \ |
* --------------------------------------------------------------
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
final float deltaX = e2.getX() - e1.getX();
final float deltaY = e2.getY() - e1.getY();
final double tan = deltaX != 0 ? Math.abs(deltaY/deltaX) : Double.MAX_VALUE;
if (tan > TAN_60_DEGREES) {
if (Math.abs(deltaY) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityY) < SWIPE_VELOCITY_THRESHOLD_PX) {
return false;
} else if (deltaY < 0) {
return onGestureListener.onGesture(Gesture.SWIPE_UP);
} else {
return onGestureListener.onGesture(Gesture.SWIPE_DOWN);
}
} else {
if (Math.abs(deltaX) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityX) < SWIPE_VELOCITY_THRESHOLD_PX) {
return false;
} else if (deltaX < 0) {
return onGestureListener.onGesture(Gesture.SWIPE_FORWARD);
} else {
return onGestureListener.onGesture(Gesture.SWIPE_BACKWARD);
}
}
}
}
用法示例
为了利用 activity 级别的手势检测,您需要完成以下任务:
- 将以下声明添加到清单文件中的应用声明中。这样,您的应用就可以接收 Activity 中的
MotionEvent
:<application>
<!-- Copy below declaration into your manifest file -->
<meta-data
android:name="com.google.android.glass.TouchEnabledApplication"
android:value="true" />
</application> - 替换 activity 的
dispatchTouchEvent(motionEvent)
方法,将动作事件传递给手势检测器的onTouchEvent(motionEvent)
方法。 - 在您的 activity 中实现
GlassGestureDetector.OnGestureListener
。
以下是 activity 级手势检测器的示例:
class MainAcvitiy : AppCompatActivity(), GlassGestureDetector.OnGestureListener {
private lateinit var glassGestureDetector: GlassGestureDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
glassGestureDetector = GlassGestureDetector(this, this)
}
override fun onGesture(gesture: GlassGestureDetector.Gesture): Boolean {
when (gesture) {
TAP ->
// Response for TAP gesture
return true
SWIPE_FORWARD ->
// Response for SWIPE_FORWARD gesture
return true
SWIPE_BACKWARD ->
// Response for SWIPE_BACKWARD gesture
return true
else -> return false
}
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
return if (glassGestureDetector.onTouchEvent(ev)) {
true
} else super.dispatchTouchEvent(ev)
}
}
public class MainActivity extends AppCompatActivity implements OnGestureListener {
private GlassGestureDetector glassGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glassGestureDetector = new GlassGestureDetector(this, this);
}
@Override
public boolean onGesture(Gesture gesture) {
switch (gesture) {
case TAP:
// Response for TAP gesture
return true;
case SWIPE_FORWARD:
// Response for SWIPE_FORWARD gesture
return true;
case SWIPE_BACKWARD:
// Response for SWIPE_BACKWARD gesture
return true;
default:
return false;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (glassGestureDetector.onTouchEvent(ev)) {
return true;
}
return super.dispatchTouchEvent(ev);
}
}
音频输入
Glass Enterprise Edition 2 是基于 AOSP 的标准设备,支持基本音频源。
以下音频源已实现高级信号处理:
语音识别
Glass Enterprise 第 2 版支持原生语音识别。 仅支持英语。

语音识别界面会等待用户说话,然后在用户转录完成后返回转录文本。如需启动该 activity,请按以下步骤操作:
- 使用
ACTION_RECOGNIZE_SPEECH
intent 调用startActivityForResult()
。启动 activity 时,支持以下 intent extra: - 替换
onActivityResult()
回调以接收来自EXTRA_RESULTS
intent extra 的转录文本,如以下代码示例所示。当用户讲话完毕时,系统就会调用此回调。
private const val SPEECH_REQUEST = 109
private fun displaySpeechRecognizer() {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
startActivityForResult(intent, SPEECH_REQUEST)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
val results: List<String>? =
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
val spokenText = results?.get(0)
// Do something with spokenText.
}
super.onActivityResult(requestCode, resultCode, data)
}
private static final int SPEECH_REQUEST = 109;
private void displaySpeechRecognizer() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
startActivityForResult(intent, SPEECH_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
List<String> results = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
String spokenText = results.get(0);
// Do something with spokenText.
}
super.onActivityResult(requestCode, resultCode, data);
}
如需了解实现详情,请参阅语音识别应用示例。
关键字偏向
Google Glass 的语音识别功能可以针对一系列关键字进行自定义调整。偏向可提高关键字识别的准确性。要为关键字启用偏向,请使用以下代码:
val keywords = arrayOf("Example", "Biasing", "Keywords")
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra("recognition-phrases", keywords)
startActivityForResult(intent, SPEECH_REQUEST)
final String[] keywords = {"Example", "Biasing", "Keywords"};
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra("recognition-phrases", keywords);
startActivityForResult(intent, SPEECH_REQUEST);
语音指令
语音指令可让用户通过 activity 执行操作。您可以使用标准 Android 菜单 API 构建语音指令,但用户可以使用语音指令(而不是轻触操作)调用菜单项。
如需为特定 Activity 启用语音指令,请按以下步骤操作:
- 在所需 Activity 中调用
getWindow().requestFeature(FEATURE_VOICE_COMMANDS)
以启用语音指令。启用此功能后,每当此 activity 获得焦点时,麦克风图标都会显示在屏幕的左下角。 - 在您的应用中请求
RECORD_AUDIO
权限。 - 替换
onCreatePanelMenu()
并膨胀菜单资源。 - 替换
onContextItemSelected()
以处理检测到的语音指令。
class VoiceCommandsActivity : AppCompatActivity() {
companion object {
const val FEATURE_VOICE_COMMANDS = 14
const val REQUEST_PERMISSION_CODE = 200
val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO)
val TAG = VoiceCommandsActivity::class.java.simpleName
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.requestFeature(FEATURE_VOICE_COMMANDS)
setContentView(R.layout.activity_voice_commands)
// Requesting permissions to enable voice commands menu
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_PERMISSION_CODE) {
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Permission denied. Voice commands menu is disabled.")
}
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
override fun onCreatePanelMenu(featureId: Int, menu: Menu): Boolean {
menuInflater.inflate(R.menu.voice_commands_menu, menu)
return true
}
override fun onContextItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
// Handle selected menu item
R.id.edit -> {
// Handle edit action
true
}
else -> super.onContextItemSelected(item)
}
}
}
public class VoiceCommandsActivity extends AppCompatActivity {
private static final String TAG = VoiceCommandsActivity.class.getSimpleName();
private static final int FEATURE_VOICE_COMMANDS = 14;
private static final int REQUEST_PERMISSION_CODE = 200;
private static final String[] PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(FEATURE_VOICE_COMMANDS);
setContentView(R.layout.activity_voice_commands);
// Requesting permissions to enable voice commands menu
ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_PERMISSION_CODE) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Permission denied. Voice commands menu is disabled.");
}
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
getMenuInflater().inflate(R.menu.voice_commands_menu, menu);
return true;
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
// Handle selected menu item
case R.id.edit:
// Handle edit action
return true;
default:
return super.onContextItemSelected(item);
}
}
}
以下是上一个 Activity 使用的菜单资源示例:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"/>
<item
android:id="@+id/edit"
android:icon="@drawable/ic_edit"
android:title="@string/edit"/>
<item
android:id="@+id/find"
android:icon="@drawable/ic_search"
android:title="@string/find"/>
</menu>
如需查看完整的示例,请参阅记事记事应用示例。
正在重新加载语音指令列表
您可以动态重新加载语音指令列表。为此,请替换 onCreatePanelMenu()
方法中的 menu
资源,或修改 onPreparePanel()
方法中的菜单对象。如需应用更改,请调用 invalidateOptionsMenu()
方法。
private val options = mutableListOf<String>()
fun onPreparePanel(featureId: Int, view: View?, menu: Menu): Boolean {
if (featureId != FEATURE_VOICE_COMMANDS) {
return super.onCreatePanelMenu(featureId, menu)
}
for (optionTitle in options) {
menu.add(optionTitle)
}
return true
}
/**
* Method showing example implementation of voice command list modification
*
* If you call [Activity.invalidateOptionsMenu] method, voice command list will be
* reloaded (onCreatePanelMenu and onPreparePanel methods will be called).
*/
private fun modifyVoiceCommandList() {
options.add("Delete")
options.add("Play")
options.add("Pause")
invalidateOptionsMenu()
}
private final List<String> options = new ArrayList<>();
@Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
if (featureId != FEATURE_VOICE_COMMANDS) {
return super.onCreatePanelMenu(featureId, menu);
}
for (String optionTitle : options) {
menu.add(optionTitle);
}
return true;
}
/**
* Method showing example implementation of voice command list modification
*
* If you call {@link Activity#invalidateOptionsMenu()} method, voice command list will be
* reloaded (onCreatePanelMenu and onPreparePanel methods will be called).
*/
private void modifyVoiceCommandList() {
options.add("Delete");
options.add("Play");
options.add("Pause");
invalidateOptionsMenu();
}
AppCompatActivity 解决方案
如需在扩展 AppCompatActivity
的 activity 中重新加载语音指令列表,请将 sendBroadcast()
方法与 reload-voice-commands
intent 操作结合使用:
sendBroadcast(Intent("reload-voice-commands"))
sendBroadcast(new Intent("reload-voice-commands"));
在运行时启用和停用语音指令
您可以在运行时启用和停用语音指令。为此,请从 onCreatePanelMenu()
方法返回适当的值,如下所示:
- 将值设为
true
即可启用。 - 将值设为
false
即可停用。
调试模式
如需为语音指令启用调试模式,请在所需的 Activity 中调用 getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS)
。调试模式可启用以下功能:
- Logcat 包含可识别的用于调试的日志。
- 当系统检测到无法识别的命令时,会显示界面叠加层,如下所示:

class VoiceCommandsActivity : AppCompatActivity() {
companion object {
const val FEATURE_VOICE_COMMANDS = 14
const val FEATURE_DEBUG_VOICE_COMMANDS = 15
const val REQUEST_PERMISSION_CODE = 200
val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO)
val TAG = VoiceCommandsActivity::class.java.simpleName
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.requestFeature(FEATURE_VOICE_COMMANDS)
window.requestFeature(FEATURE_DEBUG_VOICE_COMMANDS)
setContentView(R.layout.activity_voice_commands)
// Requesting permissions to enable voice commands menu
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
}
.
.
.
}
public class VoiceCommandsActivity extends AppCompatActivity {
private static final String TAG = VoiceCommandsActivity.class.getSimpleName();
private static final int FEATURE_VOICE_COMMANDS = 14;
private static final int FEATURE_DEBUG_VOICE_COMMANDS = 15;
private static final int REQUEST_PERMISSION_CODE = 200;
private static final String[] PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(FEATURE_VOICE_COMMANDS);
getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS);
setContentView(R.layout.activity_voice_commands);
// Requesting permissions to enable voice commands menu
ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION_CODE);
}
.
.
.
}
如需了解实现详情,请阅读重新加载应用的示例语音指令。
文字转语音 (TTS)
Text-to-Speech 功能可将数字文字转换为合成语音输出。如需了解详情,请访问 Android 开发者文档 TextToSpeech。
Glass EE2 安装了 Google 文字转语音引擎。它被设置为默认文字转语音 (TTS) 引擎,可离线使用。
Google 文字转语音引擎捆绑了以下语言区域:
孟加拉语 普通话 捷克语 丹麦语 德语 希腊语 英语 西班牙语 爱沙尼亚语 芬兰语 法语 古吉拉特语 印地语 |
匈牙利语 印度尼西亚语 意大利语 日语 爪哇语 澳洲语 澳洲语 卡纳达语 韩语 马拉雅拉姆语 挪威语 荷兰语 |
波兰语 葡萄牙语 俄语 斯洛伐克语 巽他语 瑞典语 泰米尔语 泰卢固语 泰语 土耳其语 乌克兰语 越南语 |
摄像头
Glass Enterprise Edition 2 配有 800 万像素固定焦距摄像头,该摄像头采用 f/2.4 光圈,4:3 的传感器宽高比,83° 对角线视野范围(71° x 57°,横向)。我们建议您使用标准的 CameraX 或 Camera2 API。
如需了解实现详情,请阅读示例相机应用。
相机按钮
相机按钮是 Glass Enterprise 第 2 版设备的合页上的物理按钮。
它可以像标准的键盘操作一样处理,并且可以通过 KeyEvent#KEYCODE_CAMERA
键码识别。
从 OPM1.200313.001 操作系统更新开始,具有以下操作的 intent 会从启动器应用发送:
- 对相机按钮短按
MediaStore#ACTION_IMAGE_CAPTURE
。 - 长按相机按钮
MediaStore#ACTION_VIDEO_CAPTURE
。
传感器
在开发者使用 Glass EE2 开发应用时,有多种传感器可供使用。
Glass 支持以下标准 Android 传感器:
-
TYPE_ACCELEROMETER
-
TYPE_GRAVITY
-
TYPE_GYROSCOPE
-
TYPE_LIGHT
-
TYPE_LINEAR_ACCELERATION
-
TYPE_MAGNETIC_FIELD
-
TYPE_ORIENTATION
(已弃用) -
TYPE_ROTATION_VECTOR
Glass 不支持以下 Android 传感器:
Glass 传感器坐标系如下图所示。相对于 Glass 显示屏。如需了解详情,请参阅传感器坐标系。

加速度计、陀螺仪和磁力计位于 Google Glass 设备的光学仪表板上,用户可以旋转该仪表板,使设备与视线对齐。您无法直接测量光学广告连播的角度,因此在针对应用(如罗盘方向)使用这些传感器的角度时,请注意这一点。
为了延长电池续航时间,请仅在需要时使用传感器。在决定何时开始和停止监听传感器时,请考虑应用的需求和生命周期。
传感器事件回调在界面线程上运行,因此请尽快处理事件并返回。如果处理时间太长,请将传感器事件推送到队列中,并使用后台线程来处理它们。
50 Hz 通常是一个足够的采样率,能够跟踪头部动作。
如需详细了解如何使用传感器,请参阅 Android 开发者指南。
位置信息服务
Glass Enterprise Edition 2 设备未配备 GPS 模块,不提供用户所在位置。不过,它确实实现了位置信息服务,这是显示附近 Wi-Fi 网络和蓝牙设备的列表所必需的。
如果您的应用具有设备所有者权限,您可以使用它以编程方式更改相应安全设置的值:
val devicePolicyManager = context
.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
if (devicePolicyManager.isDeviceOwnerApp(context.getPackageName())) {
val componentName = ComponentName(context, MyDeviceAdmin::class.java)
devicePolicyManager.setSecureSetting(
componentName,
Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_SENSORS_ONLY.toString()
)
}
final DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context
.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (devicePolicyManager.isDeviceOwnerApp(context.getPackageName())) {
final ComponentName componentName = new ComponentName(context, MyDeviceAdmin.class);
devicePolicyManager.setSecureSetting(componentName, Settings.Secure.LOCATION_MODE,
String.valueOf(Settings.Secure.LOCATION_MODE_SENSORS_ONLY));
}
如果您使用第三方 MDM 解决方案,MDM 解决方案必须能够为您更改这些设置。