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タグに記載しておけば起動可能です。

1
2
3
4
5
6
7
8
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/** {@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

1
2
3
/** 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を行っていますし、
コメントにあるように、アプリケーションには配信されないキーコードです。