欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

目錄

Android 11 SystemUI 啟動(dòng)流程

SystemUI 有哪內(nèi)容

從表面上看, 我們看到的狀態(tài)欄、通知欄、下拉菜單、導(dǎo)航欄、鎖屏、最近任務(wù)、低電提示等系統(tǒng)頁(yè)面都是 SystemUI 的。SystemUI,在源碼目錄中位于: framework/base/packages 目錄下, 可見(jiàn) SystemUI 和 framework 是關(guān)聯(lián)的, SystemUI 依賴了很多內(nèi)部 API , 系統(tǒng)資源, SystemUI 編譯是要依賴系統(tǒng)源碼的。

SystemUI 也是一個(gè)應(yīng)用,不過(guò)這個(gè)應(yīng)用特殊之處在于他沒(méi)有啟動(dòng)圖標(biāo)、也沒(méi)有入口 Activity 。他的入口程序是一個(gè)服務(wù):SystemUIService。 這個(gè)服務(wù)會(huì)被系統(tǒng)服務(wù)拉起來(lái), 這個(gè)服務(wù)起來(lái), SystemUI 應(yīng)用進(jìn)程就創(chuàng)建起來(lái)了,具體啟動(dòng)過(guò)程后面會(huì)分析。除了 SystemUIService , SystemUI 還有很多服務(wù), 例如: 負(fù)責(zé)鎖屏的KeyguardService、負(fù)責(zé)最近任務(wù)的 RecentsSystemUserService、負(fù)責(zé)壁紙的 ImageWallpaper 、負(fù)責(zé)截屏的TakeScreenshotService 等。

下面是PhoneStatusBarView的view 樹(shù)形圖:

PanelHolder

PanelHolder是用戶下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 兩個(gè)部分。PanelHolder是一個(gè)繼承自FrameLayout的自定義view,它的內(nèi)容是通過(guò)include status_bar_expanded.xml進(jìn)行填充的。PanelHolder的布局比較復(fù)雜,為了提高view的重用性大量的使用了include標(biāo)簽。下面是PanelHolder的view樹(shù)形圖, 只給出了了主要的view:

架構(gòu)關(guān)系

在系統(tǒng)服務(wù)中,有一個(gè)服務(wù)是專門為 SystemUI 的狀態(tài)欄服務(wù)的, 這個(gè)服務(wù)就是 StatusbarManagerService (簡(jiǎn)稱:SMS),和這個(gè)服務(wù)關(guān)系比較密切的服務(wù)是 WindowManagerService(簡(jiǎn)稱:WMS), SMS 主要管控的是狀態(tài)欄、導(dǎo)航欄, 例如:我們可以設(shè)置全屏、沉浸式狀態(tài)欄都是 SMS 在起作用。

services組件啟動(dòng)時(shí)配置列表 : (R.array.config_systemUIServiceComponents)

所有 SystemUIService 都是繼承自 SystemUI.class , SystemUI.class 是一個(gè)抽象類

com.android.systemui.util.NotificationChannels 通知信息

com.android.systemui.keyguard.KeyguardViewMediator 鎖屏

com.android.systemui.recents.Recents 近期列表

Android 10之后近期列表的顯示被移到Launcher里面了。在Launcher3的一個(gè) 類中TouchInteractionService.java IBinder mMyBinder = new IOverviewProxy.Stub() 通過(guò)AIDL的方法與systemUI通信

————————————————

com.android.systemui.volume.VolumeUI 聲音UI顯示

com.android.systemui.statusbar.phone.StatusBar 狀態(tài)欄及下拉面板

com.android.systemui.usb.StorageNotification usb通知管理

com.android.systemui.power.PowerUI 電源UI顯示管理

com.android.systemui.media.RingtonePlayer 播放鈴聲

com.android.systemui.keyboard.KeyboardUI鍵盤UI

com.android.systemui.shortcut.ShortcutKeyDispatcher快捷方式

@string/config_systemUIVendorServiceComponent廠商相關(guān)定制

com.android.systemui.util.leak.GarbageMonitor$Service垃圾監(jiān)測(cè)器

com.android.systemui.LatencyTester 延遲測(cè)試儀

com.android.systemui.globalactions.GlobalActionsComponent 關(guān)機(jī)界面的顯示、全局控制

com.android.systemui.ScreenDecorations屏幕裝飾

com.android.systemui.biometrics.AuthController生物識(shí)別

com.android.systemui.SliceBroadcastRelayHandler 切片廣播

com.android.systemui.statusbar.notification.InstantAppNotifier

com.android.systemui.theme.ThemeOverlayController

com.android.systemui.accessibility.WindowMagnification

com.android.systemui.accessibility.SystemActions

com.android.systemui.toast.ToastUI Toast

com.android.systemui.wmshell.WMShell

一、SystemUI的啟動(dòng)流程

1、SystemServer

SystemServer啟動(dòng)后,會(huì)在Main Thread啟動(dòng)ActivityManagerService,當(dāng)ActivityManagerService systemReady后,會(huì)去啟動(dòng)SystemUIService。

/frameworks/base/services/java/com/android/server/SystemServer.java

①main

/**

* The main entry point from zygote.

*/

public static void main(String[] args) {

new SystemServer().run();

}

②run

private void run() {

t.traceBegin("InitBeforeStartServices");

....

// Create the system service manager.

mSystemServiceManager = new SystemServiceManager(mSystemContext);

mSystemServiceManager.setStartInfo(mRuntimeRestart,

mRuntimeStartElapsedTime, mRuntimeStartUptime);

LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

....

}

③mActivityManagerService.systemReady

mActivityManagerService.systemReady(() -> {

//準(zhǔn)備好服務(wù)

Slog.i(TAG, "Making services ready");

....

//跟蹤開(kāi)啟系統(tǒng)界面

t.traceBegin("StartSystemUI");

try {

//開(kāi)啟系統(tǒng)界面

startSystemUi(context, windowManagerF);

} catch (Throwable e) {

reportWtf("starting System UI", e);

}

t.traceEnd();

....

}

④startSystemUi

private static void startSystemUi(Context context, WindowManagerService windowManager) {

PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);

Intent intent = new Intent();

intent.setComponent(pm.getSystemUiServiceComponent());

intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);

//Slog.d(TAG, "Starting service: " + intent);

//通過(guò)startServiceAsUser,SystemUIService就啟動(dòng)了,即SystemUI進(jìn)程開(kāi)機(jī)啟動(dòng)

context.startServiceAsUser(intent, UserHandle.SYSTEM);

windowManager.onSystemUiStarted();

}

2、systemUIService

在SystemUIService的onCreate方法中會(huì)調(diào)用SystemUIApplication的startServicesIfNeeded方法,這個(gè)方法會(huì)調(diào)用 startServicesIfNeeded(SERVICES)方法啟動(dòng)一系列服務(wù)

@Override

public void onCreate() {

super.onCreate();

// Start all of SystemUI

((SystemUIApplication) getApplication()).startServicesIfNeeded();

3、SystemUIApplication

/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

①startServicesIfNeeded()

public void startServicesIfNeeded() {

//獲取所有的服務(wù)的路徑,所有SERVICES統(tǒng)一繼承了SystemUI類:

String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());

startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);

}

②startServicesIfNeeded(String metricsPrefix, String[] services)

在重載方法中將每一個(gè)名稱通過(guò)反射來(lái)得到實(shí)例對(duì)象,然后依次調(diào)用每一個(gè)SystemUI的子類的start方法啟動(dòng)每一個(gè)模塊。

private void startServicesIfNeeded(String metricsPrefix, String[] services) {

if (mServicesStarted) {

return;

}

mServices = new SystemUI[services.length];

//檢查一下,也許在我們開(kāi)始之前很久它就已經(jīng)完成了

if (!mBootCompleteCache.isBootComplete()) {

// check to see if maybe it was already completed long before we began

// see ActivityManagerService.finishBooting()

if ("1".equals(SystemProperties.get("sys.boot_completed"))) {

mBootCompleteCache.setBootComplete();

if (DEBUG) {

Log.v(TAG, "BOOT_COMPLETED was already sent");

}

}

}

final DumpManager dumpManager = mRootComponent.createDumpManager();

Log.v(TAG, "Starting SystemUI services for user " +

Process.myUserHandle().getIdentifier() + ".");

TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",

Trace.TRACE_TAG_APP);

//開(kāi)始追蹤

log.traceBegin(metricsPrefix);

final int N = services.length;

//遍歷services這個(gè)數(shù)組

for (int i = 0; i < N; i++) {

String clsName = services[i];

if (DEBUG) Log.d(TAG, "loading: " + clsName);

log.traceBegin(metricsPrefix + clsName);

long ti = System.currentTimeMillis();

try {

SystemUI obj = mComponentHelper.resolveSystemUI(clsName);

if (obj == null) {

Constructor constructor = Class.forName(clsName).getConstructor(Context.class);

obj = (SystemUI) constructor.newInstance(this);

}

mServices[i] = obj;

} catch (ClassNotFoundException

| NoSuchMethodException

| IllegalAccessException

| InstantiationException

| InvocationTargetException ex) {

throw new RuntimeException(ex);

}

if (DEBUG) Log.d(TAG, "running: " + mServices[i]);

//依次調(diào)用service的start方法啟動(dòng)服務(wù)

mServices[i].start();

log.traceEnd();

// Warn if initialization of component takes too long

//如果組件初始化時(shí)間過(guò)長(zhǎng),則發(fā)出警告

ti = System.currentTimeMillis() - ti;

if (ti > 1000) {

Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");

}

if (mBootCompleteCache.isBootComplete()) {

mServices[i].onBootCompleted();

}

dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);

}

mRootComponent.getInitController().executePostInitTasks();

//結(jié)束追蹤

log.traceEnd();

mServicesStarted = true;

}

4、SystemUI

/**

* @see SystemUIApplication#startServicesIfNeeded()

*系統(tǒng)界面應(yīng)用 如果需要,啟動(dòng)服務(wù)

*/

public abstract class SystemUI implements Dumpable {

protected final Context mContext;

public SystemUI(Context context) {

mContext = context;

}

public abstract void start();

protected void onConfigurationChanged(Configuration newConfig) {

}

@Override

public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {

}

protected void onBootCompleted() {

}

public static void overrideNotificationAppName(Context context, Notification.Builder n,

boolean system) {

final Bundle extras = new Bundle();

String appName = system

? context.getString(com.android.internal.R.string.notification_app_name_system)

: context.getString(com.android.internal.R.string.notification_app_name_settings);

extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);

n.addExtras(extras);

}

}

二、狀態(tài)欄

1、SystemBars

SystemBars加載基本全部SystemUI的界面顯示,由前面可知調(diào)用的start方法實(shí)際上是每一個(gè)繼承于SytemUI的子類中的方法

①start

②createStatusBarFromConfig

從string資源文件里面讀取class name,通過(guò)java的反射機(jī)制實(shí)例化對(duì)象,然后調(diào)用start()方法啟動(dòng),class name的值如下圖:

private void createStatusBarFromConfig() {

......

String clsName = mContext.getString(R.string.config_statusBarComponent);

......

try {

cls = mContext.getClassLoader().loadClass(clsName);

} catch (Throwable t) {

throw andLog("Error loading status bar component: " + clsName, t);

}

try {

mStatusBar = (BaseStatusBar) cls.newInstance();

} catch (Throwable t) {

throw andLog("Error creating status bar component: " + clsName, t);

}

......

mStatusBar.start();

......

}

③String

com.android.systemui.statusbar.phone.StatusBar

2、StatusBar

①createAndAddWindows

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {

//創(chuàng)建狀態(tài)欄

makeStatusBarView(result);

mNotificationShadeWindowController.attach();

//創(chuàng)建狀態(tài)欄的窗口

mStatusBarWindowController.attach();

}

