ラベル memo の投稿を表示しています。 すべての投稿を表示
ラベル memo の投稿を表示しています。 すべての投稿を表示

2012年8月24日金曜日

Application#onTerminate()がコールされない


(Activityではなく)Application単位でresume/pauseのハンドリングが可能か調査を行いました。
結論からいうと、frameworksからコールされる明示的なモジュールはありませんでした。


で、気になったのがApplication#onTerminate()というモジュール。
Android Developerには以下のように記載されています。

http://developer.android.com/reference/android/app/Application.html#onTerminate()

This method is for use in emulated process environments. It will never be called on a production Android device, where processes are removed by simply killing them; no user code (including this callback) is executed when doing so.

そう、Androidデバイスでは呼ばれることがないと書かれています。
じゃあ、何のために存在するのでしょう?

Application#onTerminate()をコールする箇所をgrepすると、ActivityThread.javaにありました。

                case EXIT_APPLICATION:
                    if (mInitialApplication != null) {
                        mInitialApplication.onTerminate();
                    }
                    Looper.myLooper().quit();
                    break;


この処理が行われるのは、ActivityThread#scheduleExit()の内部からhandleメッセージが発行されたとき。

ActivityThread#scheduleExit()をコールするのは、以下のActivityManagerService.javaの2モジュール。


private final boolean attachApplicationLocked(IApplicationThread thread, int pid)
final void trimApplications()

モジュール内の処理を見てみると、以下のような処理でした。
                    if (app.pid > 0 && app.pid != MY_PID) {
                        EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                                app.processName, app.setAdj, "empty");
                        Process.killProcessQuiet(app.pid);
                    } else {
                        try {
                            app.thread.scheduleExit();
                        } catch (Exception e) {
                            // Ignore exceptions.
                        }
                    }
ActivityThread#scheduleExit()をコールする条件は次の場合。
  • PIDが0未満
  • PIDがMY_PID(= System Process)


System Processと同じApplicationの場合、コールするみたいなんです。
そんなApplicationってありえるのでしょうか。。。


このソースを見る限り、商品として発売されているAndroidデバイスでは呼ばれないことは確実ですね。

2012年8月15日水曜日

何気なく使っているFramework 第5回 NFCManager


確認OSバージョン

Android OS 2.3 GingerBread

NFCManagerの役割り

NfcAdapterのHelperを提供
NfcAdapter
  • Tagディスパッチを要求に使用するIntentの定義
  • フォアグラウンドでのTagディスパッチとフォアグランドでのNDEFプッシュ用の登録メソッドを提供
  • 現在、フォアグラウンドNDEFプッシュはピアツーピアのみのサポート
NfcService
  • intent-filterのActionが"android.nfc.action.TECH_DISCOVERED"のActivityへTagを配信

アプリケーションPackage『com.android.nfc』を確認中。
(作成中)

NFC規格一覧

android.nfc.techパッケージにNFCの規格に順じたクラスが存在します。

クラス名規格名備考
NfcANFC-A (ISO 14443-3A)
NfcBNFC-B (ISO 14443-3B)
NfcFNFC-F (JIS 6319-4) Felica
NfcVNFC-V (ISO 15693)
IsoDepISO-DEP (ISO 14443-4)
NdefNFC Forum data format
NdefFormatable
MifareClassicNFC-Aベースのデータ。MIFARE Pro,MIFARE PLUSなどがある
MifareUltralightMifareClassicからセキュリティといくつかのコマンドを除いたもの

f:id:baroqueworksdev:20110730025735p:image:w640

何気なく使っているFramework 第6回 画面ロック設定



確認OSバージョン

Android 2.3.5_r1

キーガード設定

Settings.apkの「Screen unlock security」から画面ロック設定ができる。
ロック解除を行うパターンやパスワードの情報は、frameworks層の"LockPatternUtils"にて管理する。

種別設定クラス備考
NoneChooseLockGeneric.java「Screen unlock security」のメニュー画面(PreferenceActivity)
パターンChooseLockPattern.java
PINChooseLockPassword.java数字のみのパスワード
PasswordChooseLockPassword.java英字/数字のパスワード


LockPatternUtilsの役割

  • パスワード情報をSHA-1とMD5を用いてハッシュコード化する
  • ハッシュコードをFileに保存、保存先は/data/system/XXX.key
  • ファイルからパスワード情報をreadし、入力したデータとチェックを行う。
  • DevicePolicyManagerにパスワードクオリティを保存する



