语音按键不能调用谷歌语音助手问题分析


当适配好语音遥控器之后,按下语音按键发送 KEYCODE_SEARCH 按键事件,如果安装了谷歌的套件以及谷歌的语音助手GoogleKatniss.apk,那么会调用谷歌的语音助手进行搜索,效果如下图。
在这里插入图片描述
搜索结果
但是遇到一个问题,就是按了语音按键之后,没有反应,不会弹出上面的界面。

流程分析

通过打印分析,按下语音按键之后,按键事件 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,就可以正常启动了。

好了,先简单分析到这里了~~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值