②makeStatusBarView

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {

final Context context = mContext;

.....

FragmentHostManager.get(mPhoneStatusBarWindow)

.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {

//CollapsedStatusBarFragment 替換 status_bar_container(狀態(tài)欄通知顯示區(qū)域)

CollapsedStatusBarFragment statusBarFragment =

(CollapsedStatusBarFragment) fragment;

PhoneStatusBarView oldStatusBarView = mStatusBarView;

mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();

//傳遞statusBar處理下拉事件

mStatusBarView.setBar(this);

//傳遞 NotificationPanelView 顯示下拉UI控制

mStatusBarView.setPanel(mNotificationPanelViewController);

mStatusBarView.setScrimController(mScrimController);

//初始化通知欄區(qū)域

statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

......

}).getFragmentManager()

.beginTransaction()

.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),

CollapsedStatusBarFragment.TAG)

.commit();

.....

2、CollapsedStatusBarFragment

①onCreateView

@Override

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,

Bundle savedInstanceState) {

return inflater.inflate(R.layout.status_bar, container, false);

}

②initNotificationIconArea

public void initNotificationIconArea(NotificationIconAreaController

notificationIconAreaController) {

//notification_icon_area是在status_bar.xml的布局,它是屬于通知Notification

//獲取到 notification_icon_area,F(xiàn)rameLayout轉(zhuǎn)為ViewGroup,

ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);

//調(diào)用 notificationIconAreaController 獲取通知要顯示的view(LinearLayout)

//在4中跟進(jìn)

mNotificationIconAreaInner =

notificationIconAreaController.getNotificationInnerAreaView();

//如果已經(jīng)有顯示的view,通過(guò) view 父布局將其自身remove,然后再重新addView。

//最后將 mNotificationIconAreaInner 顯示出來(lái)(設(shè)置透明度為1,visibility為VISIBLE)

if (mNotificationIconAreaInner.getParent() != null) {

((ViewGroup) mNotificationIconAreaInner.getParent())

.removeView(mNotificationIconAreaInner);

}

notificationIconArea.addView(mNotificationIconAreaInner);

//與上面一樣

ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area);

mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView();

if (mCenteredIconArea.getParent() != null) {

((ViewGroup) mCenteredIconArea.getParent())

.removeView(mCenteredIconArea);

}

statusBarCenteredIconArea.addView(mCenteredIconArea);

//默認(rèn)為顯示,直到我們知道其他情況

// Default to showing until we know otherwise.

showNotificationIconArea(false);

}

③showNotificationIconArea

當(dāng)狀態(tài)欄下拉時(shí),狀態(tài)欄中的圖標(biāo)icon會(huì)慢慢的變成透明和不可見(jiàn),就是通過(guò)hideSystemIconArea(true), hideNotificationIconArea(true)

//當(dāng)狀態(tài)欄下拉時(shí),設(shè)置狀態(tài)欄中的圖標(biāo)icon會(huì)慢慢的變成透明和不可見(jiàn)

public void hideNotificationIconArea(boolean animate) {

animateHide(mNotificationIconAreaInner, animate);

animateHide(mCenteredIconArea, animate);

}

//設(shè)置狀態(tài)欄圖標(biāo)透明度為1,visibility為VISIBLE

public void showNotificationIconArea(boolean animate) {

animateShow(mNotificationIconAreaInner, animate);

animateShow(mCenteredIconArea, animate);

}

public void hideOperatorName(boolean animate) {

if (mOperatorNameFrame != null) {

animateHide(mOperatorNameFrame, animate);

}

}

public void showOperatorName(boolean animate) {

if (mOperatorNameFrame != null) {

animateShow(mOperatorNameFrame, animate);

}

④animateShow

private void animateShow(View v, boolean animate) {

v.animate().cancel();

//(設(shè)置透明度為1,visibility為VISIBLE)

v.setVisibility(View.VISIBLE);

if (!animate) {

v.setAlpha(1f);

return;

}

.....

}

⑤animateHiddenState

//將視圖動(dòng)畫化為 INVISIBLE 或 GONE

private void animateHiddenState(final View v, int state, boolean animate) {

v.animate().cancel();

if (!animate) {

v.setAlpha(0f);

v.setVisibility(state);

return;

}

v.animate()

.alpha(0f)

.setDuration(160)

.setStartDelay(0)

.setInterpolator(Interpolators.ALPHA_OUT)

.withEndAction(() -> v.setVisibility(state));

}

3、status_bar.xml

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"

android:layout_width="match_parent"

android:layout_height="@dimen/status_bar_height"

android:id="@+id/status_bar"

android:orientation="vertical"

android:focusable="false"

android:descendantFocusability="afterDescendants"

android:accessibilityPaneTitle="@string/status_bar"

>

android:layout_width="match_parent"

android:layout_height="@dimen/status_bar_height"

android:id="@+id/status_bar_dark_view"

android:background="#ff000000"

android:visibility="gone" />

//

android:id="@+id/notification_lights_out"

android:layout_width="@dimen/status_bar_icon_size"

android:layout_height="match_parent"

android:paddingStart="@dimen/status_bar_padding_start"

android:paddingBottom="2dip"

android:src="@drawable/ic_sysbar_lights_out_dot_small"

android:scaleType="center"

android:visibility="gone"

/>

//

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingStart="@dimen/status_bar_padding_start"

android:paddingEnd="@dimen/status_bar_padding_end"

android:paddingTop="@dimen/status_bar_padding_top"

android:orientation="horizontal"

>

android:layout_height="match_parent"

android:layout_width="0dp"

android:layout_weight="1">

android:id="@+id/status_bar_left_side"

android:layout_height="match_parent"

android:layout_width="match_parent"

android:clipChildren="false"

>

android:id="@+id/operator_name"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout="@layout/operator_name" />

android:id="@+id/clock"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:textAppearance="@style/TextAppearance.StatusBar.Clock"

android:singleLine="true"

android:paddingStart="@dimen/status_bar_left_clock_starting_padding"

android:paddingEnd="@dimen/status_bar_left_clock_end_padding"

android:gravity="center_vertical|start"

/>

//

android:id="@+id/notification_icon_area"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:orientation="horizontal"

android:clipChildren="false"/>

android:id="@+id/cutout_space_view"

android:layout_width="0dp"

android:layout_height="match_parent"

android:gravity="center_horizontal|center_vertical"

/>

//居中的圖標(biāo)區(qū)域

android:id="@+id/centered_icon_area"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:orientation="horizontal"

android:clipChildren="false"

android:gravity="center_horizontal|center_vertical"/>

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:orientation="horizontal"

android:gravity="center_vertical|end"

>

//

//

android:id="@+id/emergency_cryptkeeper_text"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout="@layout/emergency_cryptkeeper_text"

/>

4、NotificationIconAreaController

①getNotificationInnerAreaView

/**

* Returns the view that represents the notification area.+

* 返回表示通知區(qū)域的視圖。

*/

public View getNotificationInnerAreaView() {

return mNotificationIconArea;

}

②initializeNotificationAreaViews

/**

* Initializes the views that will represent the notification area.

* 初始化將表示通知區(qū)域的視圖。

*/

protected void initializeNotificationAreaViews(Context context) {

reloadDimens(context);

LayoutInflater layoutInflater = LayoutInflater.from(context);

//通知圖標(biāo)區(qū)域布局

mNotificationIconArea = inflateIconArea(layoutInflater);

//通知圖標(biāo)

mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);

//獲取通知滾動(dòng)布局

mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();

//中心圖標(biāo)區(qū)域布局

mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null);

//居中的圖標(biāo)

mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon);

initAodIcons();

}

③inflateIconArea

protected View inflateIconArea(LayoutInflater inflater) {

return inflater.inflate(R.layout.notification_icon_area, null);

}

三、status icon加載流程

1、 status_bar.xml

android:id="@+id/centered_icon_area"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:orientation="horizontal"

android:clipChildren="false"

android:gravity="center_horizontal|center_vertical"/>

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:orientation="horizontal"

android:gravity="center_vertical|end"

>

2、system_icons.xml

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:id="@+id/system_icons"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_vertical">

//StatusIconContainer繼承AlphaOptimizedLinearLayout

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="match_parent"

android:paddingEnd="@dimen/signal_cluster_battery_padding"

android:gravity="center_vertical"

android:orientation="horizontal"/>

android:layout_height="match_parent"

android:layout_width="wrap_content"

android:clipToPadding="false"

android:clipChildren="false"

systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />

3、AlphaOptimizedLinearLayout

//該方法用來(lái)標(biāo)記當(dāng)前view是否存在過(guò)度繪制,存在返回ture,不存在返回false,

//api里面默認(rèn)返回為true,status icon不存在過(guò)度繪制。

@Override

public boolean hasOverlappingRendering() {

return false;

}

4、CollapsedStatusBarFragment

@Override

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

mStatusBar = (PhoneStatusBarView) view;

if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {

mStatusBar.restoreHierarchyState(

savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));

}

......

}

4、StatusBarIconController

①DarkIconManager

/**

* Version of ViewGroup that observes state from the DarkIconDispatcher.

*/

public static class DarkIconManager extends IconManager {

private final DarkIconDispatcher mDarkIconDispatcher;

private int mIconHPadding;

public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) {

super(linearLayout, commandQueue);

mIconHPadding = mContext.getResources().getDimensionPixelSize(

R.dimen.status_bar_icon_padding);

mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);

}

//每個(gè)icon應(yīng)該就是對(duì)應(yīng)著代表順序的index和數(shù)據(jù)類型為String的slot

@Override

protected void onIconAdded(int index, String slot, boolean blocked,

StatusBarIconHolder holder) {

StatusIconDisplayable view = addHolder(index, slot, blocked, holder);

mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);

}

.....

//onSetIcon可能就是刷新icon狀態(tài)的

@Override

public void onSetIcon(int viewIndex, StatusBarIcon icon) {

super.onSetIcon(viewIndex, icon);

mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));

}

.....

}

②IconManager

public static class IconManager implements DemoMode {

.....

protected void onIconAdded(int index, String slot, boolean blocked,

StatusBarIconHolder holder) {

addHolder(index, slot, blocked, holder);

}

protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,

StatusBarIconHolder holder) {

switch (holder.getType()) {

case TYPE_ICON:

return addIcon(index, slot, blocked, holder.getIcon());

case TYPE_WIFI:

return addSignalIcon(index, slot, holder.getWifiState());

case TYPE_MOBILE:

return addMobileIcon(index, slot, holder.getMobileState());

}

return null;

}

@VisibleForTesting

protected StatusBarIconView addIcon(int index, String slot, boolean blocked,

StatusBarIcon icon) {

StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);

view.set(icon);

mGroup.addView(view, index, onCreateLayoutParams());

return view;

}

.....

}

5、StatusBarIconControllerImpl

//繼承StatusBarIconList

public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,

ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {

......

@Inject

public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) {

//config_statusBarIcons

super(context.getResources().getStringArray(

com.android.internal.R.array.config_statusBarIcons));

Dependency.get(ConfigurationController.class).addCallback(this);

.....

}

}

6、StatusBarIconList

在初始化的時(shí)候就已經(jīng)定義好了所有的slots,然后從framework中加載出來(lái),index就是string-array中的順序。

public class StatusBarIconList {

private ArrayList mSlots = new ArrayList<>();

public StatusBarIconList(String[] slots) {

final int N = slots.length;

for (int i=0; i < N; i++) {

mSlots.add(new Slot(slots[i], null));

}

}

7、config_statusBarIcons

@string/status_bar_rotate

@string/status_bar_headset

@string/status_bar_data_saver

@string/status_bar_managed_profile

@string/status_bar_ime

@string/status_bar_sync_failing

@string/status_bar_sync_active

@string/status_bar_cast

@string/status_bar_hotspot

@string/status_bar_location

@string/status_bar_bluetooth

@string/status_bar_nfc

@string/status_bar_tty

@string/status_bar_speakerphone

@string/status_bar_zen

@string/status_bar_mute

@string/status_bar_volume

@string/status_bar_vpn

@string/status_bar_ethernet

@string/status_bar_wifi

@string/status_bar_mobile

@string/status_bar_airplane

@string/status_bar_cdma_eri

@string/status_bar_data_connection

@string/status_bar_phone_evdo_signal

@string/status_bar_phone_signal

@string/status_bar_battery

@string/status_bar_alarm_clock

@string/status_bar_secure

@string/status_bar_clock

rotate

headset

data_saver

managed_profile

ime

sync_failing

sync_active

cast

hotspot

location

bluetooth

nfc

tty

speakerphone

zen

mute

volume

wifi

cdma_eri

data_connection

phone_evdo_signal

phone_signal

battery

alarm_clock

secure

clock

mobile

vpn

ethernet

airplane

好了,到這里我們的第一部分初始化流程就講完了

四、狀態(tài)顯示流程

由上面的初始化流程我們可以知道,每個(gè)icon都對(duì)應(yīng)了slot,slot數(shù)量比較多,我們就挑一個(gè)常見(jiàn)的Headset講下,其他的流程都是大致一樣的。

1、PhoneStatusBarPolicy

初始化注冊(cè)了大量的監(jiān)聽(tīng)

①init

// 初始化headset的slot

mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);

