2012年12月8日土曜日

【Xperia AX SO-01E】ナビゲーションバー(ソフトキー)カスタマイズ

画面最下部(横向き画面だと右端)のナビゲーションバー、つまりソフトキーが並んでいる部分をカスタムしようという試みです。
今回は、履歴キーを廃止(ホームキー長押しで代替)し、その位置にメニューキーを配置、左端に電源キー、右端に検索キー(長押しで音声検索)を追加し、5キーのナビバーにしてみます。
さらに欲張って、メニューキーは常に表示されるようにしてみたりもします。

前提(私の実験環境)は以下です。
・rootとってあること
・adbコマンドが使える環境であること
・javaが入っていること
・端末のビルド番号は9.0.G.0.247
・CWMを導入してあること
・母艦はWindows 7
・トラブっても自力で復旧できること(つまり自己責任)

いじるシステムファイルは、以下になります。
/system/app/SystemUI.apk
/system/app/SystemUI.odex

好みはあるでしょうが、私はapkとodexを別々にいじりたいと思います。

■A0■SystemUI.apkをいじるにあたっての必要物資
・SystemUI.apk(今回の獲物)
・apktool.jar(apkをソースファイルに展開したり戻したりするツール)
・7za.exe(圧縮率を変えてパックしなおす際に必要)
・framework-res.apk(SystemUI.apkを展開する際に必要)

apktool.jarは、「apktool」で検索してゲット。
いろいろダウンロードできますが、apktool.jarだけあればイケます。
(apktool.jarはaapt.exeを呼び出しているようですが、aapt.exeはandroidSDKに含まれています。)
7zaは「7-zip」で検索、コマンドラインバージョンをダウンロードしてゲット。
SystemUI.apkは端末の「/system/app」に入っています。
framework-res.apkは端末の「/system/framework」にあります。

■A1■PCにおける作業フォルダの準備
ここでは「c:\apkwork」とします。
この中に、「old」と「new」フォルダを新規作成します。
今回の獲物、SystemUI.apkは「old」に入れておいてください。
その他の必要物資は、作業フォルダ直下にぶっこみます。
以降はコマンドラインでの作業になりますが、「apkwork」フォルダをShift押しながら右クリックで「コマンド ウィンドウをここで開く」すると便利です。

□apkwork ┳ □old ━ SystemUI.apk
     ┣ □new(新しいSystemUI.apkが入る予定)
     ┣ □SystemUI(作業途中で生成される。ソースファイル群が入る)
     ┣ apktool.jar
     ┗ framework-res.apk

■A2■framework-res.apkをapktoolに登録
C:\apkwork>java -jar apktool.jar if framework-res.apk

これをしておかないと、SystemUI.apkを展開しようとしたときエラーが出ます。
成功すると、Windowsのユーザーフォルダ(例えばC:\Users\[ユーザー名])に「apktool」フォルダができ、その中に「framework」フォルダができ、さらにその中に「1.apk」というものが生成されます。

■A3■SystemUI.apkを展開
C:\apkwork>java -jar apktool.jar d old\SystemUI.apk SystemUI

作業フォルダの中に新しく「SystemUI」フォルダが生成され、その中にソースファイルが展開されます。

■A4■ソースファイルの書き換え等
以下、「SystemUI」フォルダを基準に説明を進めます。

【A4-1】public.xmlにID追加
「res\values\public.xml」をテキストエディタで開きます。
最下行に「</resources>」がありますが、その前の行に電源キーと検索キー用のIDを追加します。

    <public type="id" name="recent_inspect_item" id="0x7f0f009c" />
    <public type="id" name="power" id="0x7f0f009d" />
    <public type="id" name="search" id="0x7f0f009e" />
</resources>

上の行(ここでは「recent_inspect_item」)のIDがおそらく最後尾だと思われますので、そこから1増やしたものを「power」、2増やしたものを「search」のIDに割り当てています。
(ちなみにIDは16進数ですので、分からない人は調べてください。)
大丈夫とは思いますが、同じIDが使われていないか、public.xml内を検索しておいてください。

