通过打印分析,按下语音按键之后,按键事件 KEYCODE_SEARCH 会传递到frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java 里面
case KeyEvent.KEYCODE_SEARCH: {
/*
* Do this in onKeyUp since the Search key is also used for
* chording quick launch shortcuts.
*/
if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
break;
}
if (event.isTracking() && !event.isCanceled()) {
launchDefaultSearch(event);
}
return true;
}
接着跑到 launchDefaultSearch(event) 里面,接着看下 launchDefaultSearch(event) 的代码
/**
* Helper method for adding launch-search to most applications. Opens the
* search window using default settings.
*
* @return true if search window opened
*/
private boolean launchDefaultSearch(KeyEvent event) {
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
&& !isTvUserSetupComplete()) {
// If we are in Setup or Post-Setup update mode on TV, consume the search key
return false;
}
boolean result;
final Callback cb = getCallback();
if (cb == null || isDestroyed()) {
result = false;
} else {
sendCloseSystemWindows("search");
int deviceId = event.getDeviceId();
SearchEvent searchEvent = null;
if (deviceId != 0) {
searchEvent = new SearchEvent(InputDevice.getDevice(deviceId));
}
try {
result = cb.onSearchRequested(searchEvent);
} catch (AbstractMethodError e) {
Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement"
+ " method onSearchRequested(SearchEvent); fa", e);
result = cb.onSearchRequested();
}
}
if (!result && (getContext().getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
// On TVs, if the app doesn't implement search, we want to launch assist.
Bundle args = new Bundle();
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());
return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE))
.launchLegacyAssist(null, UserHandle.myUserId(), args);
}
return result;
}
通过注释我们可以看到,If we are in Setup or Post-Setup update mode on TV, consume the search key
,意思当我们的电视还在类似开机向导初始化设置的话,就把这个key event 消费掉(就是把这个按键事件丢弃掉,不做处理)。如果没有被消费掉的话,会往下跑,On TVs, if the app doesn’t implement search, we want to launch assist. 这句话的意思是,在电视上,如果没有app实现了搜索功能,我们想要启动 assist (类似谷歌语音助手)。于是会跑到 launchLegacyAssist 里面。
在 frameworks/base/services/core/java/com/android/server/search/SearchManagerService.java 里面
launchLegacyAssist 的实现是这样的
@Override
public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) {
ComponentName comp = getLegacyAssistComponent(userHandle);
if (comp == null) {
return false;
}
long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(Intent.ACTION_ASSIST);
intent.setComponent(comp);
IActivityManager am = ActivityManager.getService();
return am.launchAssistIntent(intent, ActivityManager.ASSIST_CONTEXT_BASIC, hint,
userHandle, args);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
这个函数先通过 getLegacyAssistComponent 获取到包含 Intent.ACTION_ASSIST action 的包名和activity name,然后去启动它,就达到最开始的图示效果。
private ComponentName getLegacyAssistComponent(int userHandle) {
try {
userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userHandle, true, false, "getLegacyAssistComponent", null);
IPackageManager pm = AppGlobals.getPackageManager();
Intent assistIntent = new Intent(Intent.ACTION_ASSIST);
ResolveInfo info =
pm.resolveIntent(assistIntent,
assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY, userHandle);
if (info != null) {
return new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
}
} catch (RemoteException re) {
// Local call
Log.e(TAG, "RemoteException in getLegacyAssistComponent: " + re);
} catch (Exception e) {
Log.e(TAG, "Exception in getLegacyAssistComponent: " + e);
}
return null;
}
我们可以看到 GoogleKatniss.apk 里面的 com.google.android.katniss.search.SearchActivity 是有android.intent.action.ASSIST的action,所以按下语音按键之后,启动的就是这个activity。
上面我们分析到,按键有可能会被消费掉,接下来我们看一下 isTvUserSetupComplete() 这个函数
/**
* Check if Setup or Post-Setup update is completed on TV
* @return true if completed
*/
private boolean isTvUserSetupComplete() {
boolean isTvSetupComplete = Settings.Secure.getInt(getContext().getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
isTvSetupComplete &= Settings.Secure.getInt(getContext().getContentResolver(),
Settings.Secure.TV_USER_SETUP_COMPLETE, 0) != 0;
return isTvSetupComplete;
}
在 isTvUserSetupComplete() 里面就是查询 Settings.Secure.USER_SETUP_COMPLETE 的值和 Settings.Secure.TV_USER_SETUP_COMPLETE 的值,当这两个属性的值都不为0的时候,返回true,表示电视用户的初始化设置已经完成了。
这两个属性是在 frameworks/base/core/java/android/provider/Settings.java 里面定义的
/**
* Whether the current user has been set up via setup wizard (0 = false, 1 = true)
* @hide
*/
public static final String USER_SETUP_COMPLETE = "user_setup_complete";
/**
* Whether the current user has been set up via setup wizard (0 = false, 1 = true)
* This value differs from USER_SETUP_COMPLETE in that it can be reset back to 0
* in case SetupWizard has been re-enabled on TV devices.
*
* @hide
*/
public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
我们可以通过adb shell来查询这两个属性的值
settings get secure user_setup_complete
settings get secure tv_user_setup_complete
设置这两个属性的值
settings put secure user_setup_complete 1
settings put secure tv_user_setup_complete 1
查询到 user_setup_complete 的值为0,tv_user_setup_complete 的值为1,那么isTvUserSetupComplete() 的返回值是false。
private boolean launchDefaultSearch(KeyEvent event) {
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
&& !isTvUserSetupComplete()) {
// If we are in Setup or Post-Setup update mode on TV, consume the search key
return false;
}
那么就会跑到 return false,所以语音按键事件就被消费掉,自然也就启动不了谷歌语音助手了。只要把Settings.Secure.USER_SETUP_COMPLETE 的值设为1,就可以正常启动了。
好了,先简单分析到这里了~~~