/** Initialize the object after construction. */

public void init() {

// listen for broadcasts

IntentFilter filter = new IntentFilter();

// 注冊(cè)headset狀態(tài)變化的action

filter.addAction(AudioManager.ACTION_HEADSET_PLUG);

filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);

filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);

filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);

mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);

Observer observer = ringer -> mHandler.post(this::updateVolumeZen);

mRingerModeTracker.getRingerMode().observeForever(observer);

mRingerModeTracker.getRingerModeInternal().observeForever(observer);

....

}

②BroadcastReceiver

private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

switch (action) {

case Intent.ACTION_SIM_STATE_CHANGED:

// Avoid rebroadcast because SysUI is direct boot aware.

if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {

break;

}

break;

case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:

updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,

TelecomManager.TTY_MODE_OFF));

break;

case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:

case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:

case Intent.ACTION_MANAGED_PROFILE_REMOVED:

updateManagedProfile();

break;

//監(jiān)聽(tīng)ACTION_HEADSET_PLUG

case AudioManager.ACTION_HEADSET_PLUG:

updateHeadsetPlug(context, intent);

break;

}

}

};

③updateHeadsetPlug

完成icon添加和狀態(tài)監(jiān)聽(tīng),然后當(dāng)收到對(duì)應(yīng)的action變化的時(shí)候,更新headset icon

private void updateHeadsetPlug(Context context, Intent intent) {

boolean connected = intent.getIntExtra("state", 0) != 0;

boolean hasMic = intent.getIntExtra("microphone", 0) != 0;

if (connected) {

String contentDescription = mResources.getString(hasMic

? R.string.accessibility_status_bar_headset

: R.string.accessibility_status_bar_headphones);

//setIcon負(fù)責(zé)設(shè)置icon

mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic

: R.drawable.stat_sys_headset, contentDescription);

//setIconVisibility則根據(jù)connected狀態(tài)設(shè)置icon的可見(jiàn)性

mIconController.setIconVisibility(mSlotHeadset, true);

} else {

/*UNISOC: Add for bug 1130932 {@ */

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

if (!audioManager.isWiredHeadsetOn()) {

//setIconVisibility則根據(jù)connected狀態(tài)設(shè)置icon的可見(jiàn)性

mIconController.setIconVisibility(mSlotHeadset, false);

}

/* @} */

}

}

2、StatusBarIconControllerImpl

①setIcon

@Override

public void setIcon(String slot, int resourceId, CharSequence contentDescription) {

//根據(jù)slot找到對(duì)應(yīng)的index

int index = getSlotIndex(slot);

//用index取得對(duì)應(yīng)的icon

StatusBarIconHolder holder = getIcon(index, 0);

if (holder == null) {

//第一次的時(shí)候,icon == null,所以通過(guò)new StatusBarIcon創(chuàng)建一個(gè)

StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),

Icon.createWithResource(

mContext, resourceId), 0, 0, contentDescription);

holder = StatusBarIconHolder.fromIcon(icon);

//然后調(diào)用此方法

setIcon(index, holder);

} else {

holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);

holder.getIcon().contentDescription = contentDescription;

handleSet(index, holder);

}

}

@Override

public void setIcon(int index, @NonNull StatusBarIconHolder holder) {

boolean isNew = getIcon(index, holder.getTag()) == null;

super.setIcon(index, holder);

if (isNew) {

addSystemIcon(index, holder);

} else {

handleSet(index, holder);

}

}

private void addSystemIcon(int index, StatusBarIconHolder holder) {

String slot = getSlotName(index);

int viewIndex = getViewIndex(index, holder.getTag());

boolean blocked = mIconBlacklist.contains(slot);

//onIconAdded-》初始化流程里面StatusBarIconController中添加icon的入口

//到這里setIcon就添加完畢了

mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));

}

②setIconVisibility

public void setIconVisibility(String slot, boolean visibility) {

int index = getSlotIndex(slot);

StatusBarIconHolder holder = getIcon(index, 0);

if (holder == null || holder.isVisible() == visibility) {

return;

}

holder.setVisible(visibility);

//icon已經(jīng)創(chuàng)建成功了,icon非空,并且第一次是icon.visible != visibility的

//就會(huì)順利的走到handleSet(index, icon)

handleSet(index, holder);

}

private void handleSet(int index, StatusBarIconHolder holder) {

int viewIndex = getViewIndex(index, holder.getTag());

//初始化流程里面StatusBarIconController中setIcon的地方

//到這里setIconVisibility也設(shè)置完畢了

mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));

}

五、創(chuàng)建狀態(tài)欄的窗口

1、StatusBarWindowController

mStatusBarWindowController.attach()

/**

* Adds the status bar view to the window manager.

*/

public void attach() {

// Now that the status bar window encompasses the sliding panel and its

// translucent backdrop, the entire thing is made TRANSLUCENT and is

// hardware-accelerated.

mLp = new WindowManager.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,

mBarHeight,

WindowManager.LayoutParams.TYPE_STATUS_BAR,

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH

| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,

PixelFormat.TRANSLUCENT);

mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;

mLp.token = new Binder();

mLp.gravity = Gravity.TOP;

mLp.setFitInsetsTypes(0 /* types */);

mLp.setTitle("StatusBar");

mLp.packageName = mContext.getPackageName();

mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

//WindowManager中添加view

//mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();

//private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;

mWindowManager.addView(mStatusBarView, mLp);

mLpChanged.copyFrom(mLp);

}

2、SuperStatusBarViewFactory

/**

* Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.

* Returns a cached instance, if it has already been inflated.

*/

public StatusBarWindowView getStatusBarWindowView() {

if (mStatusBarWindowView != null) {

return mStatusBarWindowView;

}

//由其可知加載的布局來(lái)自于super_status_bar

mStatusBarWindowView =

(StatusBarWindowView) mInjectionInflationController.injectable(

LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,

/* root= */ null);

if (mStatusBarWindowView == null) {

throw new IllegalStateException(

"R.layout.super_status_bar could not be properly inflated");

}

return mStatusBarWindowView;

}

3、super_status_bar.xml

從前面可知這里會(huì)用CollapsedStatusBarFragment 替換 status_bar_container(狀態(tài)欄通知顯示區(qū)域),完成圖標(biāo)的顯示

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:sysui="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:fitsSystemWindows="true">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical"

>

android:id="@+id/status_bar_container"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:visibility="gone"

/>

android:id="@+id/car_top_navigation_bar_container"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

六、電池圖標(biāo)刷新流程

1、BatteryMeterView

① 構(gòu)造方法BatteryMeterView()

public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);

setOrientation(LinearLayout.HORIZONTAL);

setGravity(Gravity.CENTER_VERTICAL | Gravity.START);

TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,

defStyle, 0);

final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,

context.getColor(R.color.meter_background_color));

mPercentageStyleId = atts.getResourceId(R.styleable.BatteryMeterView_textAppearance, 0);

/*Bug 1296708 add charge animation of batteryView*/

//添加電池視圖的充電動(dòng)畫 true

mBatteryAnimation = mContext.getResources().getBoolean(

R.bool.config_battery_animation);

//將電池等級(jí)添加到父布局中

if (mBatteryAnimation) {

//mBatteryAnimation為true

mUnisocDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor, false);

}else{

mDrawable = new ThemedBatteryDrawable(context, frameColor);

}

/*@}*/

atts.recycle();

mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));

mShowPercentAvailable = context.getResources().getBoolean(

com.android.internal.R.bool.config_battery_percentage_setting_available);

addOnAttachStateChangeListener(

new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS,

Dependency.get(CommandQueue.class)));

setupLayoutTransition();

mSlotBattery = context.getString(

com.android.internal.R.string.status_bar_battery);

mBatteryIconView = new ImageView(context);

/*Bug 1296708 add charge animation of batteryView*/

//添加電池視圖的充電動(dòng)畫

if (mBatteryAnimation) {

mBatteryIconView.setImageDrawable(mUnisocDrawable);

}else{

mBatteryIconView.setImageDrawable(mDrawable);

}

final MarginLayoutParams mlp = new MarginLayoutParams(

getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),

getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));

mlp.setMargins(0, 0, 0,

getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));

addView(mBatteryIconView, mlp);

updateShowPercent();

mDualToneHandler = new DualToneHandler(context);

// Init to not dark at all.

//設(shè)置默認(rèn)的電池布局的主題色,當(dāng)狀態(tài)欄主題發(fā)生改變時(shí),電池布局會(huì)做相應(yīng)的更換(亮色和暗色切換)

//在 PhoneStatusBarView 中添加了DarkReceiver監(jiān)聽(tīng),最終調(diào)用到 BatteryMeterView 的onDarkChanged()方法

onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);

//設(shè)置 Settings.System.SHOW_BATTERY_PERCENT 監(jiān)聽(tīng)

mUserTracker = new CurrentUserTracker(broadcastDispatcher) {

@Override

public void onUserSwitched(int newUserId) {

mUser = newUserId;

getContext().getContentResolver().unregisterContentObserver(mSettingObserver);

getContext().getContentResolver().registerContentObserver(

Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,

newUserId);

//當(dāng)用戶點(diǎn)擊了顯示電量百分比開(kāi)關(guān),則調(diào)用 updateShowPercent()方法在電池等級(jí)前添加電量百分比

updateShowPercent();

}

};

setClipChildren(false);

setClipToPadding(false);

Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);

}

②onDarkChanged

//修改百分比的字體顏色和電池等級(jí)的畫筆顏色和背景顏色

@Override

public void onDarkChanged(Rect area, float darkIntensity, int tint) {

float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;

mNonAdaptedSingleToneColor = mDualToneHandler.getSingleColor(intensity);

mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity);

mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity);

if (!mUseWallpaperTextColors) {

//添加電池充電動(dòng)畫

updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,

mNonAdaptedSingleToneColor);

}

}

③updateColors

//在電池等級(jí)前添加電量百分比

private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) {

/*Bug 1296708 add charge animation of batteryView*/

//添加電池充電動(dòng)畫

if (mDrawable != null) {

mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor);

}

if (mUnisocDrawable != null) {

mUnisocDrawable.setColors(foregroundColor, backgroundColor);

}

mTextColor = singleToneColor;

if (mBatteryPercentView != null) {

mBatteryPercentView.setTextColor(singleToneColor);

}

}

2、PhoneStatusBarView

private DarkReceiver mBattery;

@Override

public void onFinishInflate() {

//

mBattery = findViewById(R.id.battery);

mCutoutSpace = findViewById(R.id.cutout_space_view);

mCenterIconSpace = findViewById(R.id.centered_icon_area);

updateResources();

}

@Override

protected void onAttachedToWindow() {

super.onAttachedToWindow();

// Always have Battery meters in the status bar observe the dark/light modes.

//始終在狀態(tài)欄的電池儀表觀察暗/光模式。

Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);

if (updateOrientationAndCutout()) {

updateLayoutForCutout();

}

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);

mDisplayCutout = null;

}

七、電池狀態(tài)改變流程

1、BatteryControllerImpl

①init

@Override