【A4-2】ids.xmlにアイテム追加
「res\values\ids.xml」をテキストエディタで開きます。
やはり最下行に「</resources>」がありますが、その前の行に電源キーと検索キーの記述を追加します。

    <item type="id" name="recent_inspect_item">false</item>
    <item type="id" name="power">false</item>
    <item type="id" name="search">false</item>
</resources>

【A4-3】strings.xmlに説明追加
「res\values\strings.xml」をテキストエディタで開きます。
最下行「</resources>」の前の行に電源キーと検索キーの説明文字列を追加します。

    <string name="power_settings_instructions">Enabling Extended Standby Mode in Power management will optimize battery lifetime</string>
    <string name="accessibility_power">Power</string>
    <string name="accessibility_search">Search</string>
</resources>

【A4-4】日本語版のstrings.xmlに説明追加
「res\values-ja\strings.xml」をテキストエディタで開きます。
最下行「</resources>」の前の行に電源キーと検索キーの説明文字列を日本語で追加します。

    <string name="power_settings_instructions">電源管理にて拡張待ち受けモードを有効にすると電池寿命が最適化できます</string>
    <string name="accessibility_power">電源</string>
    <string name="accessibility_search">検索</string>
</resources>

【A4-5】レイアウト編集
「res\layout\navigation_bar.xml」をテキストエディタで開きます。
ハッキリ言ってこの編集がメインになります。
このxmlファイルは大まかに以下のような構造になっています。

<?xml ~?>
<com.android~>
  <FrameLayout ~>
    <LinearLayout ~>
      縦画面時のナビゲーションバーのレイアウト
    </LinearLayout>
    <LinearLayout ~>
      縦画面時、特定状況下のナビゲーションバーのレイアウト
    </LinearLayout>
    <View ~ />
  </FrameLayout>
  <FrameLayout ~>
    <LinearLayout ~>
      横画面時のナビゲーションバーのレイアウト
    </LinearLayout>
    <LinearLayout ~>
      横画面時、特定状況下のナビゲーションバーのレイアウト
    </LinearLayout>
    <View ~ />
  </FrameLayout>
  <View ~ />
</com.android~>

特定状況下というのは、アルバムアプリで画像を表示したりしている時などのことで、ソフトキーの位置に点3つが表示されている、アレです。
縦画面時のレイアウトは、左から順番に並びます。
横画面時のレイアウトは、上から順番に並びます。
つまり、同じ位置でソフトキーを配置する場合、縦画面のレイアウトと横画面のレイアウトはxmlの記述が逆になりますので混乱しそうになります。

さて、よく見ると何となくどういじれば良いか分かってくると思いますが、簡単に要所を説明します。ただ、こうしてみたらこうなった、という程度ですので、間違ってる可能性はありますが…。

android:layout_widthやandroid:layout_heightの値は、「dip」という単位で記述され、SO-01Eの場合はピクセル数の半分になります(1dip=2ピクセル)。
SO-01Eの横幅は720ピクセルですので、ナビゲーションバーの横幅は合計360dipになるようにしないといけません。
android:layout_weightは、widthやheightに具体的なdipを入れる場合は通常0.0にしますが、widthやheightを「fill_parent」にすると、weightの値を比率として余ったスペースを自動的に計算して割り振ってくれます。
systemui:keyCodeは、物理キーのコードです。ホームキーは3、戻るキーは4、というふうに決まっています。これはネットで「android キーコード」等で検索すると一覧表が出てきますので、好きな物理キーの挙動をするソフトキーを追加できます。カメラキー(キーコード27)とか面白そうです。
android:srcは、ソフトキーの画像ファイルを表します。電源キーと検索キーの画像は後ほど追加します。
オリジナルのホームキーには「systemui:keyRepeat="false"」が記述されていますが、これは長押しを抑制します。今回ホームキー長押しで履歴を出したいので、これはカットします。
また、オリジナルのメニューキーには「android:visibility="invisible"」が記述されています。今回メニュー常時表示する際におそらく邪魔ですので、これもカットします。
あとオリジナルにある<View android:visibility="invisible" ~ />は、ソフトキー間の隙間になります。
通常時のソフトキーを配置したら、特定状況下のレイアウトもサイズを合わせておいた方が良いでしょう。

