![画像](https://habrastorage.org/getpro/habr/post_images/51f/340/e4d/51f340e4d45c2613aa2553b3590ba4be.jpg)
次のような状況を想像して:私たちは存在しないか、アンドロイドのインベントリに利用できないかのいずれかの非常に特定のプロパティを必要とし、製品を開発しています。 例えば、カードリーダーが必要です。
はい、私は外部のリーダーについて知っているが、我々は、より完全な、最終的な内部デバイスの外観にそれを作ることにした深刻な開発者です。 このような例:
![画像](https://habrastorage.org/getpro/habr/post_images/16b/f0a/91b/16bf0a91bbb9acc202e09571f842c8fe.jpg)
これらのデバイスは、特別な提供するために、持っている可能性がありますサービス 、サードパーティの開発者のための支払処理を。
この記事では、ナビゲーションのラインをカスタマイズするための新しいサービスを作成することにより、AndroidのAPIの拡張を記述します。
タスクステートメント
私たちはそこに新しい要素を追加することによってカスタマイズ(カスタマイズ)ナビゲーションラインを許可するAPIを開発しようとしています。 標準的なナビゲーションラインは、異なるデバイス上で異なって見えるだろう、とさえ持つデバイスに存在しなくてもよく、ハードウェアのナビゲーションボタン。
のは、あなたのネクサス9を見てみましょう:
![画像](https://habrastorage.org/getpro/habr/post_images/c2d/880/aa1/c2d880aa1b6a3736ac2614b5c52eaa1d.png)
ナビゲーションバー - これはナビゲーションボタンと下部の黒い長方形です。 開発者が独自のアイテムを置くことができるようにいいだろうので、彼は、(アプリケーションがフルスクリーンモードで実行されている場合を除く)すべての回で、ユーザーに示されています。
できるだけ柔軟APIを作るために、私たちのAPIの基本的なパラメータとしてAndroidのリモートビューを取ります。 これは、ユーザ(すなわち、他の開発者が)をカスタマイズする多くの標準コンポーネントを使用することができます。 また、アプリ内の要素からのフィードバック機構を提供します。
拡張ポイントとして、直接のAndroid SDKを取る:我々はAOSP(Androidのオープンソースプロジェクト)を修正し、あなた自身のAndroid SDKをビルドします。 このSDKは、当社の修正ファームウェアを実行しているデバイスに適しているが、我々はそのような制限は、先験的に適しています。 その結果、当社のサービスを使用することは非常に簡単ですし、彼らは、Android SDKからすぐに利用できるようになります。
Relizatsiya
開発環境をダウンロードして設定しAOSP
提供AOSPの開発環境を設定するために必要なすべての情報ここに 。 選択したデバイスと互換性があります(これはAndroidのバージョンに影響します)AOSPの適切なブランチを選択します。 私の場合、それはネクサス9だし、ブランチは、Android-7.1.1_r33を。
新しいサービスを開発
次の図が示す実装に関わる全てのコンポーネントは今後:
![画像](https://habrastorage.org/getpro/habr/post_images/61f/28a/fa3/61f28afa3fac3e7ea96a7907170d1cff.jpg)
アイデアは、Androidチームを通じてクライアントアプリケーションにナビゲーションバーを管理するための高度な機能を提供することです。 ファイルやNavigationBarInflaterView.java {} AOSP /frameworks/base/packages/SystemUI/res/layout/navigation_bar.xmlで定義されたナビゲーションバー。 彼らは時のシステムSystemUIアプリケーションであるとしてではなく、私たちは、Androidがsystem_processの過程にある主なサービスとして、クライアントアプリケーションから直接アクセスすることはできません。
このように、私たちの目的は、彼らは、クライアント側のコードのために利用できるようになり、これらのコンポーネントsystem_processを考えることです。
1.クライアントアプリケーションから表示されるサービスプロキシ(マネージャー)を作成します。 これは、標準のJavaクラスであること、すべての代表者のサービスの機能:
コード
package android.os; /** * /framework/base/core/java/android/os/NavBarExServiceMgr.java * It will be available in framework through import android.os.NavBarExServiceMgr; */ import android.content.Context; import android.widget.RemoteViews; public class NavBarExServiceMgr { private static final String TAG = "NavBarExServiceMgr"; private final Context context; private final INavBarExService navBarExService; public static NavBarExServiceMgr getInstance(Context context) { return (NavBarExServiceMgr) context.getSystemService(Context.NAVBAREX_SERVICE); } /** * Creates a new instance. * * @param context The current context in which to operate. * @param service The backing system service. * @hide */ public NavBarExServiceMgr(Context context, INavBarExService service) { this.context = context; if (service == null) throw new IllegalArgumentException("service is null"); this.navBarExService = service; } /** * Sets the UI component * * @param ui - ui component * @throws RemoteException * @hide */ public void setUI(INavBarExServiceUI ui) throws RemoteException { navBarExService.setUI(ui); } public String addView(int priority, RemoteViews remoteViews) { try { return navBarExService.addView(priority, remoteViews); } catch (RemoteException ignored) {} return null; } public boolean removeView(String id) { try { return navBarExService.removeView(id); } catch (RemoteException ignored) {} return false; } public boolean replaceView(String id, RemoteViews remoteViews) { try { return navBarExService.replaceView(id, remoteViews); } catch (RemoteException e) {} return false; } public boolean viewExist(String id) { try { return navBarExService.viewExist(id); } catch (RemoteException e) {} return false; } }
このクラスは、クラスの静的SystemServiceRegistryセクションに登録する必要があります。
コード
registerService(Context.NAVBAREX_SERVICE, NavBarExServiceMgr.class, new CachedServiceFetcher() { @Override public NavBarExServiceMgr createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(Context.NAVBAREX_SERVICE); INavBarExService service = INavBarExService.Stub.asInterface(b); if (service == null) { Log.wtf(TAG, "Failed to get INavBarExService service."); return null; } return new NavBarExServiceMgr(ctx, service); }});
INavBarExServiceサービスを。"); registerService(Context.NAVBAREX_SERVICE, NavBarExServiceMgr.class, new CachedServiceFetcher() { @Override public NavBarExServiceMgr createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(Context.NAVBAREX_SERVICE); INavBarExService service = INavBarExService.Stub.asInterface(b); if (service == null) { Log.wtf(TAG, "Failed to get INavBarExService service."); return null; } return new NavBarExServiceMgr(ctx, service); }});
。 registerService(Context.NAVBAREX_SERVICE, NavBarExServiceMgr.class, new CachedServiceFetcher() { @Override public NavBarExServiceMgr createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(Context.NAVBAREX_SERVICE); INavBarExService service = INavBarExService.Stub.asInterface(b); if (service == null) { Log.wtf(TAG, "Failed to get INavBarExService service."); return null; } return new NavBarExServiceMgr(ctx, service); }});
サービスは、クライアント側することができますので、以下のように提供されています:
コード
NavBarExServiceMgr navBarExServiceMgr = (NavBarExServiceMgr) getSystemService(Context.NAVBAREX_SERVICE); // TODO
Context.NAVBAREX_SERVICE)。 NavBarExServiceMgr navBarExServiceMgr = (NavBarExServiceMgr) getSystemService(Context.NAVBAREX_SERVICE); // TODO
2. AIDLサービス・インターフェースを決定します。
コード
/*
* aidl file :
* frameworks/base/core/java/android/os/INavBarExService.aidl
* This file contains definitions of functions which are
* exposed by service.
*/
package android.os;
import android.os.INavBarExServiceUI;
import android.widget.RemoteViews;
/** */
interface INavBarExService
{
/**
* @hide
*/
void setUI(INavBarExServiceUI ui);
String addView(in int priority, in RemoteViews remoteViews);
boolean removeView(in String id);
boolean replaceView(in String id, in RemoteViews remoteViews);
boolean viewExist(in String id);
}
そして、それを実現します:
コード
public class NavBarExService extends INavBarExService.Stub …
あなたが見ることができるような方法のほとんどは簡単ですが、setUIは奇妙に見えます。 これは、インタフェースINavBarExServiceUIが NavBarExServiceに自身を登録する実装するクラスによって使用される内部的な方法です。 コメント「@hide」でマークされたすべてのエンティティは、最終的なAndroidのSDKから削除されますので、クライアント側のコードから表示されません。
APIセマンティクスに関するいくつかのコメント:
- 各要素は、文字列識別子によって識別されます。 それは、新しい要素を追加することにより、サービスによって生成されます。 このように、当社のサービスを通じて、新しいアイテムを追加し、各アプリケーションは、将来の要素を持つ作品にできるようにするために返されたハンドルを保持しなければなりません。
- 他の方法は、パラメータ項目を識別する識別子として取ります。
- 各要素はまた、代わりに要素を示すパラメータ「優先順位」を持っています。 優先度の値が高いほど、左が投稿されます。
メソッドsetUIの実装:
コード
@Override public void setUI(INavBarExServiceUI ui) { Log.d(TAG, "setUI"); this.ui = ui; if (ui != null) { try { for (Pair entry : remoteViewsList.getList()) { ui.navBarExAddViewAtEnd(entry.first, entry.second); } } catch (Exception e) { Log.e(TAG, "Failed to configure UI", e); } } }
; @Override public void setUI(INavBarExServiceUI ui) { Log.d(TAG, "setUI"); this.ui = ui; if (ui != null) { try { for (Pair entry : remoteViewsList.getList()) { ui.navBarExAddViewAtEnd(entry.first, entry.second); } } catch (Exception e) { Log.e(TAG, "Failed to configure UI", e); } } }
) @Override public void setUI(INavBarExServiceUI ui) { Log.d(TAG, "setUI"); this.ui = ui; if (ui != null) { try { for (Pair entry : remoteViewsList.getList()) { ui.navBarExAddViewAtEnd(entry.first, entry.second); } } catch (Exception e) { Log.e(TAG, "Failed to configure UI", e); } } }
、E UI"); @Override public void setUI(INavBarExServiceUI ui) { Log.d(TAG, "setUI"); this.ui = ui; if (ui != null) { try { for (Pair entry : remoteViewsList.getList()) { ui.navBarExAddViewAtEnd(entry.first, entry.second); } } catch (Exception e) { Log.e(TAG, "Failed to configure UI", e); } } }
メソッドaddViewの実装:
コード
@Override public String addView(int priority, RemoteViews remoteViews) throws RemoteException { String id = UUID.randomUUID().toString(); int pos = remoteViewsList.add(priority, id, remoteViews); if (ui != null) { if (pos == 0) ui.navBarExAddViewAtStart(id, remoteViews); else if (pos == remoteViewsList.size() - 1) ui.navBarExAddViewAtEnd(id, remoteViews); else { // find previous element ID Pair prevElPair = remoteViewsList.getAt(pos - 1); ui.navBarExAddViewAfter(prevElPair.first, id, remoteViews); } } return id; }
remoteViews)がRemoteExceptionをスローします @Override public String addView(int priority, RemoteViews remoteViews) throws RemoteException { String id = UUID.randomUUID().toString(); int pos = remoteViewsList.add(priority, id, remoteViews); if (ui != null) { if (pos == 0) ui.navBarExAddViewAtStart(id, remoteViews); else if (pos == remoteViewsList.size() - 1) ui.navBarExAddViewAtEnd(id, remoteViews); else { // find previous element ID Pair prevElPair = remoteViewsList.getAt(pos - 1); ui.navBarExAddViewAfter(prevElPair.first, id, remoteViews); } } return id; }
、remoteViews)。 @Override public String addView(int priority, RemoteViews remoteViews) throws RemoteException { String id = UUID.randomUUID().toString(); int pos = remoteViewsList.add(priority, id, remoteViews); if (ui != null) { if (pos == 0) ui.navBarExAddViewAtStart(id, remoteViews); else if (pos == remoteViewsList.size() - 1) ui.navBarExAddViewAtEnd(id, remoteViews); else { // find previous element ID Pair prevElPair = remoteViewsList.getAt(pos - 1); ui.navBarExAddViewAfter(prevElPair.first, id, remoteViews); } } return id; }
- @Override public String addView(int priority, RemoteViews remoteViews) throws RemoteException { String id = UUID.randomUUID().toString(); int pos = remoteViewsList.add(priority, id, remoteViews); if (ui != null) { if (pos == 0) ui.navBarExAddViewAtStart(id, remoteViews); else if (pos == remoteViewsList.size() - 1) ui.navBarExAddViewAtEnd(id, remoteViews); else { // find previous element ID Pair prevElPair = remoteViewsList.getAt(pos - 1); ui.navBarExAddViewAfter(prevElPair.first, id, remoteViews); } } return id; }
remoteViewsListは (ないsetUIメソッドが呼ばれた)UIが接続されていないれるまで作成された要素を保持するために使用されます。 それが既に接続されている場合(フィールドUIがヌルに等しくない)新たな要素がUIに直接添加されます。
3. SystemServerは 、システム内の当社のサービスを登録する必要があります。
コード
try { traceBeginAndSlog("StartNavBarExService"); ServiceManager.addService(Context.NAVBAREX_SERVICE, new NavBarExService(context)); Slog.i(TAG, "NavBarExService Started"); } catch (Throwable e) { reportWtf("Failure starting NavBarExService Service", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
)); try { traceBeginAndSlog("StartNavBarExService"); ServiceManager.addService(Context.NAVBAREX_SERVICE, new NavBarExService(context)); Slog.i(TAG, "NavBarExService Started"); } catch (Throwable e) { reportWtf("Failure starting NavBarExService Service", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
); try { traceBeginAndSlog("StartNavBarExService"); ServiceManager.addService(Context.NAVBAREX_SERVICE, new NavBarExService(context)); Slog.i(TAG, "NavBarExService Started"); } catch (Throwable e) { reportWtf("Failure starting NavBarExService Service", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
、E)。 try { traceBeginAndSlog("StartNavBarExService"); ServiceManager.addService(Context.NAVBAREX_SERVICE, new NavBarExService(context)); Slog.i(TAG, "NavBarExService Started"); } catch (Throwable e) { reportWtf("Failure starting NavBarExService Service", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
新しいサービスを追加すると、AndroidのSEPolicyポリシーを変更する必要があり。
ファイル{} AOSP /system/selinux/service.teに新しいエントリを追加します。
type navbarex_service, app_api_service, system_server_service, service_manager_type;
ファイル{} AOSPに新しいエントリを追加/システムは/ selinuxを/ service_contexts:
navbarex u:object_r:navbarex_service:s0
アンドロイド7.0のためのSELinuxのファイル形式は、Androidの以前のバージョンとは異なることに注意してください。
4.クライアントは、文字列値の代わりに使用できることをコンテキストクラスのサービスの一定の名前を追加します。
コード
/** * Use with {@link #getSystemService} to retrieve a * {@link android.os.NavBarExServiceMgr} for using NavBarExService * * @see #getSystemService */ public static final String NAVBAREX_SERVICE = "navbarex";
検索します /** * Use with {@link #getSystemService} to retrieve a * {@link android.os.NavBarExServiceMgr} for using NavBarExService * * @see #getSystemService */ public static final String NAVBAREX_SERVICE = "navbarex";
インタフェースINavBarExServiceUIを定義します。5.:
コード
/*
* aidl file :
* frameworks/base/core/java/android/os/INavBarExServiceUI.aidl
* This file contains definitions of functions which are provided by UI.
*/
package android.os;
import android.widget.RemoteViews;
/** @hide */
oneway interface INavBarExServiceUI
{
void navBarExAddViewAtStart(in String id, in RemoteViews remoteViews);
void navBarExAddViewAtEnd(in String id, in RemoteViews remoteViews);
void navBarExAddViewBefore(in String targetId, in String id, in RemoteViews remoteViews);
void navBarExAddViewAfter(in String targetId, in String id, in RemoteViews remoteViews);
void navBarExRemoveView(in String id);
void navBarExReplaceView(in String id, in RemoteViews remoteViews);
}
それは顧客にそれが見えなくなり、属性を「隠す」、とマークされていることに注意してください。 また、キーワード一方向に注意してください。 すべてのコールが(それはまた、メソッドの空の戻り値を必要とする)非ブロックされているので、それは、より速くSystemUIにsystem_processからプロセス間通信を行います。
6. VendorServices SystemUI規格成分(それはSystemUIクラスから継承)を実装インタフェースINavBarExServiceUIします:
コード
public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
); public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
最終RemoteViews remoteViews) public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
; public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
)。 public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
" + e)前記 public class VendorServices extends SystemUI { private final Handler handler = new Handler(); private NavBarExServiceMgr navBarExServiceMgr; private volatile PhoneStatusBar statusBar; private INavBarExServiceUI.Stub navBarExServiceUI = new INavBarExServiceUI.Stub() { @Override public void navBarExAddViewAtStart(final String id, final RemoteViews remoteViews) { if (!initStatusBar()) return; handler.post(new Runnable() { @Override public void run() { statusBar.navBarExAddViewAtStart(id, remoteViews); } }); } //… } @Override protected void onBootCompleted() { super.onBootCompleted(); navBarExServiceMgr = (NavBarExServiceMgr) mContext.getSystemService(Context.NAVBAREX_SERVICE); if (navBarExServiceMgr == null) { Log.e(TAG, "navBarExServiceMgr=null"); return; } try { navBarExServiceMgr.setUI(navBarExServiceUI); } catch (Exception e) { Log.e(TAG, "setUI exception: " + e); } } }
興味深い方法はonBootCompleted。 それはNavBarExServiceMgrを通じてNavBarExServiceに自己登録(原因setUIメソッド)を生成します。 また、複数の呼び出しを行うことがsetUIれるSystemUIプロセス(例えばによる落下に)再起動することができることに留意されたいです。 後者のみに言うまでもなくを考慮すべきです。
7.クラスPhoneStatusBarは NavBarExServiceとNavigationBarInflaterViewを結ぶ重要な要素です。 これは、順番にNavigationBarInflaterViewへの参照が含まれているNavigationBarViewへのリンクが含まれています。 他方VendorServicesにSystemUI.getComponent法によりPhoneStatusBarへの参照を取得します。
コード
private boolean initStatusBar() { if (statusBar == null) { synchronized (initLock) { if (statusBar == null) { statusBar = getComponent(PhoneStatusBar.class); if (statusBar == null) { Log.e(TAG, "statusBar = null"); return false; } Log.d(TAG, "statusBar initialized"); } } } return true; }
")。 private boolean initStatusBar() { if (statusBar == null) { synchronized (initLock) { if (statusBar == null) { statusBar = getComponent(PhoneStatusBar.class); if (statusBar == null) { Log.e(TAG, "statusBar = null"); return false; } Log.d(TAG, "statusBar initialized"); } } } return true; }
); private boolean initStatusBar() { if (statusBar == null) { synchronized (initLock) { if (statusBar == null) { statusBar = getComponent(PhoneStatusBar.class); if (statusBar == null) { Log.e(TAG, "statusBar = null"); return false; } Log.d(TAG, "statusBar initialized"); } } } return true; }
奇妙な構造「if(statusBar == null)」に気づきましたか? それは「と呼ばれているダブルルームチェックロッキングパターンA 」と追求、次の目的:オブジェクトのスレッドセーフな初期化を行いますが、オブジェクトがすでに初期化されているとき、同期部に入る避けるために。 非常にシンプルPhoneStatusBarとNavigationBarViewの変更:彼らはちょうどNavigationBarInflaterViewクラスへのすべての呼び出しを委任します。
8.クラスNavigationBarInflaterView - UIで即時に変化をもたらすこのfinalクラス。 彼の方法navBarExAddViewAtStartをここに:
コード
public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
remoteViews){ public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
(mRot90 == nullの))のリターン; public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
R.id.ends_group)。 public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
R.id.ends_group)。 public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
(ends90 == nullの))のリターン; public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
ID、RemoteViews remoteViews、のViewGroup親){ public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
); public void navBarExAddViewAtStart(String id, RemoteViews remoteViews) { if ((mRot0 == null) || (mRot90 == null)) return; ViewGroup ends0 = (ViewGroup) mRot0.findViewById(R.id.ends_group); ViewGroup ends90 = (ViewGroup) mRot90.findViewById(R.id.ends_group); if ((ends0 == null) || (ends90 == null)) return; navBarExAddView(0, id, remoteViews, ends0); navBarExAddView(0, id, remoteViews, ends90); } private void navBarExAddView(int index, String id, RemoteViews remoteViews, ViewGroup parent) { View view = remoteViews.apply(mContext, parent); view.setTag(navBarExFormatTag(id)); TransitionManager.beginDelayedTransition(parent); parent.addView(view, index); }
このコードは、カスタム要素のための既存の実装および用途分野mRot0、mRot90などR.id.ends_groupのViewGroupに依存しています。 mRot0とmRot90は、肖像画や風景モードのためのマークアップを表すので、それらの両方に私達の項目を追加します。 我々はまた、いくつかのアニメーションを再生するTransitionManagerのを従事しました。
Android.mkファイル約9 1つの警告。 それは私たちのAIDLファイルへのリンクが含まれている必要があります。 LOCAL_SRC_FILESセクションの二つのファイル:
コード
LOCAL_SRC_FILES += \
core/java/android/os/INavBarExService.aidl \
core/java/android/os/INavBarExServiceUI.aidl \
...
aidl_filesセクションのINavBarExService.aidl:
コード
aidl_files := \
frameworks/base/core/java/android/os/INavBarExService.aidl \
...
揃って
ソースコードはで利用可能ですGitHubの 。 frameworks_base.patchとsystem_sepolicy.patch:カタログ「パッチ」で2つのファイルを持っています。 「{AOSP} /システム/ sepolicy」に - 最初のパッチは、ディレクトリ「{AOSP} /フレームワーク/塩基」、第二に適用されます。
NavBarExDemoディレクトリには、gradleビルドシステムのデモアプリケーションが含まれています。
実際のデバイス上で実装をテストするために、我々はする必要があります。
- ダウンロード株式Androidのソースコードと開発環境をカスタマイズします。
- パッチを適用します。
- 収集カスタムAndroidのSDK。
- カスタムファームウェアをビルドし、デバイスをフラッシュします。
- デモアプリケーションを起動します。
AndroidのSDKをビルドするには、次のコマンドを実行します。
コード
. build/envsetup.sh
lunch sdk_x86-eng
make sdk -j16
結果のZIPファイルは、ディレクトリ{} AOSP /アウト/ホスト/にあります(のlinux-x86の/ SDK / sdk_x86あなたがLinux上で実行している場合)。 ファイル名は、お使いのLinuxのユーザー名が含まれることに留意してください。 この動作を変更するには、「BUILD_NUMBER」環境変数を定義し、その値は代わりに、ユーザー名を使用します。 Jパラメータを指定する方法多くのタスクを同時に実行します。 2を乗じたプロセッサコアの数を使用してください。
ファームウェアは、次のコマンドを使用しては(ネクサス9用)を作成することができます。
コード
. build/envsetup.sh
lunch aosp_flounder-userdebug
make otapackage -j16
ファームウェアは、コマンドを使用してフラッシュすることができます。
コード
fastboot -w flashall
デバイスがロック解除されたブートローダを持っている必要があります。
結果をテストするには、特別なデモアプリケーションを作成しました。 それはあなたがナビゲーションラインの要素を管理することができます。
![画像](https://habrastorage.org/getpro/habr/post_images/e59/e6f/535/e59e6f535c8ce8a72b84479c8329139e.png)
カスタム要素を押すと、デモ・アプリケーションは、(システムが必要な場合、実際に、彼女はプロセスを開始します)が実行されていない場合でも、乾杯します。
![画像](https://habrastorage.org/getpro/habr/post_images/17a/1cf/7d1/17a1cf7d1ccdf18324eebdcbf3b1bb2d.png)
欠点
デバイスを再起動した後、すべてのカスタマイズを失うことになります。 だから、どこかでそれを維持するとよいでしょう。 また、レイアウト要素をより正確に制御するためのAPIにパラメータ«重力を»追加するには良いでしょう。
まとめ
私たちは、Android SDKを拡大しているとAndroidのナビゲーションラインをカスタマイズするために設計された新しいサービスを導入しました。 また、当社のサービスを含むネクサス9タブレット用のカスタムファームウェアを作成しました。
では前の記事 、私たちはここに、この同じ実装が、ネクサス9を安定化画面ネクサス7を実装しています:
PSは、実際に、私はまたに掲載されていました物品のオリジナル英語版の作者だblog.lemberg.co.uk私は技術的な質問に答えることができますので、。