public void init() {

//注冊(cè)廣播

registerReceiver();

if (!mHasReceivedBattery) {

// Get initial state. Relying on Sticky behavior until API for getting info.

Intent intent = mContext.registerReceiver(

null,

new IntentFilter(Intent.ACTION_BATTERY_CHANGED)

);

if (intent != null && !mHasReceivedBattery) {

onReceive(mContext, intent);

}

}

updatePowerSave();

updateEstimate();

}

②registerReceiver

private void registerReceiver() {

IntentFilter filter = new IntentFilter();

//添加廣播的方式接收

filter.addAction(Intent.ACTION_BATTERY_CHANGED);

filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);

/* UNISOC: Bug 1363779 battery icon shows '+' after switching from power saving mode to super power saving @{ */

filter.addAction(UnisocPowerManagerUtil.ACTION_POWEREX_SAVE_MODE_CHANGED);

/* @} */

filter.addAction(ACTION_LEVEL_TEST);

mBroadcastDispatcher.registerReceiver(this, filter);

}

③onReceive

@Override

public void onReceive(final Context context, Intent intent) {

final String action = intent.getAction();

//監(jiān)聽(tīng)到ACTION_BATTERY_CHANGED

if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {

if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;

mHasReceivedBattery = true;

mLevel = (int)(100f

* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)

/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));

mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;

final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,

BatteryManager.BATTERY_STATUS_UNKNOWN);

mCharged = status == BatteryManager.BATTERY_STATUS_FULL;

mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;

//遍歷回調(diào)監(jiān)聽(tīng),將狀態(tài)參數(shù)發(fā)送

fireBatteryLevelChanged();

}

....

}

④fireBatteryLevelChanged

protected final ArrayList

mChangeCallbacks = new ArrayList<>();

protected void fireBatteryLevelChanged() {

synchronized (mChangeCallbacks) {

final int N = mChangeCallbacks.size();

//遍歷回調(diào)監(jiān)聽(tīng),將狀態(tài)參數(shù)發(fā)送

for (int i = 0; i < N; i++) {

mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);

}

}

}

2、BatteryStateChangeCallback

interface BatteryStateChangeCallback {

default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {

}

default void onPowerSaveChanged(boolean isPowerSave) {

}

default void onReverseChanged(boolean isReverse, int level, String name) {

}

}

3、BatteryMeterView

BatteryMeterView實(shí)現(xiàn)了 BatteryStateChangeCallback,收到改變監(jiān)聽(tīng) onBatteryLevelChanged()

//實(shí)現(xiàn)了 BatteryStateChangeCallback

public class BatteryMeterView extends LinearLayout implements

BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {

.....

@Override

public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {

/*Bug 1296708 add charge animation of batteryView*/

if (mDrawable != null) {

//是否繪制充電中閃電形狀圖標(biāo)

mDrawable.setCharging(pluggedIn);

//根據(jù)當(dāng)前 level/100f 計(jì)算百分比繪制path

mDrawable.setBatteryLevel(level);

}

mCharging = pluggedIn;

mLevel = level;

updatePercentText();

}

....

}

八、NavigationBar導(dǎo)航欄模塊

Ⅰ創(chuàng)建導(dǎo)航欄文件

1、StatusBar

①makeStatusBarView

// ================================================================================

// Constructing the view

// ================================================================================

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {

....

//創(chuàng)建導(dǎo)航欄

createNavigationBar(result);

.....

②createNavigationBar

private final NavigationBarController mNavigationBarController;

// TODO(b/117478341): This was left such that CarStatusBar can override this method.

// Try to remove this.

protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {

//

mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);

...

}

2、NavigationBarController

①createNavigationBars

public void createNavigationBars(final boolean includeDefaultDisplay,

RegisterStatusBarResult result) {

Display[] displays = mDisplayManager.getDisplays();

for (Display display : displays) {

if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {

//

createNavigationBar(display, result);

}

}

}

②createNavigationBar

/**

* Adds a navigation bar on default display or an external display if the display supports

* system decorations.

*

* @param display the display to add navigation bar on.

*/

@VisibleForTesting

void createNavigationBar(Display display, RegisterStatusBarResult result) {

if (display == null) {

return;

}

final int displayId = display.getDisplayId();

final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;

final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();

try {

if (!wms.hasNavigationBar(displayId)) {

return;

}

} catch (RemoteException e) {

// Cannot get wms, just return with warning message.

Log.w(TAG, "Cannot get WindowManager.");

return;

}

final Context context = isOnDefaultDisplay

? mContext

: mContext.createDisplayContext(display);

//最終是通過(guò)NavigationBarFragment的create方法進(jìn)行創(chuàng)建

NavigationBarFragment.create(context, (tag, fragment) -> {

NavigationBarFragment navBar = (NavigationBarFragment) fragment;

3、NavigationBarFragment

①create

代碼做了兩件事:

1.創(chuàng)建navigationBarView 并且把navigationBarView添加到windowManager中。

2.創(chuàng)建NavigationBarFragment 替換navigation_bar_window的布局文件,改成navigation_bar

public static View create(Context context, FragmentListener listener) {

WindowManager.LayoutParams lp = new WindowManager.LayoutParams(

LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,

WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,

WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING

| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL

| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH

| WindowManager.LayoutParams.FLAG_SLIPPERY,

PixelFormat.TRANSLUCENT);

lp.token = new Binder();

lp.setTitle("NavigationBar" + context.getDisplayId());

lp.accessibilityTitle = context.getString(R.string.nav_bar);

lp.windowAnimations = 0;

lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;

View navigationBarView = LayoutInflater.from(context).inflate(

R.layout.navigation_bar_window, null);

if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);

if (navigationBarView == null) return null;

//創(chuàng)建NavigationBarFragment 替換navigation_bar_window的布局文件,改成navigation_bar

final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)

.create(NavigationBarFragment.class);

navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {

@Override

public void onViewAttachedToWindow(View v) {

final FragmentHostManager fragmentHost = FragmentHostManager.get(v);

//navigation_bar_frame是navigation_bar_window中NavigationBarFrame的ID

fragmentHost.getFragmentManager().beginTransaction()

.replace(R.id.navigation_bar_frame, fragment, TAG)

.commit();

fragmentHost.addTagListener(TAG, listener);

}

@Override

public void onViewDetachedFromWindow(View v) {

FragmentHostManager.removeAndDestroy(v);

navigationBarView.removeOnAttachStateChangeListener(this);

}

});

context.getSystemService(WindowManager.class).addView(navigationBarView, lp);

return navigationBarView;

}

③onCreateView

@Override

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,

Bundle savedInstanceState) {

//

return inflater.inflate(R.layout.navigation_bar, container, false);

}

4、navigation_bar_window.xml

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:id="@+id/navigation_bar_frame"

android:theme="@style/Theme.SystemUI"

android:layout_height="match_parent"

android:layout_width="match_parent">

5、navigation_bar.xml

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:layout_height="match_parent"

android:layout_width="match_parent"

android:background="@drawable/system_bar_background">

android:id="@+id/assist_hint_left"

android:layout_width="40dp"

android:layout_height="40dp"

android:layout_gravity="left|bottom"

android:rotation="270"

android:visibility="gone"/>

android:id="@+id/assist_hint_right"

android:layout_width="40dp"

android:layout_height="40dp"

android:layout_gravity="right|bottom"

android:rotation="180"

android:visibility="gone"/>

android:id="@+id/navigation_inflater"

android:layout_width="match_parent"

android:layout_height="match_parent" />

Ⅱ加載布局文件

6、NavigationBarInflaterView

①NavigationBarInflaterView(Context context, AttributeSet attrs)

public NavigationBarInflaterView(Context context, AttributeSet attrs) {

super(context, attrs);

createInflaters();

mOverviewProxyService = Dependency.get(OverviewProxyService.class);

mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);

/* UNISOC: add for bug 1071183,1134237 @{ */

mSupportDynamicBar = NavigationBarView.isSupportDynamicNavBar(context, mNavBarMode);

/* }@ */

}

②createInflaters

@VisibleForTesting

void createInflaters() {

mLayoutInflater = LayoutInflater.from(mContext);

Configuration landscape = new Configuration();

landscape.setTo(mContext.getResources().getConfiguration());

landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;

mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));

}

③onFinishInflate

@Override

protected void onFinishInflate() {

super.onFinishInflate();

//添加水平橫屏布局

inflateChildren();

//清空布局

clearViews();

inflateLayout(getDefaultLayout());

}

④inflateChildren

private void inflateChildren() {

removeAllViews();

//水平布局

mHorizontal = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout,

this /* root */, false /* attachToRoot */);

addView(mHorizontal);

//垂直布局

mVertical = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_vertical,

this /* root */, false /* attachToRoot */);

addView(mVertical);

updatealternativeorder();

}

⑤clearViews

private void clearViews() {

if (mButtonDispatchers != null) {

for (int i = 0; i < mButtonDispatchers.size(); i++) {

mButtonDispatchers.valueAt(i).clear();

}

}

clearAllChildren(mHorizontal.findViewById(R.id.nav_buttons));

clearAllChildren(mVertical.findViewById(R.id.nav_buttons));

}

private void clearAllChildren(ViewGroup group) {

for (int i = 0; i < group.getChildCount(); i++) {

((ViewGroup) group.getChildAt(i)).removeAllViews();

}

}

⑥inflateLayout

protected void inflateLayout(String newLayout) {

mCurrentLayout = newLayout;

if (newLayout == null) {

newLayout = getDefaultLayout();

}

String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);

if (sets.length != 3) {

Log.d(TAG, "Invalid layout.");

newLayout = getDefaultLayout();

sets = newLayout.split(GRAVITY_SEPARATOR, 3);

}

String[] start = sets[0].split(BUTTON_SEPARATOR);

String[] center = sets[1].split(BUTTON_SEPARATOR);

String[] end = sets[2].split(BUTTON_SEPARATOR);

// Inflate these in start to end order or accessibility traversal will be messed up.

inflateButtons(start, mHorizontal.findViewById(R.id.ends_group),

false /* landscape */, true /* start */);

inflateButtons(start, mVertical.findViewById(R.id.ends_group),

true /* landscape */, true /* start */);

inflateButtons(center, mHorizontal.findViewById(R.id.center_group),

false /* landscape */, false /* start */);

inflateButtons(center, mVertical.findViewById(R.id.center_group),

true /* landscape */, false /* start */);

addGravitySpacer(mHorizontal.findViewById(R.id.ends_group));

addGravitySpacer(mVertical.findViewById(R.id.ends_group));

inflateButtons(end, mHorizontal.findViewById(R.id.ends_group),

false /* landscape */, false /* start */);

inflateButtons(end, mVertical.findViewById(R.id.ends_group),

true /* landscape */, false /* start */);

updateButtonDispatchersCurrentView();

}

⑦inflateButton

private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,

boolean start) {

for (int i = 0; i < buttons.length; i++) {

inflateButton(buttons[i], parent, landscape, start);

}

}

@Nullable

protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,

boolean start) {

LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;

View v = createView(buttonSpec, parent, inflater);

if (v == null) return null;

v = applySize(v, buttonSpec, landscape, start);

parent.addView(v);

addToDispatchers(v);

View lastView = landscape ? mLastLandscape : mLastPortrait;

View accessibilityView = v;

if (v instanceof ReverseRelativeLayout) {

accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);

}

if (lastView != null) {

accessibilityView.setAccessibilityTraversalAfter(lastView.getId());

}

if (landscape) {

mLastLandscape = accessibilityView;

} else {

mLastPortrait = accessibilityView;

}

return v;

}

⑧getDefaultLayout

//導(dǎo)航欄顯示哪些控件是由getDefaultLayout來(lái)決定

protected String getDefaultLayout() {

/* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */

if (mSupportDynamicBar) {

return readLNavigationLayoutSettings();

}

/* @} */

final int defaultResource = QuickStepContract.isGesturalMode(mNavBarMode)

? R.string.config_navBarLayoutHandle

: mOverviewProxyService.shouldShowSwipeUpUI()

? R.string.config_navBarLayoutQuickstep

: R.string.config_navBarLayout;

return getContext().getString(defaultResource);

}