ということで、説明しきれてない部分もあるかと思いますが、今回navigation_bar.xmlは以下のようにします。

<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.statusbar.phone.NavigationBarView android:id="@id/nav_background" android:background="#ff000000" android:layout_width="fill_parent" android:layout_height="fill_parent" android:directionality="none"
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui">
    <FrameLayout android:id="@id/rot0" android:layout_width="fill_parent" android:layout_height="fill_parent" android:directionality="none">
        <LinearLayout android:orientation="horizontal" android:id="@id/nav_buttons" android:clipChildren="false" android:clipToPadding="false" android:layout_width="fill_parent" android:layout_height="fill_parent" android:animateLayoutChanges="true" android:directionality="none">
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/power" android:layout_width="60.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_power" android:layout_weight="0.0" android:contentDescription="@string/accessibility_power" systemui:keyCode="26" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/back" android:layout_width="80.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_back" android:layout_weight="0.0" android:contentDescription="@string/accessibility_back" systemui:keyCode="4" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/home" android:layout_width="80.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_home" android:layout_weight="0.0" android:contentDescription="@string/accessibility_home" systemui:keyCode="3" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/recent_apps" android:visibility="invisible" android:layout_width="0.0dip" android:layout_height="fill_parent" android:layout_weight="0.0" android:contentDescription="@string/accessibility_recent" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/menu" android:layout_width="80.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_menu" android:layout_weight="0.0" android:contentDescription="@string/accessibility_menu" systemui:keyCode="82" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/search" android:layout_width="60.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_search" android:layout_weight="0.0" android:contentDescription="@string/accessibility_search" systemui:keyCode="84" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
        </LinearLayout>
        <LinearLayout android:orientation="horizontal" android:id="@id/lights_out" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="fill_parent" android:directionality="none">
            <ImageView android:layout_width="80.0dip" android:layout_height="fill_parent" android:layout_marginLeft="60.0dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" android:layout_weight="0.0" />
            <ImageView android:layout_width="80.0dip" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_lights_out_dot_large" android:scaleType="center" android:layout_weight="0.0" />
            <ImageView android:layout_width="80.0dip" android:layout_height="fill_parent" android:layout_marginRight="60.0dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" android:layout_weight="0.0" />
        </LinearLayout>
        <View android:layout_gravity="top" android:id="@id/deadzone" android:clickable="true" android:layout_width="fill_parent" android:layout_height="@dimen/navigation_bar_deadzone_size" />
    </FrameLayout>
    <FrameLayout android:id="@id/rot90" android:paddingTop="0.0dip" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="fill_parent" android:directionality="none">
        <LinearLayout android:orientation="vertical" android:id="@id/nav_buttons" android:clipChildren="false" android:clipToPadding="false" android:layout_width="fill_parent" android:layout_height="fill_parent" android:animateLayoutChanges="true" android:directionality="none">
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/search" android:layout_width="fill_parent" android:layout_height="60.0dip" android:src="@drawable/ic_sysbar_search_land" android:layout_weight="0.0" android:contentDescription="@string/accessibility_search" systemui:keyCode="84" systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/menu" android:layout_width="fill_parent" android:layout_height="80.0dip" android:src="@drawable/ic_sysbar_menu_land" android:layout_weight="0.0" android:contentDescription="@string/accessibility_menu" systemui:keyCode="82" systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/recent_apps" android:visibility="invisible" android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="0.0" android:contentDescription="@string/accessibility_recent" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/home" android:layout_width="fill_parent" android:layout_height="80.0dip" android:src="@drawable/ic_sysbar_home_land" android:layout_weight="0.0" android:contentDescription="@string/accessibility_home" systemui:keyCode="3" systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/back" android:layout_width="fill_parent" android:layout_height="80.0dip" android:src="@drawable/ic_sysbar_back_land" android:layout_weight="0.0" android:contentDescription="@string/accessibility_back" systemui:keyCode="4" systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@id/power" android:layout_width="fill_parent" android:layout_height="60.0dip" android:src="@drawable/ic_sysbar_power_land" android:layout_weight="0.0" android:contentDescription="@string/accessibility_power" systemui:keyCode="26" systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
        </LinearLayout>
        <LinearLayout android:orientation="vertical" android:id="@id/lights_out" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="fill_parent" android:directionality="none">
            <ImageView android:layout_width="fill_parent" android:layout_height="80.0dip" android:layout_marginTop="60.0dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" android:layout_weight="0.0" />
            <ImageView android:layout_width="fill_parent" android:layout_height="80.0dip" android:src="@drawable/ic_sysbar_lights_out_dot_large" android:scaleType="center" android:layout_weight="0.0" />
            <ImageView android:layout_width="fill_parent" android:layout_height="80.0dip" android:layout_marginBottom="60.0dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" android:layout_weight="0.0" />
        </LinearLayout>
        <View android:layout_gravity="left" android:id="@id/deadzone" android:clickable="true" android:layout_width="@dimen/navigation_bar_deadzone_size" android:layout_height="fill_parent" />
    </FrameLayout>
    <View android:id="@id/rot270" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="fill_parent" />