DevicePolicyManagerに宣言されているパスワードクオリティ

種別クオリティ備考
NonePASSWORD_QUALITY_UNSPECIFIED 
パターンPASSWORD_QUALITY_SOMETHING
PINPASSWORD_QUALITY_NUMERIC数字のみのパスワード
PasswordPASSWORD_QUALITY_ALPHABETIC英字のみパスワード
PasswordPASSWORD_QUALITY_ALPHANUMERIC英字/数字のパスワード


f:id:baroqueworksdev:20110817001517p:image:w640

HoneyCombでのDisplayについて


stackoverflowにて、以下のような質問がありました。
How can I get android Honeycomb system’s screen width and height?


DisplayってStatusBar込みの画面サイズだったような。。。っと思い、
手元にあるNexus S と Xoomで確認をしました。

ログの出力方法

Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);

Log.e("","------------------ ");
Log.e("","display.getHeight() = " + display.getHeight());
Log.e("","display.getWidth() = " + display.getWidth());
Log.e("","------------------ ");
Log.e("","displayMetrics.heightPixels = " + displayMetrics.heightPixels);
Log.e("","displayMetrics.widthPixels = " + displayMetrics.widthPixels);
Log.e("","------------------ ");



以下のような結果となりました。
HoneyCombではSystemBarの高さ分だけ、Heightから引かれているようです。


端末vergetHeight()getWidth()
Nexus S2.3.5800480
Xoom3.27521280


GingerBreadまではStatusBarの領域まで描画できたのに対し、
HoneyCombはSystemBarが隠せない?のが影響しているのでしょうか。

何気なく使っているFramework 第7回 スクリーンショット


今回から確認OSバージョンをAndroid OS 4.0.1_r1に変更しました。


確認OSバージョン

Android OS 4.0.1_r1 ICS

スクリーンショット


ICSから端末単体でスクリーンショットが撮れるようになりました。
とりあえず、frameworks内の確認!!


スクリーンショットが実行されるまでの流れ


  1. Native層のInputDispatcherからinterceptKeyBeforeQueueingをコール
  2. 一旦、Java層にコールバック
  3. InputManagerを経てPhoneWindowManager#interceptKeyBeforeQueueing

    1. interceptKeyBeforeQueueing
    2. interceptScreenshotChord()
    3. mScreenshotChordLongPress#run
    4. takeScreenshot()
  4. SystemUIに新たに追加されたService、TakeScreenshotService

    1. run()
  5. GlobalScreenshot#takeScreenshot
  6. Surface.screenshotをコールし画面イメージのBitmap取得
  7. アニメーションして保存


確認しておいた方がいいソース

\frameworks\base\services\input
- InputDispatcher.cpp
- InputManager.cpp
\frameworks\base\policy\src\com\android\internal\policy\impl
- PhoneWindowManager.java
\frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot
- GlobalScreenshot.java
- TakeScreenshotService.java


簡易クラス図


f:id:baroqueworksdev:20111204004218p:image:w640

何気なく使っているFrameworks 第9回 CPU使用状況を表示/ addView


CPU使用状況を表示

ICSから開発者向けオプションに「CPU使用状況を表示」が追加されました。

f:id:baroqueworksdev:20120124001631p:image:w360


Settingsで設定すると

以下の処理順でサービス:LoadAverageServiceが起動します。
このサービス内でProcessから取得した値をオーバーレイ表示を行っています。

値が何を意味するのかはProcessStats.javaをみればわかります。
ついでに、Viewの追加→WindowManagerServiceまでの流れも要チェックです。

特にViewRoot.javaがViewRootImpl.javaへ変わり、中身もそれなりに修正されています。

DevelopmentSettings#onPreferenceTreeClick
- writeCpuUsageOptions()
- startService "com.android.systemui.LoadAverageService" of SysttemUI.
- LoadAverageService#onCreate()
- new LoadView extends ProcessStats
- new Stats
- WindowManager#addView() with Overlay layer of SECURE_SYSTEM_OVERLAY_LAYER
- WindowManagerImpl#addView
- ViewRootImpl#setView
- IWindowSession#add
- Session#add
- WindowManagerService#addWindow


確認しておいた方がいいソース