⑨createView

private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {

View v = null;

String button = extractButton(buttonSpec);

if (LEFT.equals(button)) {

button = extractButton(NAVSPACE);

} else if (RIGHT.equals(button)) {

button = extractButton(MENU_IME_ROTATE);

}

if (HOME.equals(button)) {

v = inflater.inflate(R.layout.home, parent, false);

} else if (BACK.equals(button)) {

v = inflater.inflate(R.layout.back, parent, false);

} else if (RECENT.equals(button)) {

v = inflater.inflate(R.layout.recent_apps, parent, false);

} else if (MENU_IME_ROTATE.equals(button)) {

v = inflater.inflate(R.layout.menu_ime, parent, false);

} else if (NAVSPACE.equals(button)) {

v = inflater.inflate(R.layout.nav_key_space, parent, false);

} else if (CLIPBOARD.equals(button)) {

v = inflater.inflate(R.layout.clipboard, parent, false);

} else if (CONTEXTUAL.equals(button)) {

v = inflater.inflate(R.layout.contextual, parent, false);

} else if (HOME_HANDLE.equals(button)) {

v = inflater.inflate(R.layout.home_handle, parent, false);

} else if (IME_SWITCHER.equals(button)) {

v = inflater.inflate(R.layout.ime_switcher, parent, false);

} else if (button.startsWith(KEY)) {

String uri = extractImage(button);

int code = extractKeycode(button);

v = inflater.inflate(R.layout.custom_key, parent, false);

((KeyButtonView) v).setCode(code);

if (uri != null) {

if (uri.contains(":")) {

((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));

} else if (uri.contains("/")) {

int index = uri.indexOf('/');

String pkg = uri.substring(0, index);

int id = Integer.parseInt(uri.substring(index + 1));

((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));

}

}

}

/* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */

else if (HIDE.equals(button)) {

v = inflater.inflate(R.layout.hide, parent, false);

} else if (PULL.equals(button)) {

v = inflater.inflate(R.layout.pull, parent, false);

/*UNISOC: Add for bug 902309 1146896 @{ */

} else if (SPACE_PLACE.equals(button)) {

v = inflater.inflate(R.layout.space, parent, false);

/* }@ */

} else {

return null;

}

/* @} */

return v;

}

7、home.xml、back.xml、recent_apps.xml

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:id="@+id/home"

android:layout_width="@dimen/navigation_key_width"

android:layout_height="match_parent"

android:layout_weight="0"

systemui:keyCode="3"

android:scaleType="center"

android:contentDescription="@string/accessibility_home"

android:paddingStart="@dimen/navigation_key_padding"

android:paddingEnd="@dimen/navigation_key_padding"

/>

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:id="@+id/back"

android:layout_width="@dimen/navigation_key_width"

android:layout_height="match_parent"

android:layout_weight="0"

systemui:keyCode="4"

android:scaleType="center"

android:contentDescription="@string/accessibility_back"

android:paddingStart="@dimen/navigation_key_padding"

android:paddingEnd="@dimen/navigation_key_padding"

/>

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:systemui="http://schemas.android.com/apk/res-auto"

android:id="@+id/recent_apps"

android:layout_width="@dimen/navigation_key_width"

android:layout_height="match_parent"

android:layout_weight="0"

android:scaleType="center"

android:contentDescription="@string/accessibility_recent"

android:paddingStart="@dimen/navigation_key_padding"

android:paddingEnd="@dimen/navigation_key_padding"

/>

8、config

left[.5W],back[1WC];home;recent[1WC],right[.5W]

back[1.7WC];home;contextual[1.7WC]

back[40AC];home_handle;ime_switcher[40AC]

第一、導(dǎo)航欄顯示哪些控件是由getDefaultLayout來(lái)決定。

left[.5W],back[1WC];home;recent[1WC],right[.5W]

一般情況下我們是home,recent,back這三個(gè)鍵,如果你需要加其他的就在這個(gè)配置文件夾。同時(shí)在createView添加對(duì)應(yīng)的布局文件。

第二、createView方法創(chuàng)建對(duì)應(yīng)的布局文件,并且添加到導(dǎo)航欄中。

那么我們現(xiàn)在布局文件都添加完成了,但是你會(huì)發(fā)現(xiàn)在NavigationBarInflaterView沒(méi)有對(duì)資源文件添加的代碼已經(jīng)控件點(diǎn)擊觸摸事件處理邏輯。那么這兩部分代碼在哪里呢?

答案是:

1.NavigationBarView 完成資源文件添加。

2.NavigationBarFragment 添加點(diǎn)擊事件和觸摸事件的處理邏輯。

Ⅲ資源文件添加

1、NavigationBarView

①NavigationBarView(Context context, AttributeSet attrs)

public NavigationBarView(Context context, AttributeSet attrs) {

super(context, attrs);

mIsVertical = false;

mLongClickableAccessibilityButton = false;

mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);

//UNISOC: Add for bug 1242615

mOldNavBarMode = mNavBarMode;

/* UNISCO: Bug 1072090,1116092 new feature of dynamic navigationbar @{*/

mSupportDynamicBar = isSupportDynamicNavBar(mContext, mNavBarMode);

mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);

/* }@ */

/* UNISOC: Modify for bug963304 {@ */

mStatusBarManager = (StatusBarManager) mContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE);

/* @} */

boolean isGesturalMode = isGesturalMode(mNavBarMode);

mSysUiFlagContainer = Dependency.get(SysUiState.class);

mPluginManager = Dependency.get(PluginManager.class);

// Set up the context group of buttons

mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);

final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,

R.drawable.ic_ime_switcher_default);

final RotationContextButton rotateSuggestionButton = new RotationContextButton(

R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button);

final ContextualButton accessibilityButton =

new ContextualButton(R.id.accessibility_button,

R.drawable.ic_sysbar_accessibility_button);

mContextualButtonGroup.addButton(imeSwitcherButton);

if (!isGesturalMode) {

mContextualButtonGroup.addButton(rotateSuggestionButton);

}

mContextualButtonGroup.addButton(accessibilityButton);

mOverviewProxyService = Dependency.get(OverviewProxyService.class);

mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);

mFloatingRotationButton = new FloatingRotationButton(context);

mRotationButtonController = new RotationButtonController(context,

R.style.RotateButtonCCWStart90,

isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);

mConfiguration = new Configuration();

mTmpLastConfiguration = new Configuration();

mConfiguration.updateFrom(context.getResources().getConfiguration());

mScreenPinningNotify = new ScreenPinningNotify(mContext);

mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));

mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));

mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));

mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));

mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));

mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);

mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);

mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);

mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);

mDeadZone = new DeadZone(this);

/* UNISOC: Bug 1072090 new feature of dynamic navigationbar @{ */

if(mSupportDynamicBar){

mStatusBar = Dependency.get(StatusBar.class);

mButtonDispatchers.put(R.id.hide, new ButtonDispatcher(R.id.hide));

mButtonDispatchers.put(R.id.pull, new ButtonDispatcher(R.id.pull));

}

mNavColorSampleMargin = getResources()

.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);

//updateStates更新?tīng)顟B(tài)

mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService,

mSysUiFlagContainer, mPluginManager, this::updateStates);

mRegionSamplingHelper = new RegionSamplingHelper(this,

new RegionSamplingHelper.SamplingCallback() {

@Override

public void onRegionDarknessChanged(boolean isRegionDark) {

getLightTransitionsController().setIconsDark(!isRegionDark ,

true /* animate */);

}

@Override

public Rect getSampledRegion(View sampledView) {

if (mOrientedHandleSamplingRegion != null) {

return mOrientedHandleSamplingRegion;

}

updateSamplingRect();

return mSamplingBounds;

}

@Override

public boolean isSamplingEnabled() {

return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);

}

});

}

②updateStates

public void updateStates() {

final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI();

if (mNavigationInflaterView != null) {

// Reinflate the navbar if needed, no-op unless the swipe up state changes

mNavigationInflaterView.onLikelyDefaultLayoutChange();

}

updateSlippery();

//初始化加載資源,主要是圖片

reloadNavIcons();

updateNavButtonIcons();

setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());

WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI);

getHomeButton().setAccessibilityDelegate(

showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);

}

③reloadNavIcons

//初始化加載資源,主要是圖片

private void reloadNavIcons() {

updateIcons(Configuration.EMPTY);

}

Ⅳ添加點(diǎn)擊事件和觸摸事件的處理邏輯。

1、NavigationBarFragment

①onViewCreated

@Override

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

mNavigationBarView = (NavigationBarView) view;

final Display display = view.getDisplay();

// It may not have display when running unit test.

if (display != null) {

mDisplayId = display.getDisplayId();

mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;

}

mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());

mNavigationBarView.setDisabledFlags(mDisabledFlags1);

mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);

mNavigationBarView.setOnTouchListener(this::onNavigationTouch);

if (savedInstanceState != null) {

mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);

}

mNavigationBarView.setNavigationIconHints(mNavigationIconHints);

mNavigationBarView.setWindowVisible(isNavBarWindowVisible());

添加home,recent觸摸事件回調(diào)

prepareNavigationBarView();

checkNavBarModes();

IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);

filter.addAction(Intent.ACTION_SCREEN_ON);

filter.addAction(Intent.ACTION_USER_SWITCHED);

//UNISOC: Add for bug 1274603

filter.addAction(Intent.ACTION_USER_PRESENT);

filter.addAction(PowerManagerEx.ACTION_POWEREX_SAVE_MODE_CHANGED);

mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,

Handler.getMain(), UserHandle.ALL);

notifyNavigationBarScreenOn();

mOverviewProxyService.addCallback(mOverviewProxyListener);

updateSystemUiStateFlags(-1);

......

}

②prepareNavigationBarView

setOnClickListener,setOnTouchListener,setLongClickable,setOnLongClickListener就是給對(duì)應(yīng)的控件添加控制代碼

private void prepareNavigationBarView() {

mNavigationBarView.reorient();

ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();

recentsButton.setOnClickListener(this::onRecentsClick);

recentsButton.setOnTouchListener(this::onRecentsTouch);

recentsButton.setLongClickable(true);

recentsButton.setOnLongClickListener(this::onLongPressBackRecents);

ButtonDispatcher backButton = mNavigationBarView.getBackButton();

backButton.setLongClickable(true);

ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();

homeButton.setOnTouchListener(this::onHomeTouch);

homeButton.setOnLongClickListener(this::onHomeLongClick);

ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();

accessibilityButton.setOnClickListener(this::onAccessibilityClick);

accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);

updateAccessibilityServicesState(mAccessibilityManager);

updateScreenPinningGestures();

}

③onHomeTouch

private boolean onHomeTouch(View v, MotionEvent event) {

if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {

return true;

}

// If an incoming call is ringing, HOME is totally disabled.

// (The user is already on the InCallUI at this point,

// and his ONLY options are to answer or reject the call.)

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

mHomeBlockedThisTouch = false;

TelecomManager telecomManager =

getContext().getSystemService(TelecomManager.class);

if (telecomManager != null && telecomManager.isRinging()) {

if (mStatusBarLazy.get().isKeyguardShowing()) {

Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +

"No heads up");

mHomeBlockedThisTouch = true;

return true;

}

}

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

mStatusBarLazy.get().awakenDreams();

break;

}

return false;

}

④onHomeLongClick

@VisibleForTesting

boolean onHomeLongClick(View v) {

if (!mNavigationBarView.isRecentsButtonVisible()

&& ActivityManagerWrapper.getInstance().isScreenPinningActive()) {

return onLongPressBackHome(v);

}

if (shouldDisableNavbarGestures()) {

return false;

}

mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);

/* UNISOC: Bug 1074234, 970184, Super power feature @{ */

if (UnisocPowerManagerUtil.isSuperPower()) {

Log.d(TAG, "onHomeLongClick SUPPORT_SUPER_POWER_SAVE ignore!");

return false;

}

/* @} */

mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS);

Bundle args = new Bundle();

