通过语音输入,你可以创建真正的免触摸操作界面。Glass 为您提供了三种使用语音输入的方式。
主要语音指令会从首页卡片启动 Glassware,上下文语音指令可以在 activity 内执行操作,而系统的语音识别 activity 可让您接收来自用户的自由格式的语音输入。
主要语音指令
这些语音指令会从主屏幕卡片(时钟卡片)启动 Glassware。在您声明主语音指令后,如果用户决定通过点按主屏幕卡片来启动 Glassware,Glass 会自动创建一个触摸菜单项作为后备选项。
如需在 ok glass 语音主菜单中添加语音指令,请执行以下操作:
在
res/xml/<my_voice_trigger>.xml
中为语音指令创建一个 XML 资源,该资源使用VoiceTriggers.Command
中定义的某个现有语音指令。例如,这里展示了如何使用“开始跑步”选项。<?xml version="1.0" encoding="utf-8"?> <trigger command="START_A_RUN" />
如需创建一条语音指令来提示用户在启动您的 activity 或服务之前说出其他短语,请同时添加一个
input
元素。例如,如果您在使用“发布更新”功能,则可能需要执行此操作。<?xml version="1.0" encoding="utf-8"?> <trigger command="POST_AN_UPDATE"> <input prompt="@string/glass_voice_prompt" /> </trigger>
在 Android 清单中,使用
com.google.android.glass.action.VOICE_TRIGGER
操作注册一个 intent 过滤器。如果 intent 过滤器检测到用户说出您的语音指令,就会启动您的 activity 或服务。<?xml version="1.0" encoding="utf-8"?> <application ...> <activity | service ...> <intent-filter> <action android:name= "com.google.android.glass.action.VOICE_TRIGGER" /> </intent-filter> <meta-data android:name="com.google.android.glass.VoiceTrigger" android:resource="@xml/my_voice_trigger" /> </activity | service> // ... </application>
为您的 activity 或服务声明
android:icon
属性。这样一来,Glass 就会在 ok, glass 触摸菜单中显示 Glassware 的图标。<activity |service android:icon="@drawable/my_icon" ...> ... </activity | service>
如果您的语音指令使用语音提示并启动一个 activity,请使用以下代码获取任何转写文本(例如在
onResume()
中):ArrayList<String> voiceResults = getIntent().getExtras() .getStringArrayList(RecognizerIntent.EXTRA_RESULTS);
如果语音指令启动了服务,则
onStartCommand()
回调中会提供 intent extra:@Override public int onStartCommand(Intent intent, int flags, int startId) { ArrayList<String> voiceResults = intent.getExtras() .getStringArrayList(RecognizerIntent.EXTRA_RESULTS); // ... }
设置限制
如果您需要以下某项或所有功能来启动 Glassware,请在 res/xml/<my_voice_trigger>.xml
资源中指定这些功能。如果这些功能不可用,Glass 会停用以下语音指令:
camera
network
microphone
<trigger command="POST_AN_UPDATE"> <constraints camera="true" network="true" /> </trigger>
情境语音指令
情境语音指令可让用户通过 activity 执行操作。您可以使用标准 Android 菜单 API 构建上下文语音指令,但用户可以使用语音指令(而不是轻触)调用菜单项。
如需为特定 activity 启用上下文语音指令,请执行以下操作:
在所需 activity 中调用
getWindow().requestFeature(
WindowUtils.FEATURE_VOICE_COMMANDS
)
即可启用上下文语音指令。启用此功能后,每当此 activity 获得焦点时,系统都会在屏幕页脚区域显示 ok glass 菜单。替换
onCreatePanelMenu()
并处理启用WindowUtils.FEATURE_VOICE_COMMANDS
的情况。启用后,您可以在此处执行一次性菜单设置,例如膨胀菜单资源或调用Menu.add()
方法来创建语音菜单系统。替换
onMenuItemSelected()
以在用户说出语音指令时处理它们。用户选择完菜单项后,只要 activity 保持焦点,“ok, glass”语音指令会自动重新出现在屏幕的页脚部分中,准备接受新的语音指令。以下代码会启用上下文语音指令、适时扩充菜单资源,并在用户说出语音指令时处理语音指令:
public class ContextualMenuActivity extends Activity { @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); // Requests a voice menu on this activity. As for any other // window feature, be sure to request this before // setContentView() is called getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS); setContentView(R.layout.activity_main); } @Override public boolean onCreatePanelMenu(int featureId, Menu menu) { if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) { getMenuInflater().inflate(R.menu.main, menu); return true; } // Pass through to super to setup touch menu. return super.onCreatePanelMenu(featureId, menu); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) { switch (item.getItemId()) { case R.id.dogs_menu_item: // handle top-level dogs menu item break; case R.id.cats_menu_item: // handle top-level cats menu item break; case R.id.lab_menu_item: // handle second-level labrador menu item break; case R.id.golden_menu_item: // handle second-level golden menu item break; case R.id.calico_menu_item: // handle second-level calico menu item break; case R.id.cheshire_menu_item: // handle second-level cheshire menu item break; default: return true; } return true; } // Good practice to pass through to super if not handled return super.onMenuItemSelected(featureId, item); } }
下面是上一个 activity 使用的菜单资源示例。请注意如何为分层语音菜单系统创建嵌套菜单项。在以下示例中,第一个菜单项可通过以下方式访问:Ok glass, Show me dogs, Labrador。
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Use the constants defined in the ContextualMenus.Command enum--> <item android:id="@+id/dogs_menu_item" android:title="@string/show_me_dogs"> <menu> <item android:id="@+id/lab_menu_item" android:title="@string/labrador" /> <item android:id="@+id/golden_menu_item" android:title="@string/golden" /> </menu> </item> <item android:id="@+id/cats_menu_item" android:title="@string/show_me_cats"> <menu> <item android:id="@+id/cheshire_menu_item" android:title="@string/cheshire" /> <item android:id="@+id/calico_menu_item" android:title="@string/calico" /> </menu> </item> </menu>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Use the constants defined in the ContextualMenus.Command enum--> <item android:id="@+id/play_menu_item" android:title="PLAY_MUSIC" /> <item android:id="@+id/pause_menu_item" android:title="PAUSE_MUSIC" /> </menu>
(可选)替换
onPreparePanel()
,检查是否已启用WindowUtils.FEATURE_VOICE_COMMANDS
。启用后,您可以在此处执行其他逻辑来设置菜单系统,例如根据某些条件添加和移除某些菜单项。您还可以根据某些条件开启(返回true
)和关闭(返回false
)上下文语音菜单。例如:private boolean mVoiceMenuEnabled; ... @Override public boolean onPreparePanel(int featureId, View view, Menu menu) { if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) { // toggle this boolean on and off based on some criteria return mVoiceMenuEnabled; } // Good practice to call through to super for other cases return super.onPreparePanel(featureId, view, menu); }
同时支持语音菜单和触摸菜单
由于上下文语音指令使用现有的 Android 菜单 API,因此您可以重复使用触摸菜单已有的大量代码和资源,同时支持这两种类型的菜单。
除了您已在几种方法中检查的 WindowUtils.FEATURE_VOICE_COMMANDS
功能之外,您只需检查 Window.FEATURE_OPTIONS_PANEL
功能,然后添加逻辑,以针对某些用户操作(例如点按)打开触摸菜单。
例如,您可以更改上一个 activity 示例,以添加对触摸菜单的支持,如下所示(更改带有注释):
// 1. Check for Window.FEATURE_OPTIONS_PANEL
// to inflate the same menu resource for touch menus.
@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {
if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS ||
featureId == Window.FEATURE_OPTIONS_PANEL) {
...
}
// 2. Check for Window.FEATURE_OPTIONS_PANEL
// to handle touch menu item selections.
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS ||
featureId == Window.FEATURE_OPTIONS_PANEL) {
...
}
完成这些更改后,您可以点按或说出 Ok glass 以显示菜单。
使用不公开列出的语音指令进行开发
如果您想分发 Glassware,则必须使用 VoiceTriggers.Command
中已获批准的主语音指令,以及 ContextualMenus.Command
中已获批准的上下文语音指令。
如果您想使用 GDK 中未提供的语音指令,可以在 AndroidManifest.xml
文件中请求 Android 权限:
<uses-permission
android:name="com.google.android.glass.permission.DEVELOPMENT" />
使用未列出的主要语音指令
在
res/values/strings.xml
中声明一个字符串值,用于定义语音触发器的名称。(可选)在启动 Glassware 之前声明语音提示以显示语音识别 Glassware。<?xml version="1.0" encoding="utf-8"?> <resources> <string name="glass_voice_trigger">read me a story</string> <string name="glass_voice_prompt">what story?</string> </resources>
在
res/xml/<my_voice_trigger>.xml
中为语音触发器创建 XML 资源。对于未列出的语音指令,您应该使用keyword
属性,而不是用于已批准的语音指令的command
属性。keyword
属性应该是对定义语音指令的字符串资源的引用。对于立即启动 activity 或服务的简单语音触发器,只需指定trigger
元素即可:<?xml version="1.0" encoding="utf-8"?> <trigger keyword="@string/glass_voice_trigger" />
如需创建语音触发器来提示用户在启动您的 activity 或服务之前说出其他短语,请也添加一个输入元素:
<?xml version="1.0" encoding="utf-8"?> <trigger keyword="@string/glass_voice_trigger"> <input prompt="@string/glass_voice_prompt" /> </trigger>
使用未列出的上下文相关语音指令
创建菜单项时,请使用任意文本作为菜单项的标题。例如:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Use the constants defined in the ContextualMenus.Command enum-->
<item
android:id="@+id/pizza_menu_item"
android:title="@string/find_pizza" />
</menu>
启动语音识别
语音识别 Glassware 会等待用户讲话,并在用户讲完后返回转录的文本。如需启动 activity,请执行以下操作:
- 使用
ACTION_RECOGNIZE_SPEECH
intent 调用startActivityForResult()
。启动 activity 时支持以下 intent extra: 替换
onActivityResult()
回调,以从EXTRA_RESULTS
intent extra 接收转录文本。当用户结束讲话时,系统会调用此回调。private static final int SPEECH_REQUEST = 0; 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); }