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è)抽象類
Android 10之后近期列表的顯示被移到Launcher里面了。在Launcher3的一個(gè) 類中TouchInteractionService.java IBinder mMyBinder = new IOverviewProxy.Stub() 通過(guò)AIDL的方法與systemUI通信
————————————————
一、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
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 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 好了,到這里我們的第一部分初始化流程就講完了 四、狀態(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 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 第一、導(dǎo)航欄顯示哪些控件是由getDefaultLayout來(lái)決定。 一般情況下我們是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 private final Optional private Context mContext; private Handler mHandler; private TrustManager mTrustManager; private OverviewProxyService mOverviewProxyService; @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyRecentsImpl(Optional Optional 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; 參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。