args.putInt(

AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS);

mAssistManager.startAssist(args);

mStatusBarLazy.get().awakenDreams();

if (mNavigationBarView != null) {

mNavigationBarView.abortCurrentGesture();

}

return true;

}

九、Recents模塊

packages/apps/SystemUI/src/com/android/systemui/recents/

1、Recents

public class Recents extends SystemUI implements CommandQueue.Callbacks {

private final RecentsImplementation mImpl;

private final CommandQueue mCommandQueue;

public Recents(Context context, RecentsImplementation impl, CommandQueue commandQueue) {

super(context);

mImpl = impl;

mCommandQueue = commandQueue;

}

//由前面SystemUI的啟動(dòng)可知,調(diào)用的都為子類的start方法

//在start方法添加了回調(diào)和調(diào)用了RecentsImplementation的onStart方法,下面跟進(jìn)RecentsImplementation

@Override

public void start() {

mCommandQueue.addCallback(this);

mImpl.onStart(mContext);

}

...

}

2、RecentsImplementation

public interface RecentsImplementation {

//可以看到該接口中方法皆為default修飾的方法,但均未寫函數(shù)體,具體實(shí)現(xiàn)由子類實(shí)現(xiàn),于是跟進(jìn)OverviewProxyRecentsImpl類

default void onStart(Context context) {}

default void onBootCompleted() {}

default void onAppTransitionFinished() {}

default void onConfigurationChanged(Configuration newConfig) {}

default void preloadRecentApps() {}

default void cancelPreloadRecentApps() {}

default void showRecentApps(boolean triggeredFromAltTab) {}

default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}

default void toggleRecentApps() {}

default void growRecents() {}

default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,

int metricsDockAction) {

return false;

}

default void dump(PrintWriter pw) {}

}

3、OverviewProxyRecentsImpl

/**

* An implementation of the Recents interface which proxies to the OverviewProxyService.

*/

@Singleton

public class OverviewProxyRecentsImpl implements RecentsImplementation {

private final static String TAG = "OverviewProxyRecentsImpl";

@Nullable

private final Lazy mStatusBarLazy;

private final Optional mDividerOptional;

private Context mContext;

private Handler mHandler;

private TrustManager mTrustManager;

private OverviewProxyService mOverviewProxyService;

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")

@Inject

public OverviewProxyRecentsImpl(Optional> statusBarLazy,

Optional dividerOptional) {

mStatusBarLazy = statusBarLazy.orElse(null);

mDividerOptional = dividerOptional;

}

//可見(jiàn)之前調(diào)用的onStart()方法具體是調(diào)用的該子類的重寫方法

@Override

public void onStart(Context context) {

mContext = context;

mHandler = new Handler();

mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);

mOverviewProxyService = Dependency.get(OverviewProxyService.class);

}

@Override

public void toggleRecentApps() {

// If connected to launcher service, let it handle the toggle logic

IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();

if (overviewProxy != null) {

final Runnable toggleRecents = () -> {

try {

if (mOverviewProxyService.getProxy() != null) {

//可以看到顯示最近的app的方法都是通過(guò)得到OverviewProxyService的代理,之后對(duì)其操作,

//接著跟進(jìn)OverviewProxyService類查看overviewProxy的由來(lái)

mOverviewProxyService.getProxy().onOverviewToggle();

mOverviewProxyService.notifyToggleRecentApps();

}

} catch (RemoteException e) {

Log.e(TAG, "Cannot send toggle recents through proxy service.", e);

}

};

// Preload only if device for current user is unlocked

if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {

mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {

// Flush trustmanager before checking device locked per user

mTrustManager.reportKeyguardShowingChanged();

mHandler.post(toggleRecents);

}, null, true /* dismissShade */, false /* afterKeyguardGone */,

true /* deferred */);

} else {

toggleRecents.run();

}

return;

} else {

// Do nothing

}

}

}

4、OverviewProxyService

①getProxy

private IOverviewProxy mOverviewProxy;

//可以看到getProxy()方法返回的是一個(gè)mOverviewProxy:IOverviewProxy對(duì)象引用,接下來(lái)查看其具體指向哪個(gè)對(duì)象

public IOverviewProxy getProxy() {

return mOverviewProxy;

}

②ServiceConnection

//通過(guò)mOverviewServiceConnection應(yīng)該可以發(fā)現(xiàn),應(yīng)該是bindService中的一個(gè)參數(shù)。

private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

if (SysUiState.DEBUG) {

Log.d(TAG_OPS, "Overview proxy service connected");

}

mConnectionBackoffAttempts = 0;

mHandler.removeCallbacks(mDeferredConnectionCallback);

try {

service.linkToDeath(mOverviewServiceDeathRcpt, 0);

} catch (RemoteException e) {

// Failed to link to death (process may have died between binding and connecting),

// just unbind the service for now and retry again

Log.e(TAG_OPS, "Lost connection to launcher service", e);

disconnectFromLauncherService();

retryConnectionWithBackoff();

return;

}

mCurrentBoundedUserId = getCurrentUserId();

//mOverviewProxy指向了IOverviewProxy的一個(gè)遠(yuǎn)程代理

mOverviewProxy = IOverviewProxy.Stub.asInterface(service);

Bundle params = new Bundle();

params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());

params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);

params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);

try {

mOverviewProxy.onInitialize(params);

} catch (RemoteException e) {

mCurrentBoundedUserId = -1;

Log.e(TAG_OPS, "Failed to call onInitialize()", e);

}

dispatchNavButtonBounds();

......

}

③internalConnectToCurrentUser

private void internalConnectToCurrentUser() {

disconnectFromLauncherService();

// If user has not setup yet or already connected, do not try to connect

if (!isEnabled()) {

Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled());

return;

}

mHandler.removeCallbacks(mConnectionRunnable);

//ACTION_QUICKSTEP,這個(gè)action就是Launcher中的

Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP);

if (mRecentsComponentName != null) {

launcherServiceIntent.setPackage(mRecentsComponentName.getPackageName());

}

try {

//傳入的intent為launcherServiceIntent,其參數(shù)為ACTION_QUICKSTEP,查看定義這個(gè)action就是Launcher中的

mBound = mContext.bindServiceAsUser(launcherServiceIntent,

mOverviewServiceConnection,

Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,

UserHandle.of(getCurrentUserId()));

} catch (SecurityException e) {

Log.e(TAG_OPS, "Unable to bind because of security error", e);

} catch (IllegalArgumentException e) {

Log.e(TAG_OPS, "Unable to bind because of illegal argument error", e);

}

if (mBound) {

// Ensure that connection has been established even if it thinks it is bound

mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);

} else {

// Retry after exponential backoff timeout

retryConnectionWithBackoff();

}

}

5、AndroidManifest.xml

代碼位于packages/apps/Launcher3/quickstep/AndroidManifest.xml

//TouchInteractionService

android:name="com.android.quickstep.TouchInteractionService"

android:permission="android.permission.STATUS_BAR_SERVICE"

android:directBootAware="true" >

6、TouchInteractionService

private OverviewCommandHelper mOverviewCommandHelper;

private final IBinder mMyBinder = new IOverviewProxy.Stub() {

@BinderThread

public void onInitialize(Bundle bundle) {

ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(

bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));

MAIN_EXECUTOR.execute(() -> {

SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);

TouchInteractionService.this.initInputMonitor();

preloadOverview(true /* fromInit */);

});

sIsInitialized = true;

}

@BinderThread

@Override

//也就是說(shuō),上面OverviewProxyRecentsImpl調(diào)用的mOverviewProxyService.getProxy().onOverviewToggle()

//其實(shí)是調(diào)用TouchInteractionService中的mMyBinder的實(shí)現(xiàn),mMyBinder就是IOverviewProxy的一個(gè)遠(yuǎn)程代理。

public void onOverviewToggle() {

TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");

mOverviewCommandHelper.onOverviewToggle();

}

....

}

7、OverviewCommandHelper

①onOverviewToggle

//也就是說(shuō),上面OverviewProxyRecentsImpl調(diào)用的mOverviewProxyService.getProxy().onOverviewToggle()

@BinderThread

public void onOverviewToggle() {

// If currently screen pinning, do not enter overview

if (mDeviceState.isScreenPinningActive()) {

return;

}

ActivityManagerWrapper.getInstance()

.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);

//可以看到這里主要啟動(dòng)了RecentsActivityCommand線程

MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());

}

public RecentsActivityCommand() {

mActivityInterface = mOverviewComponentObserver.getActivityInterface();

mCreateTime = SystemClock.elapsedRealtime();

mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,

RecentsModel.getRunningTaskId(), mDeviceState);

// Preload the plan

mRecentsModel.getTasks(null);

}

②run()

最終實(shí)現(xiàn)了一個(gè)從點(diǎn)擊switch到Launcher的RecentsActivity啟動(dòng)的過(guò)程

@Override

public void run() {

long elapsedTime = mCreateTime - mLastToggleTime;

mLastToggleTime = mCreateTime;

if (handleCommand(elapsedTime)) {

// Command already handled.

return;

}

if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {

// If successfully switched, then return

return;

}

// Otherwise, start overview.

mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);

mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),

new RemoteAnimationProvider() {

@Override

public AnimatorSet createWindowAnimation(

RemoteAnimationTargetCompat[] appTargets,

RemoteAnimationTargetCompat[] wallpaperTargets) {

return RecentsActivityCommand.this.createWindowAnimation(appTargets,

wallpaperTargets);

}

}, mContext, MAIN_EXECUTOR.getHandler(),

mAnimationProvider.getRecentsLaunchDuration());

}

十、VolumeUI模塊

這個(gè)模塊使用MVP架構(gòu)完成設(shè)計(jì)的

通過(guò) SystemUI之StatusBar創(chuàng)建 可知,VolumeUI 的入口為 VolumeUI#start()

Ⅰ MVP架構(gòu)綁定流程

1、VolumeUI

①start()

@Override

public void start() {

boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);

boolean enableSafetyWarning =

mContext.getResources().getBoolean(R.bool.enable_safety_warning);

mEnabled = enableVolumeUi || enableSafetyWarning;

if (!mEnabled) return;

//mVolumeComponent從名字可以看出,它代表 VolumeUI 組件,通過(guò)它可以創(chuàng)建整個(gè)MVP。

mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);

//register啟動(dòng)VolumeUI的功能

setDefaultVolumeController();

}

②VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent)

//VolumeUI 啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè) VolumeDialogComponent 對(duì)象

@Inject

public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {

super(context);

mVolumeComponent = volumeDialogComponent;

}

③setDefaultVolumeController

private void setDefaultVolumeController() {

DndTile.setVisible(mContext, true);

if (D.BUG) Log.d(TAG, "Registering default volume controller");

//VolumeDialogComponent 對(duì)象創(chuàng)建完成后,就會(huì)調(diào)用它的register()方法啟動(dòng) VolumeUI 功能。

//register啟動(dòng)VolumeUI的功能

//它其實(shí)就是關(guān)聯(lián) Presenter 層和 Model 層。

mVolumeComponent.register();

}

④VolumeDialogComponent的register方法

private final VolumeDialogControllerImpl mController;

@Override

public void register() {

mController.register();

DndTile.setCombinedIcon(mContext, true);

}

⑤VolumeDialogControllerImpl的register方法

public void register() {

setVolumeController();

setVolumePolicy(mVolumePolicy);

showDndTile(mShowDndTile);

try {

mMediaSessions.init();

} catch (SecurityException e) {

Log.w(TAG, "No access to media sessions", e);

}

}

2、VolumeDialogComponent

①VolumeDialogComponent 的構(gòu)造函數(shù)

//接口,實(shí)現(xiàn)類為VolumeDialogImpl

private VolumeDialog mDialog;

@Inject

public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator,

VolumeDialogControllerImpl volumeDialogController) {

mContext = context;

mKeyguardViewMediator = keyguardViewMediator;

mController = volumeDialogController;

mController.setUserActivityListener(this);

// Allow plugins to reference the VolumeDialogController.

Dependency.get(PluginDependencyProvider.class)

.allowPluginDependency(VolumeDialogController.class);

Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)

