借助 Android SDK,您可以访问 Glass EE2 提供的各种输入和传感器。 本页简要介绍了可用的功能、实现细节和实用提示。
触摸手势
您可以使用 Android SDK 启用对 Glass 触控板原始数据的访问权限。这是通过手势检测器实现的,该检测器可自动检测 Glass 上的常见手势,例如点按、滑动和滚动。
您还可以在应用中使用此手势检测器来处理点按、向前滑动、向后滑动和向下滑动手势。这与之前的 Glass 设备类似。
最好通过以下方式使用这些手势:
- 点按:确认或输入。
- 向前滑动、向后滑动:浏览卡片和界面。
- 向下滑动:返回或退出。
如需了解实现详情,请参阅手势检测器示例。
使用 Android 手势检测器检测手势
借助 Android GestureDetector,您可以检测简单和复杂的手势,例如使用多根手指或滚动的手势。
检测 activity 级手势
仅当界面中的哪个部分具有焦点无关紧要时,才在 activity 级别检测手势。
例如,如果您希望在用户点按触控板时显示菜单,无论哪个视图具有焦点,都可以在 activity 内处理 MotionEvent。
以下示例展示了使用
GestureDetector 的 activity 级手势检测,并实现了
GestureDetector.OnGestureListener 来处理识别的手势,然后将这些手势处理并转换为以下内容:
TAPSWIPE_FORWARDSWIPE_BACKWARDSWIPE_UPSWIPE_DOWN
Kotlin
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)) } }
Java
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 级手势检测器的示例:
Kotlin
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) } }
Java
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 Edition 2 支持语音识别的原生实现。 此功能仅支持英语。
语音识别界面会等待用户说话,然后在用户说完后返回转写文本。如需开始此活动,请按以下步骤操作:
- 使用
ACTION_RECOGNIZE_SPEECHintent 调用startActivityForResult()。启动 activity 时,支持以下 intent extra: - 替换
onActivityResult()回调以从EXTRA_RESULTSintent extra 接收转写文本,如以下代码示例所示。当用户结束说话时,系统会调用此回调。
Kotlin
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) }
Java
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); }
如需了解实现详情,请参阅语音识别应用示例。
关键字的偏差
Glass 上的语音识别功能可以偏向于某个关键字列表。偏向可提高关键字识别准确率。如需为关键字启用偏向,请使用以下代码:
Kotlin
val keywords = arrayOf("Example", "Biasing", "Keywords") val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) intent.putExtra("recognition-phrases", keywords) startActivityForResult(intent, SPEECH_REQUEST)
Java
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()以处理检测到的语音指令。
Kotlin
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) } } }
Java
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() 方法。
Kotlin
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() }
Java
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 操作结合使用:
Kotlin
sendBroadcast(Intent("reload-voice-commands"))
Java
sendBroadcast(new Intent("reload-voice-commands"));
在运行时启用和停用语音指令
您可以在运行时启用和停用语音指令。为此,请从 onCreatePanelMenu() 方法返回适当的值,如下所示:
- 将值设置为
true以启用。 - 将值设置为
false可停用。
调试模式
如需为语音指令启用调试模式,请在所需的 activity 中调用 getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS)。调试模式会激活以下功能:
- Logcat 包含带有识别到的短语的日志,用于调试。
- 检测到无法识别的命令时,系统会显示界面叠加层,如图所示:
Kotlin
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 ) } . . . }
Java
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 Edition 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 显示屏。如需了解详情,请参阅 传感器坐标系。
加速度计、陀螺仪和磁力计位于 Glass 设备的镜片舱上,用户可以旋转镜片舱,使设备与自己的视线对齐。您无法直接测量光学模块的角度,因此在将这些传感器的角度用于应用(例如指南针航向)时,请注意这一点。
为了节省电量,请仅在需要时监听传感器。在决定何时开始和停止监听传感器时,请考虑应用的需求和生命周期。
传感器事件回调在界面线程上运行,因此请尽可能快速地处理事件并返回。如果处理时间过长,请将传感器事件推送到队列中,并使用后台线程来处理这些事件。
50 Hz 通常是跟踪头部运动的足够采样率。
如需详细了解如何使用传感器,请参阅 Android 开发者指南。
位置信息服务
Glass Enterprise Edition 2 设备未配备 GPS 模块,因此无法提供用户的位置信息。不过,它确实实现了位置信息服务,这对于显示附近的 Wi-Fi 网络和蓝牙设备列表是必需的。
如果您的应用具有设备所有者权限,则可以使用该权限以编程方式更改相应安全设置的值:
Kotlin
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() ) }
Java
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 解决方案必须能够为您更改这些设置。