2012年8月30日木曜日

Android 4.1 JellyBeanのロック画面表示中のActivityLifeCycleについて

Androidのバージョンによって、動作に差異があったのでメモ。
おそらく、Google側も省電力を気にしているのだと思います。




Android 2.3.3 GB(API Level 10) のActivityLifeCycle

電源キー押下によりScreen OFF
onPause()

電源キー押下によりScreen ON → ロック画面表示中
onResume()

ロック画面解除
※ActivityのActivityLifeCycleメソッドはコールされず

Android 4.0.X ICS(API Level 14,15) のActivityLifeCycle

電源キー押下によりScreen OFF
onPause()
 ↓
onStop()

電源キー押下によりScreen ON → ロック画面表示中
onRestart()
 ↓
onStart()
 ↓
onResume()

ロック画面解除
※ActivityのActivityLifeCycleメソッドはコールされず

Android 4.1 JellyBean(API Level 16) のActivityLifeCycle

電源キー押下によりScreen OFF
onPause()
 ↓
onStop()

電源キー押下によりScreen ON → ロック画面表示中
※ActivityのActivityLifeCycleメソッドはコールされず
ロック画面解除
onRestart()
 ↓
onStart()
 ↓
onResume()

2012年8月28日火曜日

自アプリケーションがTOPかどうか確認する方法(4)

ActivityのTaskListの順番を見るよりは、アプリケーションプロセスを見る方が正確だと思います。
ActivityManager#getRunningAppProcesses()で取得可能です。


処理:

  1. ActivityManagerから実行中プロセス情報一覧を取得
  2. 自アプリケーションが存在するかチェック
  3. プロセス情報(importance )の状態を確認する


ソースはこんな感じ。


    private void checkRunningAppProcess(){
        Log.e("","checkRunningAppProcess");

        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        List<runningappprocessinfo> processInfoList = am.getRunningAppProcesses();
        for( RunningAppProcessInfo info : processInfoList){
            if(info.processName.equals(getPackageName())){
                if( info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
                    // app is FOREGROUND
                    Log.e("","app is FOREGROUND");
                }
            }
        }
    }

2012年8月27日月曜日

自アプリケーションがTOPかどうか確認する方法(3)

ActivityManager#getRecentTasks()の情報って?

getRunningTasks()と同様にgetRecentTasks()を見てみます。