.withPlugin(VolumeDialog.class)

//VolumeDialogComponent 通過(guò) createDefault() 創(chuàng)建 VolumeDialogImpl 對(duì)象,它代表 View 層

.withDefault(this::createDefault)

.withCallback(dialog -> {

if (mDialog != null) {

mDialog.destroy();

}

mDialog = dialog;

//然后通過(guò)init() 進(jìn)行了初始化。

mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);

}).build();

applyConfiguration();

Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,

VOLUME_SILENT_DO_NOT_DISTURB);

}

②createDefault()

protected VolumeDialog createDefault() {

VolumeDialogImpl impl = new VolumeDialogImpl(mContext);

impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);

impl.setAutomute(true);

impl.setSilentMode(false);

return impl;

}

2、VolumeDialogImpl---View層

①init(int windowType, Callback callback)

public void init(int windowType, Callback callback) {

initDialog();

mAccessibility.init();

//向 VolumeDialogControllerImpl (Presenter層) 注冊(cè)一個(gè)回調(diào)

//也就是 View 層與 Presenter 層建立關(guān)聯(lián),從而可以通過(guò) Presenter 層控制 View 層。

mController.addCallback(mControllerCallbackH, mHandler);

mController.getState();

Dependency.get(ConfigurationController.class).addCallback(this);

}

3、VolumeDialogControllerImpl---P層

①register

//進(jìn)行AudioManager的關(guān)聯(lián),也就是presenter層和model層的關(guān)聯(lián)

public void register() {

//進(jìn)行AudioManager的關(guān)聯(lián)

setVolumeController();

setVolumePolicy(mVolumePolicy);

showDndTile(mShowDndTile);

try {

mMediaSessions.init();

} catch (SecurityException e) {

Log.w(TAG, "No access to media sessions", e);

}

}

②setVolumeController()

private AudioManager mAudio;

protected final VC mVolumeController = new VC();

protected void setVolumeController() {

try {

mAudio.setVolumeController(mVolumeController);

} catch (SecurityException e) {

Log.w(TAG, "Unable to set the volume controller", e);

return;

}

}

Ⅱ 按下 Power 鍵后,VolumeUI 是如何顯示UI的

由于 VolumeDialogControllerImpl 向AudioManager注冊(cè)了回調(diào),當(dāng)按下音量鍵調(diào)整了音量后,VolumeDialogControllerImpl 就會(huì)收到回調(diào)

1、VC(VolumeDialogControllerImpl內(nèi)部類)

private final W mWorker;

private final class VC extends IVolumeController.Stub {

private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";

......

@Override

public void volumeChanged(int streamType, int flags) throws RemoteException {

if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)

+ " " + Util.audioManagerFlagsToString(flags));

if (mDestroyed) return;

//mWorker為繼承于Handler的內(nèi)部final類,根據(jù)收到的消息不同處理

mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();

}

..........

}

2、W(VolumeDialogControllerImpl內(nèi)部類)

①handleMessage

private final class W extends Handler {

private static final int VOLUME_CHANGED = 1;

private static final int DISMISS_REQUESTED = 2;

.....

W(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

//當(dāng)消息為VOLUME_CHANGED時(shí),調(diào)用onVolumeChangedW方法

case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;

case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;

case GET_STATE: onGetStateW(); break;

case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;

case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;

case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;

case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;

case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;

case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;

case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;

case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;

case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;

case USER_ACTIVITY: onUserActivityW(); break;

case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;

case GET_CAPTIONS_COMPONENT_STATE:

onGetCaptionsComponentStateW((Boolean) msg.obj); break;

case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);

}

}

}

②onVolumeChangedW

boolean onVolumeChangedW(int stream, int flags) {

//根據(jù) flags 決定要執(zhí)行哪個(gè)回調(diào),如果要顯示UI,就會(huì)回調(diào) onShowRequested()

final boolean showUI = shouldShowUI(flags);

final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;

final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;

final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;

boolean changed = false;

if (showUI) {

changed |= updateActiveStreamW(stream);

}

int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);

changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);

changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);

if (changed) {

mCallbacks.onStateChanged(mState);

}

//這個(gè)回調(diào)當(dāng)然是由 View 層實(shí)現(xiàn)的,也就是在VolumeDialogImpl中調(diào)用

if (showUI) {

mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);

}

if (showVibrateHint) {

mCallbacks.onShowVibrateHint();

}

if (showSilentHint) {

mCallbacks.onShowSilentHint();

}

if (changed && fromKey) {

Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);

}

return changed;

}

3、VolumeDialogImpl

①onShowRequested

public void init(int windowType, Callback callback) {

initDialog();

mAccessibility.init();

//初始化的時(shí)候添加回調(diào)addCallback

mController.addCallback(mControllerCallbackH, mHandler);

mController.getState();

Dependency.get(ConfigurationController.class).addCallback(this);

}

//mControllerCallbackH

private final VolumeDialogController.Callbacks mControllerCallbackH

= new VolumeDialogController.Callbacks() {

@Override

public void onShowRequested(int reason) {

//

showH(reason);

}

}

②showH

private void showH(int reason) {

if (D.BUG) Log.d(TAG, "showH r=" + Events.SHOW_REASONS[reason]);

mHandler.removeMessages(H.SHOW);

mHandler.removeMessages(H.DISMISS);

rescheduleTimeoutH();

/* UNISOC: Modify for bug1347675,1384445 @{ */

Configuration config = mContext.getResources().getConfiguration();

boolean orientationPortrait = config.orientation == ORIENTATION_PORTRAIT;

if ((mConfigChanged || (mOrientationPortrait != orientationPortrait)) && !mDialog.isShowing()) {

initDialog(); // resets mShowing to false

mConfigurableTexts.update();

mConfigChanged = false;

mOrientationPortrait = orientationPortrait;

}

/* @} */

initSettingsH();

mShowing = true;

mIsAnimatingDismiss = false;

mDialog.show();

Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());

mController.notifyVisible(true);

mController.getCaptionsComponentState(false);

checkODICaptionsTooltip(false);

}

ⅢAudioService對(duì)音量鍵處理流程

1、PhoneWindowManager

①dispatchDirectAudioEvent

// pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,

// KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE

private void dispatchDirectAudioEvent(KeyEvent event) {

// When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR

// or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.

HdmiControlManager hdmiControlManager = getHdmiControlManager();

if (null != hdmiControlManager

&& !hdmiControlManager.getSystemAudioMode()

&& shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {

HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();

if (audioSystemClient != null) {

audioSystemClient.sendKeyEvent(

event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);

return;

}

}

try {

//這里通過(guò)AIDL獲取IAudioService的實(shí)例

getAudioService().handleVolumeKey(event, mUseTvRouting,

mContext.getOpPackageName(), TAG);

} catch (Exception e) {

Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"

+ event, e);

}

}

②getAudioService()

import android.media.IAudioService;

static IAudioService getAudioService() {

IAudioService audioService = IAudioService.Stub.asInterface(

ServiceManager.checkService(Context.AUDIO_SERVICE));

if (audioService == null) {

Log.w(TAG, "Unable to find IAudioService interface.");

}

return audioService;

}

這里是直接執(zhí)行了音頻鍵的操作,通過(guò)Binder獲取到了AudioService的實(shí)例,去調(diào)用了handleVolumeKey方法,參數(shù)含義如下:

按鍵類型 Audio Service操作類型 含義 KEYCODE_VOLUME_UP AudioManager.ADJUST_RAISE 音量加 KEYCODE_VOLUME_DOWN AudioManager.ADJUST_LOWER 音量減 KEYCODE_VOLUME_MUTE AudioManager.ADJUST_TOGGLE_MUTE 改變靜音狀態(tài)

2、AudioService

①handleVolumeKey

//AudioService繼承了IAudioService

public class AudioService extends IAudioService.Stub

implements AccessibilityManager.TouchExplorationStateChangeListener,

AccessibilityManager.AccessibilityServicesStateChangeListener {

// pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,

// KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE

public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,

@NonNull String callingPackage, @NonNull String caller) {

int keyEventMode = VOL_ADJUST_NORMAL;

if (isOnTv) {

if (event.getAction() == KeyEvent.ACTION_DOWN) {

keyEventMode = VOL_ADJUST_START;

} else { // may catch more than ACTION_UP, but will end vol adjustement

// the vol key is either released (ACTION_UP), or multiple keys are pressed

// (ACTION_MULTIPLE) and we don't know what to do for volume control on CEC, end

// the repeated volume adjustement

keyEventMode = VOL_ADJUST_END;

}

} else if (event.getAction() != KeyEvent.ACTION_DOWN) {

return;

}

int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND

| AudioManager.FLAG_FROM_KEY;

switch (event.getKeyCode()) {

case KeyEvent.KEYCODE_VOLUME_UP:

//在按鍵的處理過(guò)程中,并沒(méi)有將相應(yīng)的code傳遞給AudioService,

//而是使用了相關(guān)的定義,將KEYCODE_VOLUME_UP等操作轉(zhuǎn)化為了ADJUST_RAISE等。

//而flag存儲(chǔ)了一些對(duì)音量的要求或者信息吧,這個(gè)也很重要。

adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,

AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,

Binder.getCallingUid(), true, keyEventMode);

break;

case KeyEvent.KEYCODE_VOLUME_DOWN:

adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,

AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,

Binder.getCallingUid(), true, keyEventMode);

break;

case KeyEvent.KEYCODE_VOLUME_MUTE:

if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {

adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,

AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,

Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);

}

break;

default:

Log.e(TAG, "Invalid key code " + event.getKeyCode() + " sent by " + callingPackage);

return; // not needed but added if code gets added below this switch statement

}

}

}

②adjustSuggestedStreamVolume

private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,

String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,

int keyEventMode) {

if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType

+ ", flags=" + flags + ", caller=" + caller

+ ", volControlStream=" + mVolumeControlStream

+ ", userSelect=" + mUserSelectedVolumeControlStream);

if (direction != AudioManager.ADJUST_SAME) {

sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_SUGG_VOL, suggestedStreamType,

direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)

.append("/").append(caller).append(" uid:").append(uid).toString()));

}

boolean hasExternalVolumeController = notifyExternalVolumeController(direction);

new MediaMetrics.Item(mMetricsId + "adjustSuggestedStreamVolume")

.setUid(Binder.getCallingUid())

.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage)

.set(MediaMetrics.Property.CLIENT_NAME, caller)

.set(MediaMetrics.Property.DIRECTION, direction > 0

? MediaMetrics.Value.UP : MediaMetrics.Value.DOWN)

.set(MediaMetrics.Property.EXTERNAL, hasExternalVolumeController

? MediaMetrics.Value.YES : MediaMetrics.Value.NO)

.set(MediaMetrics.Property.FLAGS, flags)

.record();

if (hasExternalVolumeController) {

return;

}

final int streamType;

synchronized (mForceControlStreamLock) {

// Request lock in case mVolumeControlStream is changed by other thread.

if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1

streamType = mVolumeControlStream;

} else {

// 這里獲取到,可能是活動(dòng)狀態(tài)的音頻流,但是不確定,還有待進(jìn)一步確認(rèn)

final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);

final boolean activeForReal;

if (maybeActiveStreamType == AudioSystem.STREAM_RING

|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {

activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);

} else {

activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);

}

if (activeForReal || mVolumeControlStream == -1) {

streamType = maybeActiveStreamType;

} else {

// activeForReal為false并且mVolumeControlStream不為-1

// 表示用戶點(diǎn)擊了音量進(jìn)度條,這時(shí)候要操作修改的流類型為mVolumeControlStream對(duì)應(yīng)的流類型

streamType = mVolumeControlStream;

}

}

}

final boolean isMute = isMuteAdjust(direction);

// 確保我們獲取到的流類型是有效的

ensureValidStreamType(streamType);

// 將我們獲取到的流,進(jìn)行流映射,拿到最終需要操作的流類型

final int resolvedStream = mStreamVolumeAlias[streamType];

// Play sounds on STREAM_RING only.