\packages\apps\Settings\src\com\android\settings
  • DevelopmentSettings.java

\frameworks\base\packages\SystemUI\src\com\android\systemui
  • LoadAverageService.java

\frameworks\base\core\java\com\android\internal\os
  • ProcessStats.java

\frameworks\base\core\java\android\view
  • ViewRootImpl.java

\frameworks\base\services\java\com\android\server\wm
  • WindowManagerService.java

\frameworks\base\policy\src\com\android\internal\policy\impl
  • PhoneWindowManager.java


簡易クラス図

f:id:baroqueworksdev:20120124002429p:image:w640

PhoneモードとTabletモードの切り分け


Android 4.x(Ice Cream Sandwich)はPhoneとTablet、両方のOSとして使用されます。
では、どこでPhone/Tabletの切り分けはどこで行っているか確認します。

Phone/Tabletの切り分け

起動処理中、以下の処理でFrameworks内のDisplaySizeを初期化します。
ServerThread#run()
- WindowManagerService#displayReady
- PhoneWindowManagerService#setInitialDisplaySize


PhoneWindowManagerService#setInitialDisplaySizeのそれらしき処理があります。

// Determine whether the status bar can hide based on the size
// of the screen.  We assume sizes > 600dp are tablets where we
// will use the system bar.
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / DisplayMetrics.DENSITY_DEVICE;
mStatusBarCanHide = shortSizeDp < 600;


ソースは
 スクリーンのswWidth * DisplayMetrics.DENSITY_DEFAULT
 DisplayMetrics.DENSITY_DEVICE が600未満かどうか
と記述されています。


DisplayMetrics.javaに記述されている値はこんな感じ。
    /**
     * Standard quantized DPI for medium-density screens.
     */
    public static final int DENSITY_MEDIUM = 160;

    /**
     * The reference density used throughout the system.
     */
    public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;

    /**
     * The device's density.
     * @hide becase eventually this should be able to change while
     * running, so shouldn't be a constant.
     */
     public static final int DENSITY_DEVICE = getDeviceDensity();

     private static int getDeviceDensity() {
         // qemu.sf.lcd_density can be used to override ro.sf.lcd_density
         // when running in the emulator, allowing for dynamic configurations.
         // The reason for this is that ro.sf.lcd_density is write-once and is
         // set by the init process when it parses build.prop before anything else.
         return SystemProperties.getInt("qemu.sf.lcd_density",
         SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT));
     }


crespo(Nexus s)の場合、
 swWidth = 480
 ro.sf.lcd_density=240
 shortSizeDp = 480 * 160 / 240 = 320

よって、Phoneモードとなる。


maguro(Galaxy Nexus)の場合、
 swWidth = 720
 ro.sf.lcd_density=320
 shortSizeDp = 720 * 160 / 320 = 360

よって、Phoneモードとなる。

maguro(Galaxy Nexus)でTabletモードで起動したい場合、
lcd_densityを320から160に変更すれば可能なはず。

 swWidth = 720
 ro.sf.lcd_density=160
 shortSizeDp = 720 * 160 / 160 = 720



確認しておきたいソース


\frameworks\base\policy\src\com\android\internal\policy\impl
- PhoneWindowManagerService.java

\frameworks\base\core\java\android\util
- DisplayMetrics.java

\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar
- この配下全部 StatusBar / SystemBarの切り分けなど。

NavigationBarの有無判定


アプリケーション作成時に「NavigationBarが表示されている端末かどうか?」の判定が必要となった場合、どうすればいいか確認します。

以下のモジュールがFrameworks内にあります
 PhoneWindowManager#hasNavigationBar()
 WindoManagerService#hasNavigationBar()

このモジュールを使用しているソースがあるかどうかチェック。
アプリケーションで使えそうなモジュールはViewConfigurationというクラス。

public class ViewConfiguration {
    private ViewConfiguration(Context context) {
             :
        if (!sHasPermanentMenuKeySet) {
            IWindowManager wm = Display.getWindowManager();
            try {
                sHasPermanentMenuKey = wm.canStatusBarHide() && !wm.hasNavigationBar();
                sHasPermanentMenuKeySet = true;
            } catch (RemoteException ex) {
                sHasPermanentMenuKey = false;
            }
        }
             :
    }

    public boolean hasPermanentMenuKey() {
        return sHasPermanentMenuKey;
    }
}