ActivityManagerService#getRecentTasks()の処理をチェック。

    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
            int flags) {
                     :
                     :
            final int N = mRecentTasks.size();
            ArrayList<ActivityManager.RecentTaskInfo> res
                    = new ArrayList<ActivityManager.RecentTaskInfo>(
                            maxNum < N ? maxNum : N);
            for (int i=0; i<N && maxNum > 0; i++) {
                     :
                     :
       

mRecentTasksのTaskRecord情報からListを生成します。

TaskRecordをmRecentTasksに追加するタイミング

ActivityManagerService#addRecentTaskLocked(TaskRecord task)でaddを行っており、
このモジュールをコールするのはActivityStackのこちら。

final boolean realStartActivityLocked()
final boolean resumeTopActivityLocked()

Activity起動、またはresumeを行ってActivityがTopになったタイミングでtaskをaddします。


TaskRecordをmRecentTasksから削除するタイミング

削除するのは以下のタイミング
addRecentTaskLocked()のadd処理でMAX(=20)を超えた場合、古いものを削除 RecentsPanelView(=履歴表示)からSwipで削除した場合、ActivityManagerService#removeTask()をコール


こちらはRunningTasksとくらべ、ユーザー操作に近いトリガーとなります。


getRecentTasks()は『起動アプリ履歴に残っているTask一覧』であって、
現在、起動中と意味でTask情報を取得したい場合はgetRunningTasks()を使用する方が好ましいと思います。

自アプリケーションがTOPかどうか確認する方法(2)

ActivityManager#getRunningTasks()の情報って?

ActivityManager#getRunningTasks()の情報が何かソースを見てみます。

モジュールを辿っていくと、ActivityManagerService#getTasks()をコールします。

ActivityManager#getRunningTasks()
- ActivityManagerService#getTasks()


ActivityManagerService#getTasks()は以下のような処理。


    public List getTasks(int maxNum, int flags,
                         IThumbnailReceiver receiver) {
        ArrayList list = new ArrayList();
                     :
                     :
            int pos = mMainStack.mHistory.size()-1;
            ActivityRecord next =
                pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
            ActivityRecord top = null;
            TaskRecord curTask = null;
            int numActivities = 0;
            int numRunning = 0;
            while (pos >= 0 && maxNum > 0) {
                     :
                     :
            }
    }
mMainStack.mHistoryのActivityRecord情報からListを生成します。



ActivityRecordをmMainStack.mHistoryに追加するタイミング

mMainStack.mHistoryのadd()をコールするソースはActivityStack.java。
もともとActivityManagerServiceに記述されていたソースのうち、Stack情報に関わる操作などがActivityStack.javaに分離されました。

で、mMainStack.mHistoryのadd()をコールするのは以下のモジュール。


final boolean switchUser()
private final void startActivityLocked()
private final ActivityRecord resetTaskIfNeededLocked()
private final ActivityRecord moveActivityToFrontLocked()
final void moveTaskToFrontLocked()
final boolean moveTaskToBackLocked()
Activityをstartしたり、起動済みのActivityをFrontに移行する際に呼ばれていました。



ActivityRecordをmMainStack.mHistoryから削除するタイミング

リストから明示的に削除を行っているのは以下のモジュールです。

removeActivityFromHistoryLocked
このモジュールはActivityがDestroyされるときにコールされます。

final boolean destroyActivityLocked
つまり、アプリ内のすべてのActivityがDestroyされるとmMainStack.mHistory内から抹殺され、 (onStop状態でも)Activityが一つでも残っていればmMainStack.mHistoryに存在することとなります。

自アプリケーションがTOPかどうか確認する方法

以前の記事でApplication単位でresume/pauseのハンドリングが不可能とわかりました。
 http://baroqueworksdevjp.blogspot.jp/2012/08/applicationonterminate.html


では、他に何かいい方法がないか調べてみました。


ActivityManagerの公開APIを使う

以下のような、Task情報を取得できるAPIをActivityのonStop()でコールしてみました。
    private void checkRecentTasks(){
        Log.e("","checkRecentTasks");

        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        List<RecentTaskInfo> taskInfo = am.getRecentTasks(10, ActivityManager.RECENT_WITH_EXCLUDED);
        for( RecentTaskInfo info : taskInfo){
            Log.e("", "" + info.baseIntent.getComponent().getPackageName());
        }
    }
    
    private void checkRunningTasks(){
        Log.e("","checkRunningTasks");
  
        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> taskInfo = am.getRunningTasks(10);
        for( RunningTaskInfo info : taskInfo){
            Log.e("","" + info.topActivity.getPackageName());
        }
    }


Homeキー押下によりSampleアプリをBackGroudにさせたlogです。

checkRecentTasks
08-27 21:12:13.314: E/(7511): com.android.launcher
08-27 21:12:13.314: E/(7511): jp.baroqueworksdev.sampleapp
08-27 21:12:13.314: E/(7511): com.android.packageinstaller
08-27 21:12:13.314: E/(7511): jp.r246.twicca
08-27 21:12:13.314: E/(7511): com.alphonso.pulse
08-27 21:12:13.314: E/(7511): com.chnavi.android2ch
08-27 21:12:13.314: E/(7511): jp.mixi
08-27 21:12:13.314: E/(7511): com.facebook.katana
08-27 21:12:13.314: E/(7511): com.android.chrome

checkRunningTasks
08-27 21:12:13.321: E/(7511): com.android.launcher
08-27 21:12:13.321: E/(7511): jp.baroqueworksdev.sampleapp
08-27 21:12:13.321: E/(7511): jp.r246.twicca
08-27 21:12:13.321: E/(7511): com.alphonso.pulse
08-27 21:12:13.321: E/(7511): com.chnavi.android2ch
08-27 21:12:13.321: E/(7511): jp.mixi
08-27 21:12:13.321: E/(7511): com.facebook.katana
08-27 21:12:13.321: E/(7511): com.android.chrome


直近に起動したアプリ順に並んでいました。
Android Developer Siteにも以下のように書いています。
 with the most recent being first and older ones after in order.


上記のようなTaskリストを取得して、自アプリがTOPかどうかチェックするのも一つの手段かと思います。.
ただし、ロック画面が表示されている場合、TOPにいても「画面上表示されておらずユーザーには見えない」ことを忘れずに。

2012年8月25日土曜日

DTI ServersMan SIM 3G 100 が届きました

500円SIMのニュースで話題になった、DTIの「ServersMan SIM 3G 100」を購入しました。




申し込みから到着まで


到着までの時系列
 8/20(月) DTI公式ページから申し込み http://dream.jp/
 8/22) 発送 (※ヤマト運輸 東京都台東区からでした)
 8/25(土) 到着


Galaxy Nexus(※海外版)に挿してみた

Galaxy NexusにSIMを挿入してみました。 DTIはdocomoさんのMVNOなので、APN設定にmopera UI / SPモードが標準で表示されていました。


DTIのAPN設定を完了し通信状態となっても、アンテナピクトが更新されませんでした。
これは端末側のソフトウェアの問題なので気にしない、気にしない。



テザリング

Galaxy Nexus(※海外版)でテザリングができるかどうか試してみました。




問題なくテザリング動作可能でした。
Docomo版端末は制限事項があるみたいなのでご自身で調べてみてください。


使ってみた感想

Twitterやメールなどテキストベースなら問題ない通信速度です。
ただ、Facebookやmixiなど画像を多く含むアプリケーションでは、ストレスを感じてしまいます。
100kのベストエフォートなんで、こんな感じです。

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月17日金曜日

Page Actionを作成する


Browser Actionは常にアイコンが表示されるのに対し、Page Actionは特定のWebサイトのみアイコンを表示させます。

必要なことは以下の2つ。

  • tabが切り替わった通知を受けるためにListenerを登録
  • manifest.jsonにpage_actionを記述


tabが切り替わった通知を受けるためにListenerを登録


Listenerに登録するモジュールをJavaScriptファイルに記述します。

background.js
// Called when the url of a tab changes.
function checkForValidUrl(tabId, changeInfo, tab) {
console.log("checkForValidUrl");
  if (tab.url.indexOf("http://www.blogger.com/blogger") > -1) {
    // ... show the page action.
    chrome.pageAction.show(tabId);
  }
};

// Listen for any changes to the URL of any tab.
chrome.tabs.onUpdated.addListener(checkForValidUrl);


上記のようなJavaScriptを作成し、manifest.jsonの"background"に記述します。
Chromeのtab変更通知を受けるためにchrome.tabsをコールするので"permissions"も追加

  "background": { "scripts": ["background.js"] },
  "permissions": [
    "tabs","http://*/*"
  ],


manifest.jsonにpage_actionを記述


Page Actionを指定するために、browser_actionからpage_actionに変更します。
tabのListener登録を含めると、以下のようなmanifest.jsonとなります。


{
  "name": "Page Action Extension",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Page Action Extension Test.",
  "background": { "scripts": ["background.js"] },
  "permissions": [
    "tabs","http://*/*"
  ],
  "page_action":
  {
    "default_icon": "icon.png",
    "default_title": "this URL!",
    "default_popup": "popup.html"
 }
}


正常に動作すると、以下のようにアドレスバーの右端にアイコンが表示されます。


Pop Upを表示する

Browser Actionのアイコンをクリックした際、Pop Upを表示してみます。

以下のように、manifest.jsonに「"default_popup": "popup.html"」を追記します。


{
  "name": "Browser Action Extension",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Browser Action Extension Test.",
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  }
}



以下のように、PopUpが表示されます。





Pop Upの中身はmanifest.jsonで指定したhtml内に記述します。

Google Chrome Extensionsを作成してみる


Google Chrome Extensionsを作成してみようと思い、以下の公式ページを参照しながら勉強中。
http://developer.chrome.com/extensions/getstarted.html

Chrome Extensionsに最低限必要なファイルは以下の2つ。
  • manifest.json
  • icon.png


manifest.jsonにはExtensionの名前やVersionなどを記載する。
以下のようなmanifest.jsonを作成してみます。

{
  "name": "Browser Action Extension",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Browser Action Extension Test.",
  "browser_action": {
    "default_icon": "icon.png"
  }
}


chromeで読み込ませたところ、以下のようなアイコンが表示されました。


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()がよばれ無効化されます。
無効化されたアプリは、アプリ一覧に表示されなくなります。