if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&

resolvedStream != AudioSystem.STREAM_RING) {

flags &= ~AudioManager.FLAG_PLAY_SOUND;

}

// For notifications/ring, show the ui before making any adjustments

// Don't suppress mute/unmute requests

// Don't suppress adjustments for single volume device

// 通知和響鈴,調(diào)整音量之前先顯示UI。

if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)

&& !mIsSingleVolume) {

direction = 0;

flags &= ~AudioManager.FLAG_PLAY_SOUND;

flags &= ~AudioManager.FLAG_VIBRATE;

if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");

}

// 這里設(shè)置音量

adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,

hasModifyAudioSettings, keyEventMode);

}

suppressAdjustment:字面意思為抑制調(diào)整,為什么抑制調(diào)整呢,說(shuō)白了,當(dāng)我們沒(méi)有顯示音量的UI進(jìn)度條的時(shí)候,不管我們是加音量還是減音量(注意:靜音和解靜音除外),這個(gè)時(shí)候都是先顯示音量條,而不去改變音量的大小。所以當(dāng)這個(gè)方法返回true的時(shí)候, direction = 0,這里direction為0就表示我們的操作為ADJUST_SAME,大家可以在AudioManager里面查看ADJUST_SAME的注釋就知道這個(gè)操作表示只彈出UI但是不調(diào)整音量大小。

③adjustStreamVolume

protected void adjustStreamVolume(int streamType, int direction, int flags,

String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,

int keyEventMode) {

//mUseFixedVolume表示使用固定音量,我們無(wú)法修改音量

if (mUseFixedVolume) {

return;

}

if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction

+ ", flags=" + flags + ", caller=" + caller);

ensureValidDirection(direction);

ensureValidStreamType(streamType);

boolean isMuteAdjust = isMuteAdjust(direction);

if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {

return;

}

// If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure

// that the calling app have the MODIFY_PHONE_STATE permission.

if (isMuteAdjust &&

(streamType == AudioSystem.STREAM_VOICE_CALL ||

streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&

mContext.checkCallingOrSelfPermission(

android.Manifest.permission.MODIFY_PHONE_STATE)

!= PackageManager.PERMISSION_GRANTED) {

Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="

+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());

return;

}

// If the stream is STREAM_ASSISTANT,

// make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.

if (streamType == AudioSystem.STREAM_ASSISTANT &&

mContext.checkCallingOrSelfPermission(

android.Manifest.permission.MODIFY_AUDIO_ROUTING)

!= PackageManager.PERMISSION_GRANTED) {

Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="

+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());

return;

}

// use stream type alias here so that streams with same alias have the same behavior,

// including with regard to silent mode control (e.g the use of STREAM_RING below and in

// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)

//進(jìn)行音頻流的映射,拿到映射后的音頻流

int streamTypeAlias = mStreamVolumeAlias[streamType];

//mStreamStates是一個(gè)存儲(chǔ)VolumeStreamState類型的數(shù)組,保存著每個(gè)音頻流的狀態(tài)。

//VolumeStreamState是AudioService的一個(gè)內(nèi)部類,里面保存單個(gè)音頻流的所有信息,比如流類型,音量大小,mute狀態(tài)等。

//并且相同的流類型,在不同的設(shè)備,大小也是不一樣的(比如耳機(jī)和揚(yáng)聲器,媒體音量大小是不一樣的)

//這也是在VolumeStreamState里面去維護(hù)的。

VolumeStreamState streamState = mStreamStates[streamTypeAlias];

final int device = getDeviceForStream(streamTypeAlias);

int aliasIndex = streamState.getIndex(device);

boolean adjustVolume = true;

int step;

// skip a2dp absolute volume control request when the device

// is not an a2dp device

if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)

&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {

return;

}

// If we are being called by the system (e.g. hardware keys) check for current user

// so we handle user restrictions correctly.

if (uid == android.os.Process.SYSTEM_UID) {

uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));

}

if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)

!= AppOpsManager.MODE_ALLOWED) {

return;

}

// reset any pending volume command

// 清除掉任何待處理的音量命令

synchronized (mSafeMediaVolumeStateLock) {

mPendingVolumeCommand = null;

}

// 表示不是固定音量

flags &= ~AudioManager.FLAG_FIXED_VOLUME;

// 如果是多媒體音量,并且是使用固定音量的設(shè)備

if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {

// 加上表示固定音量的flag

flags |= AudioManager.FLAG_FIXED_VOLUME;

// Always toggle between max safe volume and 0 for fixed volume devices where safe

// volume is enforced, and max and 0 for the others.

// This is simulated by stepping by the full allowed volume range

if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&

mSafeMediaVolumeDevices.contains(device)) {

step = safeMediaVolumeIndex(device);

} else {

step = streamState.getMaxIndex();

}

if (aliasIndex != 0) {

aliasIndex = step;

}

} else {

// convert one UI step (+/-1) into a number of internal units on the stream alias

// 如果不是多媒體音量,或者是多媒體音量但是不是固定音量的設(shè)備時(shí)

// 將音量值的步進(jìn)量從源流類型變換到目標(biāo)流類型下,由于不同的流類型的音量調(diào)節(jié)范圍不同,所以這個(gè)轉(zhuǎn)換是必需的

step = rescaleStep(10, streamType, streamTypeAlias);

}

// // 情景模式的處理

// If either the client forces allowing ringer modes for this adjustment,

// or the stream type is one that is affected by ringer modes

if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||

(streamTypeAlias == getUiSoundsStreamType())) {

int ringerMode = getRingerModeInternal();

// do not vibrate if already in vibrate mode

// 如果已經(jīng)是震動(dòng)模式,則不進(jìn)行震動(dòng)

if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {

flags &= ~AudioManager.FLAG_VIBRATE;

}

// Check if the ringer mode handles this adjustment. If it does we don't

// need to adjust the volume further.

// 根據(jù)我們的操作來(lái)檢查是否需要切換情景模式

final int result = checkForRingerModeChange(aliasIndex, direction, step,

streamState.mIsMuted, callingPackage, flags);

adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;

// If suppressing a volume adjustment in silent mode, display the UI hint

if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {

flags |= AudioManager.FLAG_SHOW_SILENT_HINT;

}

// If suppressing a volume down adjustment in vibrate mode, display the UI hint

if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {

flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;

}

}

// If the ringer mode or zen is muting the stream, do not change stream unless

// it'll cause us to exit dnd

// 勿擾模式

if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {

adjustVolume = false;

}

// 獲取舊的音量大小

int oldIndex = mStreamStates[streamType].getIndex(device);

if (adjustVolume

&& (direction != AudioManager.ADJUST_SAME) && (keyEventMode != VOL_ADJUST_END)) {

mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);

// 先處理靜音調(diào)整

if (isMuteAdjust) {

boolean state;

if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {

state = !streamState.mIsMuted;

} else {

state = direction == AudioManager.ADJUST_MUTE;

}

if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {

setSystemAudioMute(state);

}

for (int stream = 0; stream < mStreamStates.length; stream++) {

if (streamTypeAlias == mStreamVolumeAlias[stream]) {

if (!(readCameraSoundForced()

&& (mStreamStates[stream].getStreamType()

== AudioSystem.STREAM_SYSTEM_ENFORCED))) {

// 這里獲取當(dāng)前流對(duì)應(yīng)的VolumeStreamState實(shí)例,然后去調(diào)用mute方法

// 最終也會(huì)到AudioSystem去調(diào)用native方法

mStreamStates[stream].mute(state);

}

}

}

} else if ((direction == AudioManager.ADJUST_RAISE) &&

!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {

// 安全音量提示,音量增加的時(shí)候才會(huì)去檢測(cè)

Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);

mVolumeController.postDisplaySafeVolumeWarning(flags);

} else if (!isFullVolumeDevice(device)

&& (streamState.adjustIndex(direction * step, device, caller,

hasModifyAudioSettings)

|| streamState.mIsMuted)) {

// Post message to set system volume (it in turn will post a

// message to persist).

if (streamState.mIsMuted) {

// Unmute the stream if it was previously muted

if (direction == AudioManager.ADJUST_RAISE) {

// unmute immediately for volume up

streamState.mute(false);

} else if (direction == AudioManager.ADJUST_LOWER) {

if (mIsSingleVolume) {

sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,

streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);

}

}

}

// 設(shè)置音量到底層

sendMsg(mAudioHandler,

MSG_SET_DEVICE_VOLUME,

SENDMSG_QUEUE,

device,

0,

streamState,

0);

}

int newIndex = mStreamStates[streamType].getIndex(device);

// Check if volume update should be send to AVRCP

if (streamTypeAlias == AudioSystem.STREAM_MUSIC

&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)

&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {

if (DEBUG_VOL) {

Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="

+ newIndex + "stream=" + streamType);

}

mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);

}

// Check if volume update should be send to Hearing Aid

if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {

// only modify the hearing aid attenuation when the stream to modify matches

// the one expected by the hearing aid

if (streamType == getHearingAidStreamType()) {

if (DEBUG_VOL) {

Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="

+ newIndex + " stream=" + streamType);

}

mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);

}

}

// Check if volume update should be sent to Hdmi system audio.

if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {

setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);

}

}

final int newIndex = mStreamStates[streamType].getIndex(device);

if (adjustVolume) {

synchronized (mHdmiClientLock) {

if (mHdmiManager != null) {

// mHdmiCecSink true => mHdmiPlaybackClient != null

if (mHdmiCecSink

&& mHdmiCecVolumeControlEnabled

&& streamTypeAlias == AudioSystem.STREAM_MUSIC

// vol change on a full volume device

&& isFullVolumeDevice(device)) {

int keyCode = KeyEvent.KEYCODE_UNKNOWN;

switch (direction) {

case AudioManager.ADJUST_RAISE:

keyCode = KeyEvent.KEYCODE_VOLUME_UP;

break;

case AudioManager.ADJUST_LOWER:

keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;

break;

case AudioManager.ADJUST_TOGGLE_MUTE:

keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;

break;

default:

break;

}

if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {

final long ident = Binder.clearCallingIdentity();

try {

final long time = java.lang.System.currentTimeMillis();

switch (keyEventMode) {

case VOL_ADJUST_NORMAL:

mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);

mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);

break;

case VOL_ADJUST_START:

mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);

break;

case VOL_ADJUST_END:

mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);

break;

default:

Log.e(TAG, "Invalid keyEventMode " + keyEventMode);

}

} finally {

Binder.restoreCallingIdentity(ident);

}

}

}

if (streamTypeAlias == AudioSystem.STREAM_MUSIC

&& (oldIndex != newIndex || isMuteAdjust)) {

maybeSendSystemAudioStatusCommand(isMuteAdjust);

}

}

}

}

// 通知外界音量發(fā)生變化

sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);

}

④sendVolumeUpdate

// UI update and Broadcast Intent

protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)

{

streamType = mStreamVolumeAlias[streamType];

if (streamType == AudioSystem.STREAM_MUSIC) {

flags = updateFlagsForTvPlatform(flags);

if (isFullVolumeDevice(device)) {

flags &= ~AudioManager.FLAG_SHOW_UI;

}

}

mVolumeController.postVolumeChanged(streamType, flags);

}

3、AudioManager

/**

* Increase the ringer volume.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_RAISE = 1;

/**

* Decrease the ringer volume.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_LOWER = -1;

/**

* Maintain the previous ringer volume. This may be useful when needing to

* show the volume toast without actually modifying the volume.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_SAME = 0;

/**

* Mute the volume. Has no effect if the stream is already muted.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_MUTE = -100;

/**

* Unmute the volume. Has no effect if the stream is not muted.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_UNMUTE = 100;

/**

* Toggle the mute state. If muted the stream will be unmuted. If not muted

* the stream will be muted.

*

* @see #adjustVolume(int, int)

* @see #adjustStreamVolume(int, int, int)

*/

public static final int ADJUST_TOGGLE_MUTE = 101;

參考鏈接

評(píng)論可見(jiàn),查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18504270.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問(wèn)

文章目錄