2012年7月21日土曜日

各TAGとBUILD_ID

AOSPに新しいTAGが出現しました。
ざっと確認したところ、以下のような感じです。

tagBUILD_ID確認機種
android-4.1.1_r1JRO03CGalaxy Nexus
android-4.1.1_r1.1JRO03DNexus 7
android-4.1.1_r2JRO03ENexus S
android-4.1.1_r3JRO03HXoom Wifi model(wingray)*1
*1:7/29 追記

2012年7月5日木曜日

ASSIT機能アプリケーションを作成する

Goolge Nowのように、端末のASSIT機能として動作するアプリケーションを作成する方法です。
Google Now(= Intent.ACTION_ASSIST)の起動トリガーでも登場した、
Intent.ACTION_ASSISTを使用します。


http://developer.android.com/reference/android/content/Intent.html#ACTION_ASSIST


AndroidManifest.xmlのactivityタグに記載しておけば起動可能です。

        <activity
            android:name=".TestApp"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.ASSIST" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>


こんな感じでResolverに選択候補として表示されました。


f:id:baroqueworksdev:20120706015251p:image:w240


と、いうことでGoogle Nowの代わりに、
iPhoneの『Siri』やDocomoさんの『しゃべってコンシェル』のような、
3rdベンダーアプリもASSIT機能として搭載可能ですね!!

Google Now(= Intent.ACTION_ASSIST)の起動トリガー

以下の記事でNavigationBarからGoogle Nowを起動するトリガーを発見しました。

Android 4.1で追加されたNAVIGATION_BAR_PANEL_LAYERについて
http://d.hatena.ne.jp/baroqueworksdev/20120630/1341088267


今回はもう少し、調査してみます。
SearchPanelViewからGoogle Nowを起動する処理はこんな感じ。
<SDK>\sources\android-16\com\android\systemui\SearchPanelView.java

    private void startAssistActivity() {
        // Close Recent Apps if needed
        mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
        // Launch Assist
        Intent intent = SearchManager.getAssistIntent(mContext);
        if (intent == null) return;
        try {
            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
                    R.anim.search_launch_enter, R.anim.search_launch_exit,
                    getHandler(), this);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivity(intent, opts.toBundle());
        } catch (ActivityNotFoundException e) {
            Slog.w(TAG, "Activity not found for " + intent.getAction());
            onAnimationStarted();
        }
    }


どうやら、Google Nowは「Assist」というカテゴリーに属しているようです。
SearchManager#getAssistIntent()というモジュールから、
対象アプリを取得してアプリ起動を行っていました。
SearchManagerのソースを確認。

<SDK>\sources\android-16\android\app\SearchManager.java

    /**
     * Gets an intent for launching installed assistant activity, or null if not available.
     * @return The assist intent.
     *
     * @hide
     */
    public static final Intent getAssistIntent(Context context) {
        PackageManager pm = context.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_ASSIST);
        ComponentName component = intent.resolveActivity(pm);
        if (component != null) {
            intent.setComponent(component);
            return intent;
        }
        return null;
    }

Intent.ACTION_ASSISTというアクションIntentを持つアプリを取得しています。
これはAPI 16で新たに追加されたアクションです。


SearchManager#getAssistIntent()のコール個所

ASSITアプリを起動するソースは以下の3か所でした。

com\android\internal\policy\impl\LockScreen.java
  → ロック画面から起動
com\android\systemui\SearchPanelView.java
  → NavigationBarのタップ処理から起動
com\android\internal\policy\impl\PhoneWindowManager.java
  → KeyEvent.KEYCODE_ASSISTが押下されたときに起動

KeyEventにKEYCODE_ASSISTが追加されています!!
KeyEvent.KEYCODE_ASSISTが押下されたときに処理はこちら。

com\android\internal\policy\impl\PhoneWindowManager.java

    /** {@inheritDoc} */
    @Override
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
           :
           :
        } else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
            if (down) {
                if (repeatCount == 0) {
                    mAssistKeyLongPressed = false;
                } else if (repeatCount == 1) {
                    mAssistKeyLongPressed = true;
                    if (!keyguardOn) {
                         launchAssistLongPressAction();
                    }
                }
            } else {
                if (mAssistKeyLongPressed) {
                    mAssistKeyLongPressed = false;
                } else {
                    if (!keyguardOn) {
                        launchAssistAction();
                    }
                }
            }
            return -1;
        }
           :
           :
    }

    private void launchAssistLongPressAction() {
        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);

        // launch the search activity
        Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            // TODO: This only stops the factory-installed search manager.  
            // Need to formalize an API to handle others
            SearchManager searchManager = getSearchManager();
            if (searchManager != null) {
                searchManager.stopSearch();
            }
            mContext.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Slog.w(TAG, "No activity to handle assist long press action.", e);
        }
    }

    private void launchAssistAction() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
        Intent intent = SearchManager.getAssistIntent(mContext);
        if (intent != null) {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_SINGLE_TOP
                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            try {
                mContext.startActivity(intent);
            } catch (ActivityNotFoundException e) {
                Slog.w(TAG, "No activity to handle assist action.", e);
            }
        }
    }



KeyEvent.KEYCODE_ASSISの定義はこちら。
<SDK>\sources\android-16\android\view\KeyEvent.java

    /** Key code constant: Assist key.
     * Launches the global assist activity.  Not delivered to applications. */
    public static final int KEYCODE_ASSIST          = 219;

PhoneWindowManager#interceptKeyBeforeDispatching()で return -1を行っていますし、
コメントにあるように、アプリケーションには配信されないキーコードです。