hasPermanentMenuKey()というmethodは
Permanentな(常に押下できる)MenuKeyは存在する/しないか判定するmethod。

sHasPermanentMenuKeyは以下の条件で値が決定する。
wm.canStatusBarHide()は「Phone/Tabletの判定」、wm.hasNavigationBar()は「NavigationBarの有無判定」なので、
Tabletも場合、そのそもNavigationBarは非対応なので、falseとなる。
Phone ModeでかつNavigationBarがない場合、trueとなる。
Phone ModeでかつNavigationBarがある場合、falseとなる。

となる。

と、いうことでアプリ側で以下のように値を取得すればOK。

//hasPermanentMenuKey == true はNavigationBarが非表示
//hasPermanentMenuKey == false はNavigationBarが表示
boolean isNavigationBar = ! ViewConfiguration.get(this).hasPermanentMenuKey();


関連リンク:

 何気なく使っているFrameworks 第10回 NavigationBar / ナビゲーションバー

 EmulatorでNavigationBarを表示する

プリインストールアプリの無効化



f:id:baroqueworksdev:20120215235245p:image:w360


設定のアプリ一覧からプリインストールの無効化ができます。
ただし、すべてのプリインストールが無効にできるわけではありません。


以下のソースを確認。

packages\apps\Settings\src\com\android\settings\applications
  • InstalledAppDetails.java

    private void initUninstallButtons() {
        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
        boolean enabled = true;
        if (mUpdatedSysApp) {
            mUninstallButton.setText(R.string.app_factory_reset);
        } else {
            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                enabled = false;
                if (SUPPORT_DISABLE_APPS) {
                    try {
                        // Try to prevent the user from bricking their phone
                        // by not allowing disabling of apps signed with the
                        // system cert and any launcher app in the system.
                        PackageInfo sys = mPm.getPackageInfo("android",
                                PackageManager.GET_SIGNATURES);
                        Intent intent = new Intent(Intent.ACTION_MAIN);
                        intent.addCategory(Intent.CATEGORY_HOME);
                        intent.setPackage(mAppEntry.info.packageName);
                        List homes = mPm.queryIntentActivities(intent, 0);
                        if ((homes != null && homes.size() > 0) ||
                                (mPackageInfo != null && mPackageInfo.signatures != null &&
                                        sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
                            // Disable button for core system applications.
                            mUninstallButton.setText(R.string.disable_text);
                        } else if (mAppEntry.info.enabled) {
                            mUninstallButton.setText(R.string.disable_text);
                            enabled = true;
                        } else {
                            mUninstallButton.setText(R.string.enable_text);
                            enabled = true;
                        }
                    } catch (PackageManager.NameNotFoundException e) {
                        Log.w(TAG, "Unable to get package info", e);
                    }
                }
            } else {
                mUninstallButton.setText(R.string.uninstall_text);
            }
        }
        // If this is a device admin, it can't be uninstall or disabled.
        // We do this here so the text of the button is still set correctly.
        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
            enabled = false;
        }
        mUninstallButton.setEnabled(enabled);
        if (enabled) {
            // Register listener
            mUninstallButton.setOnClickListener(this);
        }
    }


以下のプリインストールアプリは無効化できません。
  • マーケットからUpdateを行った
  • Intent.CATEGORY_HOMEのActivityを持っている (= Launcher Application)
  • System と同じSignature を持っている ( = System Application)


Disableを選択すると、PackegeManager#setApplicationEnabledSetting()がよばれ無効化されます。
無効化されたアプリは、アプリ一覧に表示されなくなります。

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

2012年6月30日土曜日

PhoneモードとTabletモードの切り分け

ICSから変更されています。
これは注目すべき変更点です。

Phone/Tabletの切り分け

PhoneWindowManagerService#setInitialDisplaySizeの処理を確認

        // SystemUI (status bar) layout policy
        int shortSizeDp = shortSize
                * DisplayMetrics.DENSITY_DEFAULT
                / DisplayMetrics.DENSITY_DEVICE;

        if (shortSizeDp < 600) {
            // 0-599dp: "phone" UI with a separate status & navigation bar
            mHasSystemNavBar = false;
            mNavigationBarCanMove = true;
        } else if (shortSizeDp < 720) {
            // 600-719dp: "phone" UI with modifications for larger screens
            mHasSystemNavBar = false;
            mNavigationBarCanMove = false;
        } else {
            // 720dp: "tablet" UI with a single combined status & navigation bar
            mHasSystemNavBar = true;
            mNavigationBarCanMove = false;
        }