</com.android.systemui.statusbar.phone.NavigationBarView>

かなり見づらいと思いますがご容赦を…。
このままテキストエディタ等にコピペしてゆっくりご覧になられた方がよろしいかと思われます。

なお、履歴キーは廃止にするはずなのに、幅ゼロで記述を残してあります。これはなぜかというと、履歴キーはあちこちから参照されているようで、完全に無くしてしまうと矛盾が生じてエラーが出てしまうので、それを回避するためです。

ソフトキーの配置を入れ替えたい場合はxml内の記述順を入れ替えるだけです。
存分にレイアウトしたら、上書き保存してください。

【A4-6】画像ファイルを追加
今回新しく2つのソフトキーを追加しますが、画像ファイルは以下の場所に追加します。
\res\drawable-xhdpi
画像の種類は「png」です。画像ファイル名はnavigation_bar.xml内のandroid:srcの記述に合わせてください。
(android:src="@drawable/ic_sysbar_power"であれば、画像ファイル名は「ic_sysbar_power.png」にする)
自分で画像を作るのが面倒なら、他のファイルから適当な画像を抜き出しましょう。
例えば電源キーは/system/framework/framework-res.apk、検索キーは/system/app/GoogleQuickSearchBox.apkの中にそれっぽいやつが入っています。
(apkは拡張子をzipに変えれば解凍して中の画像を取り出せます。解凍した中のres\drawable-xhdpiに入っていると思います。)

今回は以下4つの画像を追加します。
・ic_sysbar_power.png
・ic_sysbar_power_land.png
・ic_sysbar_search.png
・ic_sysbar_search_land.png

以上でapkのソースの編集は終わりです。

■A5■ソースを再びSystemUI.apkにパック
C:\apkwork>java -jar apktool.jar b SystemUI new\SystemUI.apk

「SystemUI」フォルダ内のソースファイル群がまとめられ、newフォルダに新たなSystemUI.apkが生成されます。

■A6■SystemUI.apkを圧縮率を変えてパックし直し
手順A5で出来たSystemUI.apkは、なぜかそのままでは使用できません。
必要以上に圧縮されているらしいです。
ということで、以下の処理をしてください。

C:\apkwork>7za x -o"C:\apkwork\tmp" "C:\apkwork\old\SystemUI.apk" > nul
C:\apkwork>rmdir /s /q tmp\res
C:\apkwork>7za a -tzip -mx0 "C:\apkwork\new\SystemUI.apk" "C:\apkwork\tmp\*"
C:\apkwork>7za a -tzip -mx0 "C:\apkwork\new\SystemUI.apk" "C:\apkwork\SystemUI\build\apk\resources.arsc"
C:\apkwork>rmdir /s /q tmp