ICSでは600dp以上をTabletとして扱っていました。
JBでTabletとして認識するには720dp以上が必要です。

Android 4.1で追加されたNAVIGATION_BAR_PANEL_LAYERについて

f:id:baroqueworksdev:20120701053040p:image:w240

以下の記事にて、表示レイヤーの確認を行いました。

JBの表示レイヤーについて
http://d.hatena.ne.jp/baroqueworksdev/20120630/1341084903

今回は新規追加レイヤー、NAVIGATION_BAR_PANEL_LAYERの確認です。


NAVIGATION_BAR_PANEL_LAYERの使用箇所

PhoneWindowManager#windowTypeToLayerLw()にてwindowTypeからLayerの値に変換するので、grepはTYPE_NAVIGATION_BAR_PANELで。
引っかかるのは以下の3ファイルでした。

SystemUIが使用していますね。

  • com\android\systemui\statusbar\phone\PhoneStatusBar.java
  • com\android\systemui\statusbar\tablet\TabletStatusBar.java
  • com\android\systemui\statusbar\tablet\TabletTicker.java

PhoneStatusBar.javaに焦点をあてて調査します。
TYPE_NAVIGATION_BAR_PANELを使用しているのは以下のモジュール。
BaseStatusBar#updateSearchPanel
↓
PhoneStatusBar#getSearchLayoutParams() ←★ここでWindowLayoutのパラメータとして使用

BaseStatusBar#updateSearchPanelのモジュール内で、
mSearchPanelViewというViewをWindowにaddViewしていました。

    protected void updateSearchPanel() {
        // Search Panel
        boolean visible = false;
        if (mSearchPanelView != null) {
            visible = mSearchPanelView.isShowing();
            WindowManagerImpl.getDefault().removeView(mSearchPanelView);
        }

        // Provide SearchPanel with a temporary parent to allow layout params to work.
        LinearLayout tmpRoot = new LinearLayout(mContext);
        mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_search_panel, tmpRoot, false);
        mSearchPanelView.setOnTouchListener(
                 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
        mSearchPanelView.setVisibility(View.GONE);

        WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());

        WindowManagerImpl.getDefault().addView(mSearchPanelView, lp);
        mSearchPanelView.setBar(this);
        if (visible) {
            mSearchPanelView.show(true, false);
        }
    }



SearchPanelViewってなんだ?


mSearchPanelViewの表示タイミングは以下のソースを参照。
com\android\systemui\statusbar\phone\PhoneStatusBar.java


    @Override
    public void showSearchPanel() {
        super.showSearchPanel();
        WindowManager.LayoutParams lp =
            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp);
    }


    private Runnable mShowSearchPanel = new Runnable() {
        public void run() {
            showSearchPanel();
        }
    };
    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            switch(event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!shouldDisableNavbarGestures()) {
                    mHandler.removeCallbacks(mShowSearchPanel);
                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
                }
            break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mHandler.removeCallbacks(mShowSearchPanel);
            break;
        }
        return false;
        }
    };

    private void prepareNavigationBarView() {
        mNavigationBarView.reorient();

        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
        updateSearchPanel();
    }



ようはHomeキーを一定時間押し続けると、SearchPanelViewが表示されます。
Viewの処理はこちらのソースを参照

\sources\android-16\com\android\systemui\SearchPanelView.java




JBの表示レイヤーについて


Android SDKのAPI 16が公開されました。
一緒にソースもDLできるので、毎度おなじみの表示レイヤーのチェックを行いました。

確認OSバージョン

Android OS 4.1 JB

表示レイヤー