これ、もし作業フォルダが深いところにあると、フルパスを入れるのが面倒ですね(フルパスでないと7zaが上手くいかない)。
これはバッチファイルにしておくと、使い回しが出来て便利です。

私は以下のようなバッチファイルを作りました。

@if "%1" == "" (goto end)

7za x -o"%~dp0tmp" "%~dp0old\%1.apk" > nul
rmdir /s /q tmp\res
7za a -tzip -mx0 "%~dp0new\%1.apk" "%~dp0tmp\*"
7za a -tzip -mx0 "%~dp0new\%1.apk" "%~dp0%1\build\apk\resources.arsc"
@if exist "%~dp0%1\build\apk\classes.dex" (
7za a -tzip -mx0 "%~dp0new\%1.apk" "%~dp0%1\build\apk\classes.dex"
)
rmdir /s /q tmp
:end

これを「apkrepack.bat」という名前で保存し、以下のように使います。

C:\apkwork>apkrepack.bat SystemUI

これで今度こそ、実際に機能する改変済みのSystemUI.apkがnewフォルダに出来ました。

■A7■手順Bに続く…
出来た新しいSystemUI.apkですが、手順Bで使用します。

# なお、このapkだけでも、差し替えたらなんと一応動きます。
# ただし、追加したソフトキーはロック画面でも表示されっぱなし、メニューキーは常時表示になりません。
# そのあたりの処理はodexの方でやっているわけですな。
# 電源キーの追加だけしたい、とかであれば、apkの編集だけで十分です。
# パーミッションを0644にして、/system/appに置くだけです。


■B0■SystemUI.odexをいじるにあたっての必要物資
・SystemUI.apk(先ほど手順Aでいじったもの)
・SystemUI.odex(今回の獲物)
・baksmali-1.4.1.jar(odexをソースファイルに展開するツール)
・smali-1.4.1.jar(ソースファイルをdexにするツール)
・7za.exe(dexをapkに埋め込む際に必要)
・dexopt-wrapper(dex入りのapk→odexを生成するツール)
・android.policy.odex(baksmali時に必要)
・apache-xml.odex(baksmali時に必要)
・bouncycastle.odex(baksmali時に必要)
・core.odex(baksmali時に必要)
・core-junit.odex(baksmali時に必要)
・ext.odex(baksmali時に必要)
・filterfw.odex(baksmali時に必要)
・framework.odex(baksmali時に必要)
・framework_ext.odex(baksmali時に必要)
・services.odex(baksmali時に必要)

baksmaliとsmaliは、「smali」で検索してゲット。
7zaは「7-zip」で検索、コマンドラインバージョンをダウンロードしてゲット。
dexopt-wrapperは http://forum.xda-developers.com/showpost.php?p=3864655&postcount=36 からゲット。
SystemUI.apkは先ほど手順Aで生成したものです。
その他odexは端末からぶっこ抜きましょう。
SystemUI.odexは端末の「/system/app」に入っています。
その他odex類は全部端末の「/system/framework」に入っています。
やたらodexが多いですが、10個より多くなることは無さそうです。

■B1■PCにおける作業フォルダの準備
ここでは「c:\odexwork」とします。
この中に、「old」と「new」フォルダを新規作成します(すでにある場合はそのまま使います)。
必要物資のうち、SystemUI.apkとSystemUI.odexは「old」に入れておいてください。
その他の必要物資は、作業フォルダ直下に全部ぶっこみます(すでにある物に関してはそのままでいいです)。
以降はコマンドラインでの作業になりますが、「odexwork」フォルダをShift押しながら右クリックで「コマンド ウィンドウをここで開く」すると便利です。