OSのメジャーアップデートなので、表示レイヤーの変更点があるかどうか確認しました。
ソース
SDK\sources\android-16\com\android\internal\policy\impl
- PhoneWindowManager.java
(★ = JBにて新規追加されたレイヤー)
レイヤー名用途
2WALLPAPER_LAYER壁紙
2APPLICATION_LAYER一般アプリケーション
3PHONE_LAYER着信などの電話用
4SEARCH_BAR_LAYER検索バー
5SYSTEM_DIALOG_LAYER電源OFFダイアログなど
6TOAST_LAYERトースト表示
7PRIORITY_PHONE_LAYERSIMエラー表示など
8SYSTEM_ALERT_LAYERANRやLowバッテリー通知
9INPUT_METHOD_LAYER文字入力UI
10INPUT_METHOD_DIALOG_LAYER文字入力UIのダイアログ
11KEYGUARD_LAYERキーガード表示
12KEYGUARD_DIALOG_LAYERシャットダウン中やSIMロック表示、キーガード表示中の電源OFFダイアログなど
13SCREENSAVER_LAYERスクリーンセーバー★
14STATUS_BAR_SUB_PANEL_LAYERPhone用expandしたパネル
15STATUS_BAR_LAYERStatusBar
16STATUS_BAR_PANEL_LAYERStatusBarをexpandしたパネル
17VOLUME_OVERLAY_LAYERボリューム変更
18SYSTEM_OVERLAY_LAYERキーガードより上位に表示するシステムオーバーレイ
19NAVIGATION_BAR_LAYERナビゲーションBar
20NAVIGATION_BAR_PANEL_LAYERナビゲーションBarの上に表示するために必要なパネル(searchなど)★
21SYSTEM_ERROR_LAYERシステムエラー通知
22DRAG_LAYERドラッグ&ドロップ操作用
23SECURE_SYSTEM_OVERLAY_LAYER
24BOOT_PROGRESS_LAYERBoot中のDialog表示
25POINTER_LAYERマウスポインター
26HIDDEN_NAV_CONSUMER_LAYERFakeWindow用


大きな変更点は
  1. SCREENSAVER_LAYERの追加
  2. NAVIGATION_BAR_PANEL_LAYERの追加

NAVIGATION_BAR_PANEL_LAYERのの使用箇所はべっと調査します。

2012年3月31日土曜日

AOSPのTag間のdiffとlogを取得する



AOSPにandroid-4.0.4_r1.1が現れました。

android-4.0.3_r1とのdiffとlogの取得を行いました。


diffの取得

ファイル差分すべてを取得
$ repo forall -p -c git diff android-4.0.3_r1..android-4.0.4_r1.1 > diff_android-4.0.3_r1_android-4.0.4_r1.1.txt


変更されたファイルの一覧と変更行数を取得
$ repo forall -p -c git diff --stat android-4.0.3_r1..android-4.0.4_r1.1 > diff_android-4.0.3_r1_android-4.0.4_r1.1_stat.txt


ディレクトリ毎の変更量(%)を取得
$ repo forall -p -c git diff --dirstat android-4.0.3_r1..android-4.0.4_r1.1 > diff_android-4.0.3_r1_android-4.0.4_r1.1_dirstat.txt


logの取得


TAG間のlog(コミットタイトル)を取得
$ repo forall -p -c git log --oneline --no-merges android-4.0.3_r1..android-4.0.4_r1.1 > log_android-4.0.3_r1_android-4.0.4_r1.1.txt


どうぞ


diffとlogの実行結果を以下にUploadしました。よろしければ、どうぞ。

https://docs.google.com/leaf?id=0BwdCdBWuE_7cSHE0Mkpodm5TN3VMZjFmOTJ2QUU0UQ

2011年6月16日木曜日

Fragmentについて



(作成中)


Fragmentのサンプル

難しい話は置いといて結果的にどうなるのか、
Android 3.1のエミュレータにインストールされている「ApiDemo」でUIを確認。
f:id:baroqueworksdev:20110618043421p:image:w640


サンプルプログラムはこちら。
http://developer.android.com/intl/ja/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html


ActivityにFragmentを追加しているのは以下のコード。

// Check what fragment is currently shown, replace if needed.
DetailsFragment details = (DetailsFragment)
                getFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
    // Make new fragment to show this selection.
    details = DetailsFragment.newInstance(index);

    // Execute a transaction, replacing any existing fragment
    // with this one inside the frame.
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.details, details);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.commit();
}


以下の箇所で、「containerViewをR.id.detailsとして、フラグメントdetailsに置き換えます」と指定。
    ft.replace(R.id.details, details);



ft.commit()後、DetailsFragment.onCreateView()がコールされて、フラグメントのViewをActivityに渡している。


Hierarchy ViewでView階層を確認。
ピンク枠がFragmentのcontainerView(親View)
黄色枠がFragmentが持っているView

f:id:baroqueworksdev:20110618044501p:image:w640


GingerBread以前のバージョンでもLayout.xmlを駆使すれば、
見た目は真似できますね。


Fragmentのライフサイクル

FragmentのライフサイクルはActivityのライフサイクルに結び付けられています。

Resume状態になるまでに呼び出されるライフサイクルメソッド

method内容
onAttach()Activityに関連付けれた際に1度だけコールされる
onCreate()fragmentの初期化生成のためによばれる
onCreateView()fragmentのView階層を生成し戻り値として返す
onActivityCreated()Activity.onCreaateが完了したことをframentに通知
onStart()ActivityのonStart()と同等
onResume()ActivityのonResume()と同等


Destroy状態になるまでに呼び出されるライフサイクルメソッド

method内容
onPause()fragment操作が一時停止状態に陥るとき、ActivityのonPause()と同等
onStop()fragment操作が一時停止状態となりユーザーから見えない状態、ActivityのonStop()と同等
onDestroyView()fragmentのView階層をCleanUp可能な状態
onDestroy()fragmentをCleanUp可能な状態
onDetach()Activityとの関連付けが解除された状態


上記のfragmentのライフサイクルとActivityのライフサイクルとの関係を、図に記す。
f:id:baroqueworksdev:20110617020005p:image

(developerサイトから拝借)

(作成中)

参考


http://developer.android.com/intl/ja/reference/android/app/Fragment.html

http://developer.android.com/intl/ja/guide/topics/fundamentals/fragments.html

2011年5月14日土曜日

アプリケーションが android:largeHeap=”true”指定されているかどうか確認する


ApplicationInfoの情報


AndroidManifest.xmlに情報を記述するってことは、その情報はApplicationInfoに格納されることは容易に想像がつくと思います。

デベロッパーサイトのApplicationInfoに以下の記載がありました。

public static final int FLAG_LARGE_HEAP

Since: API Level 11

Value for flags: true when the application has requested a large heap for its processes. Corresponds to android:largeHeap.

Constant Value: 1048576 (0x00100000)


ApplicationInfoのpublicメンバー、「flags」のフラグをチェックすれば判断できるってことですね。



MOTOROLA XOOMのインストールアプリを確認


以下のようにインストールアプリの情報を取得して、FLAG_LARGE_HEAPのフラグが立っているアプリのみログ出力してみました。

PackageManager pm = getPackageManager();
if(pm != null){
    List<PackageInfo> package_list = pm.getInstalledPackages(
                    PackageManager.GET_UNINSTALLED_PACKAGES );
    for (PackageInfo info : package_list){
        if(info.applicationInfo != null){
            if((info.applicationInfo.flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0){
                Log.e("FLAG_LARGE_HEAP", info.packageName);
            }
        }
    }
}


結果は以下のアプリでした。

05-15 04:25:42.530: ERROR/FLAG_LARGE_HEAP(7592): com.android.launcher

05-15 04:25:42.530: ERROR/FLAG_LARGE_HEAP(7592): com.google.android.youtube
なんとなく納得できそうなアプリ達でした。




2011年5月13日金曜日

android:largeHeap=”true”で使用可能なHeapサイズ


アプリケーションで使用するHeapサイズを拡大する

APIレベル11(ハニカム)からアプリケーションで使用するHeapサイズを拡大できるらしい。

デベロッパーサイトに以下のような記載があります。

Return the approximate per-application memory class of the current device when an application is running with a large heap. This is the space available for memory-intensive applications; most applications should not need this amount of memory, and should instead stay with the getMemoryClass() limit. The returned value is in megabytes. This may be the same size as getMemoryClass() on memory constrained devices, or it may be significantly larger on devices with a large amount of available RAM.

The is the size of the application's Dalvik heap if it has specified android:largeHeap="true" in its manifest.


AndroidManifest.xmlに以下の記述をすると使用Heapサイズの拡大可能。
android:largeHeap="true"


ActivityManager.getLargeMemoryClass()でサイズを確認。
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
int largeMemory = am.getLargeMemoryClass();  


ためしに、Motorola Xoom(MZ600) Android OS 3.0で確認したところ、"256MB"でした。


(追記)
Android OS 3.1にUpdate後確認。
Motorola Xoom(MZ600) Android OS 3.1で確認したところ、"256MB"でした。