□odexwork ┳ □old ┳ SystemUI.apk
      ┃    ┗ SystemUI.odex
      ┣ □new(deodex_SystemUI.apkが入る予定)
      ┣ □SystemUI(作業途中で生成される。ソースファイル群が入る)
      ┣ baksmali-1.4.1.jar
      ┣ smali-1.4.1.jar
      ┣ 7za.exe
      ┣ dexopt-wrapper
      ┣ android.policy.odex
      ┣ apache-xml.odex
      ┣ bouncycastle.odex
      ┣ core.odex
      ┣ core-junit.odex
      ┣ ext.odex
      ┣ filterfw.odex
      ┣ framework.odex
      ┣ framework_ext.odex
      ┗ services.odex

■B2■baksmaliする
C:\odexwork>java -Xmx512m -jar baksmali-1.4.1.jar --api-level 15 -x old\SystemUI.odex -o SystemUI

このとき、作業フォルダに必要なodexが無いとエラーが出ますが、エラーの中で何が必要か教えてくれます。
成功すると、作業フォルダ内に「SystemUI」フォルダができ、その中にソースファイルがたくさん入っています。

■B3■ソースファイルの書き換え等
「SystemUI」フォルダ内、以下のファイルをテキストエディタで書き換えます。
com\android\systemui\statusbar\phone\NavigationBarView.smali

「.method public getRecentsButton()Landroid/view/View;」を検索。(826行目あたり)
そこから14行くらい下に「.end method」があります。
その下に空行を挟み、次のように追加します。

.method public getRecentsButton()Landroid/view/View;
    .registers 3

    .prologue
    .line 101
    iget-object v0, p0, Lcom/android/systemui/statusbar/phone/NavigationBarView;->mCurrentView:Landroid/view/View;

    const v1, 0x7f0f002c

    invoke-virtual {v0, v1}, Landroid/view/View;->findViewById(I)Landroid/view/View;

    move-result-object v0

    return-object v0
.end method

.method public getSearchButton()Landroid/view/View;  #この行から追加
    .registers 3

    .prologue
    .line 101
    iget-object v0, p0, Lcom/android/systemui/statusbar/phone/NavigationBarView;->mCurrentView:Landroid/view/View;

    const v1, 0x7f0f009e

    invoke-virtual {v0, v1}, Landroid/view/View;->findViewById(I)Landroid/view/View;

    move-result-object v0

    return-object v0
.end method  #この行まで追加

.method public onFinishInflate()V

上のをそのままコピーし、メソッド名とconst値の2か所を変えるだけですね。
これにより、システムが検索キー(の表示・非表示)を扱えるようになります。
「.line 101」が2つになっちゃいますが気にしない。
お気付きかとは存じますが、「0x7f0f009e」はapkいじりの時に追加した検索キーのIDです。

次に、「.method public setDisabledFlags(IZ)V」を検索。(1077行目あたり)
その直下の「.registers 10」を「.registers 12」に書き換えます。

.method public setDisabledFlags(IZ)V
    .registers 10
↓↓↓
.method public setDisabledFlags(IZ)V
    .registers 12  #変更

「.registers 10」じゃなくて「.locals 7」とかになっているかもしれませんが、とにかく数字を2つ増やします。

その70行くらいあと(1148行目あたり)に、「invoke-virtual うんたらかんたら->getHomeButton()Landroid/view/View;」があり、その下に「move-result-object v6」がありますが、その下に空行を挟んで以下のような感じで追記します。

    .line 164
    invoke-virtual {p0}, Lcom/android/systemui/statusbar/phone/NavigationBarView;->getHomeButton()Landroid/view/View;

    move-result-object v6

    invoke-virtual {p0}, Lcom/android/systemui/statusbar/phone/NavigationBarView;->getSearchButton()Landroid/view/View;  #追加

    move-result-object v7  #追加

    invoke-virtual {p0}, Lcom/android/systemui/statusbar/phone/NavigationBarView;->getMenuButton()Landroid/view/View;  #追加

    move-result-object v8  #追加

    if-eqz v1, :cond_43

さらにその数行下(1165行目あたり)に「invoke-virtual {v6, v4}, Landroid/view/View;->setVisibility(I)V」がありますが、その下にも記述を追加します。

    invoke-virtual {v6, v4}, Landroid/view/View;->setVisibility(I)V

    invoke-virtual {v7, v4}, Landroid/view/View;->setVisibility(I)V  #追加

    invoke-virtual {v8, v4}, Landroid/view/View;->setVisibility(I)V  #追加

    .line 165

これらの追加で、検索キーとメニューキーがロック画面等で出てこなくなります。

とどめに、「.method public setMenuVisibility(ZZ)V」を検索。(1498行目あたり)
その33行くらい下(1531行目あたり)にある「invoke-virtual うんたらかんたら->setVisibility(I)V」の行をコメントアウト(行頭に「#」を付ける)します。

    :goto_12
    invoke-virtual {v1, v0}, Landroid/view/View;->setVisibility(I)V
↓↓↓
    :goto_12
#    invoke-virtual {v1, v0}, Landroid/view/View;->setVisibility(I)V

ここは、メニューキーが必要無い時に非表示にする処理ですが、これをコメントアウトすることによって、常にメニューキーが表示される状態になります。

NavigationBarView.smaliの編集がすんだら上書き保存してください。

■B4■smaliする
C:\odexwork>java -Xmx512m -jar smali-1.4.1.jar --api-level 15 -o classes.dex SystemUI

classes.dexが生成されます。

■B5■classes.dexをSystemUI.apkに埋め込み
C:\odexwork>copy /y old\SystemUI.apk new\deodex_SystemUI.apk
C:\odexwork>7za a -tzip -mx0 new\deodex_SystemUI.apk classes.dex
C:\odexwork>del classes.dex

オリジナルのapkと区別をつけるため、「deodex_SystemUI.apk」という名前でapkをコピーしておき、その中にclasses.dexを埋め込みます。

■C1■端末をCWMモードにして待機
手順Aと手順Bでapkとodex(のもと)が準備できました。
以降は端末をCWMモードにして作業をすすめます。
理由は安全のためと、転送したファイルの所有者が最初からrootになってて気分が良いから。

端末の電源を入れ、SONYロゴが出てから15秒くらいして青LEDが点くのですぐさま音量DOWNボタンを押しっぱなしにし、LEDが緑に変わったらボタンを離します。
しばらくするとCWMのメニュー画面になります。
「mounts and storage」に入り、「mount /system」と「mount /data」をしておきます。
もちろんPCと端末はUSBでつないでおいてください。

■C2■作ったファイルを端末にぶっこむ
C:\odexwork>adb push old\SystemUI.apk /data/local/tmp
C:\odexwork>adb push new\deodex_SystemUI.apk /data/local/tmp
C:\odexwork>adb push dexopt-wrapper /data/local/tmp

多分、rootをとった時に/data/local/tmpは作られていると思うので、それ前提です。
すでにdexopt-wrapperを導入している場合、3行目は不要です。

■C3■dexopt-wrapperのパーミッション変更
以降は端末のシェルに入っての作業となります。

C:\odexwork>adb shell
~ # cd /data/local/tmp
/data/local/tmp # chmod 0755 dexopt-wrapper

すでにdexopt-wrapperを導入している場合、3行目は不要です。

■C4■dexを埋め込んだapkからodex生成
/data/local/tmp # ./dexopt-wrapper deodex_SystemUI.apk SystemUI.odex /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework_ext.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar

deodex_SystemUI.apkを元に、SystemUI.odexが生成されます。
3つ目の引数がやたら長いですが、これを指定しておかないと、上手くodexが作れません。しかも順番も重要のようですので、この部分は下手にいじらないほうが良いでしょう。

なお、dexopt-wrapperは生成ファイルを上書きできないようですので、すでに/data/local/tmpにSystemUI.odexがある場合は、あらかじめ「rm SystemUI.odex」しておきましょう。

■C5■odexに署名をコピー
/data/local/tmp # busybox dd if=/system/app/SystemUI.odex of=SystemUI.odex bs=1 count=20 skip=52 seek=52 conv=notrunc

手順C4で生成したodexは、署名がまずいらしいので、元のodexから署名部分を移植します。

■C6■パーミッション変更
/data/local/tmp # chmod 0644 SystemUI.apk
/data/local/tmp # chmod 0644 SystemUI.odex

生成物のパーミッションを0644に変更。
odexのほうはすでに0644になっているかもしれませんが、念の為。

■C7■仕上げ
/data/local/tmp # mv /system/app/SystemUI.apk /system/app/SystemUI.apk.bak
/data/local/tmp # cp SystemUI.apk /system/app
/data/local/tmp # mv /system/app/SystemUI.odex /system/app/SystemUI.odex.bak
/data/local/tmp # cp SystemUI.odex /system/app
/data/local/tmp # reboot

apkもodexも元のファイルをリネームしてバックアップ(すでにしたことがあれば不要)しておき、新しいapkとodexを/system/appに配置。
そして再起動。
ちゃんと起動し、ステータスバーやナビゲーションバーが出てきたら成功と思われます。

11 件のコメント:

  1. なにかと質問ですみません。
    ナビゲーションバーを5つではなく、
    バック、ホーム(アプリ履歴)、メニューとしたいばあいどうすればいいでしょうか?
    何度やっても表示が消えてしまいます。
    教えていただければ助かります。

    返信削除
    返信
    1. まず、その改変ですと、編集するのは navigation_bar.xml だけです。SystemUI.odexのほうはいじりません。
      表示が消えるというのは、SystemUIがエラーで落ちるということでしょうか。
      本文にもありますが、@id/recent_appsの行はうかつに消すとエラーになりますので、layout_widthを0.0dipにするにとどめてください。
      するとメニューキーが履歴キーの位置に詰まってきますので、幅を80.0dipに変更。
      (もちろん、上記は縦画面・横画面両方の部分を編集してください。)
      横画面時のレイアウトではメニューの右にスペースを設けないといけないので、左端のもの(幅が40.0dipのView)と同じものを追加してください。縦画面時のレイアウトの方はメニューの上に追加です。

      ホーム長押しを有効にするために、@id/homeの行のsystemui:keyRepeat="false"は削除。これも当然横画面・縦画面の2か所あります。

      以上でいけると思います。
      別のコメントでも書きましたが、一気にいじるのではなく、少しずつ改変しては動作確認して、どこでエラーになるかを切り分けるようにしてください。
      そのやり方でやっていくと、どの部分をいじるといけなかったか大体分かってくると思います。
      漠然と「表示が消えてしまう」では、こちらもアドバイスのしようがありませんので…。

      削除
  2. ご丁寧にありがとうございました。
    やってみます。

    返信削除
  3. こちらの記事を参考に、xperia sx でメニューキーの常時表示が出来るようになりました。 ありがとうございました。

    返信削除
  4. キューブキューブさんの作られた5キーナビバーデーターをいただくことは出来ませんか?

    返信削除
    返信
    1. 記事に載せてるコードでは不足ですか。

      削除
  5. もしdeodexのばあいは
    B0からはどうすればいいでしょうか?
    僕は電源キーがロック画面で消えたくないです。
    教えていただけませんか?

    返信削除
    返信
    1. 申し訳ありません、私はdeodexで試したことがないので分かりません。

      削除
    2. じゃdeodexの問題は自分で解決します
      ただ電源キーがロック画面で消えない方法教えていただけませんか?

      削除
    3. 前提の環境ではロック画面でボタンが消えることはありません。
      ただ、最近の機種とかはロック画面でナビバー自体が消えたりするのかもしれません。その場合は諦めてください。
      (当方では検証できないです。記事が古いということで…。)

      削除
  6. 初めまして。
    記事を参考にナビバーの追加はうまくいったのですが、
    電源キーを長押しすると複数回タップされているような挙動を
    しています。(電源メニューの表示後、一度消え再度表示されます。)
    systemui:keyRepeat="false"を追記したりして、長押しを制御しようと
    しましたがうまくいきません。
    心当たりがありましたら。教えていただけないでしょうか?

    返信削除