系统服务-SystemUI9.0(1)

SystemUI总结
https://www.jianshu.com/p/1a1c53cc44c3
android 下拉状态栏(SystemUI)常见修改记录
https://blog.csdn.net/Mis_wenwen/article/details/73468788
systemui整体介绍
https://cloud.tencent.com/developer/article/1155393
StatusBar 系统图标
https://cloud.tencent.com/developer/article/1186839
Notification流程
https://www.cnblogs.com/cczheng-666/p/10953193.html
SystemUI9.0系统应用图标加载流程
https://blog.csdn.net/tj_shenzhendaxue/article/details/88698042
Android 8.0 SystemUI(四):二说顶部 StatusBar 信号等图标
https://cloud.tencent.com/developer/article/1350097
导航栏
https://blog.csdn.net/qq_31530015/article/details/53507968

SystemUI分类
状态栏StatusBar:通知消息提示和状态展现
导航栏NavigationBar:返回,HOME,Recent
锁屏界面KeyGuard:锁屏模块可以看做单独的应用,提供基本的手机个人隐私保护
最近任务Recents:近期应用管理,以堆叠栈的形式展现。
通知栏Notification Panel:QuickSettings和Notification展示系统或应用通知内容。提供快速系统设置开关
音量调节对话框VolumeUI:来用展示或控制音量的变化:媒体音量、铃声音量与闹钟音量
截屏界面ScreenShot::长按电源键+音量下键后截屏,用以展示截取的屏幕照片/内容
电源管理PowerUI:主要处理和Power相关的事件,比如省电模式切换、电池电量变化和开关屏事件等
RingtonePlayer:铃声播放
StackDivider:控制管理分屏
PipUI:提供对于画中画模式的管理

9.0源码目录:
系统服务-SystemUI9.0(1)_第1张图片
SystemUI启动流程
由init进程->Zygote进程->SystemServer进程->systemui启动

按电源键,系统上电,从固定地址加载固化在ROM的bootloader代码到RAM中执行,bootloader引导程序将os拉起,系统拉起后进行初始化和解析启动init进程。

startOtherServices()中,通过调用AMS的systemReady()方法通知AMS准备就绪。systemReady()拥有一个名为goingCallback的Runnable实例作为参数,当AMS完成systemReady()处理后会回调Runnable.run()

ps:frameworks/base/service/java/com/android/server/SystemServer.java

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
......
if (mSystemReady) {
    // If we're done calling all the receivers, run the next "boot phase" passed in
    // by the SystemServer
    if (goingCallback != null) {
        goingCallback.run();
    }
    return;
}
......
}

AMS在回调中启动SystemUI

mActivityManagerService.systemReady(() -> {
......
    startSystemUi(context, windowManagerF);
......
}
static final void startSystemUi(Context context, WindowManagerService windowManager) {
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.android.systemui",
                "com.android.systemui.SystemUIService"));
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    //Slog.d(TAG, "Starting service: " + intent);
    context.startServiceAsUser(intent, UserHandle.SYSTEM);
    windowManager.onSystemUiStarted();
}

在开启服务后,WMS回调SystemUI已开启

ps:frameworks/base/service/core/java/com/android/server/wm/WindowManagerService.java

public void onSystemUiStarted() {
    mPolicy.onSystemUiStarted();
}

ps:frameworks/base/core/java/android/view/WindowManagerPolicy.java

void onSystemUiStarted();

SystemUIService继承了Service

public class SystemUIService extends Service {
......
@Override
public void onCreate() {
    super.onCreate();
    ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    ......
}
......
}
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java

public void startServicesIfNeeded() {
      String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
      android.util.Log.e("sunyang", "SystemUIApplication startServicesIfNeeded names :" + names);
      startServicesIfNeeded(names);
}

private void startServicesIfNeeded(String[] services) {
......
try {
    cls = Class.forName(clsName);
    mServices[i] = (SystemUI) cls.newInstance();
}
......
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
android.util.Log.e("sunyang", "SystemUIApplication startServicesIfNeeded running :" + mServices[i]);
mServices[i].start();
......
}

 loading: com.android.systemui.Dependency
 loading: com.android.systemui.util.NotificationChannels
 loading: com.android.systemui.statusbar.CommandQueue$CommandQueueStart
 loading: com.android.systemui.keyguard.KeyguardViewMediator
 loading: com.android.systemui.recents.Recents
 loading: com.android.systemui.volume.VolumeUI
 loading: com.android.systemui.stackdivider.Divider
 loading: com.android.systemui.SystemBars
 loading: com.android.systemui.usb.StorageNotification
 loading: com.android.systemui.power.PowerUI
 loading: com.android.systemui.media.RingtonePlayer
 loading: com.android.systemui.keyboard.KeyboardUI
 loading: com.android.systemui.pip.PipUI
 loading: com.android.systemui.shortcut.ShortcutKeyDispatcher
 loading: com.android.systemui.VendorServices
 loading: com.android.systemui.util.leak.GarbageMonitor$Service
 loading: com.android.systemui.LatencyTester
 loading: com.android.systemui.globalactions.GlobalActionsComponent
 loading: com.android.systemui.ScreenDecorations
 loading: com.android.systemui.fingerprint.FingerprintDialogImpl
 loading: com.android.systemui.SliceBroadcastRelayHandler
 loading: com.android.systemui.screenswitch.ScreenSwitchUI
 loading: com.android.systemui.presssensor.PressSensorUI

统一接口

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.java

public abstract class SystemUI implements SysUiServiceProvider {
...
public abstract void start();
处理系统状态变化的回调,这里的状态变化包括:时区变更,字体大小变更,输入模式变更,屏幕大小变更,屏幕方向变更等
protected void onConfigurationChanged(Configuration newConfig) {}
用来将模块的内部状态dump到输出流中,这个方法主要是辅助调试所用。开发者可以在开发过程中,通过adb shell执行dump来了解系统的内部状态
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
系统启动完成的回调方法
protected void onBootCompleted() {}
...
}

状态栏:StatusBar Notification QuickSettings

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java

@Override
public void start() {
    if (DEBUG) Log.d(TAG, "start");
    createStatusBarFromConfig(); 创建了状态栏
}

ps:frameworks/base/packages/SystemUI/res/value/config.xml
config_statusBarComponent com.android.systemui.statusbar.phone.StatusBar

private void createStatusBarFromConfig() {
    if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
    final String clsName = mContext.getString(R.string.config_statusBarComponent);
    if (clsName == null || clsName.length() == 0) {
        throw andLog("No status bar component configured", null);
    }
    Class cls = null;
    try { 类加载器加载对应类
        cls = mContext.getClassLoader().loadClass(clsName);
    } catch (Throwable t) {
        throw andLog("Error loading status bar component: " + clsName, t);
    }
    try { 反射创建对象
        mStatusBar = (SystemUI) cls.newInstance();
    } catch (Throwable t) {
        throw andLog("Error creating status bar component: " + clsName, t);
    }
    mStatusBar.mContext = mContext;
    mStatusBar.mComponents = mComponents;
    mStatusBar.start();
    if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}

调用StatusBar的start() 状态栏和导航栏的启动过程
1、获取IStatusBarService,IStatusBarService是运行于system_server的系统服务,它接受操作状态栏/导航栏的请求并将其转发给StatusBar,为了保证SystemUI意外退出不会发生信息丢失,IStatusBarService保存了所有状态栏和导航栏进行显示或处理的信息副本
2、将一个继承自IStatusBar.Stub的CommandQueue的实例注册到IStatusBarService以建立通信,并将信息副本取回
3、创建状态栏与导航栏的窗口createAndAddWindows()创建statusbar
4、使用从IStatusBarService取回的信息副本

7.1.3 理解IStatusBarService
public class StatusBarManagerService extends IStatusBarService.Stub {}
与WMS、IMS等系统服务一样,StatusBarManagerService 在ServerThread中创建

        if (!isWatch) {
            traceBeginAndSlog("StartStatusBarManagerService");
            try {
                statusBar = new StatusBarManagerService(context, wm);
                ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
            } catch (Throwable e) {
                reportWtf("starting StatusBarManagerService", e);
            }
            traceEnd();
        }

构造方法:
    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
        mContext = context;
        mWindowManager = windowManager;

        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
        LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
    }

获取保存在其中的副本
StatusBarManagerService.registerStatusBar
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {

	@Override
	public void start() {
	...
	......
	    //mCommandQueue是CommandQueue类的一个实例,CommandQueue继承自IStatusBar.Stub,因此它是IStatusBar
        //的Bn端。在完成注册后,这个Binder对象的Bp端将会保存在IStatusBarService中。
        // Connect in to the status bar manager service
        mCommandQueue = getComponent(CommandQueue.class);
        mCommandQueue.addCallbacks(this);
        //switches存储了一些杂项:禁用功能列表,SystemUIVisiblity,是否在导航栏中显示虚拟的菜单键,输入法窗口
        //是否可见,输入法窗口是否消费BACK键,是否接入了实体键盘,实体键盘是否被启用
        int[] switches = new int[9];
        ArrayList binders = new ArrayList<>();
        ArrayList iconSlots = new ArrayList<>();
        ArrayList icons = new ArrayList<>();
        Rect fullscreenStackBounds = new Rect();
        Rect dockedStackBounds = new Rect();
        try {
            //2 向IStatusBarService进行注册,并获取保存在IStatusBarService中的信息副本
            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                    fullscreenStackBounds, dockedStackBounds);
        } catch (RemoteException ex) {
            // If the system process isn't there we're doomed anyway.
        }
        //3 创建状态栏与导航栏的窗口
		createAndAddWindows(); 创建statusbar
		......
		mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
        //禁用某些功能
        mCommandQueue.disable(switches[0], switches[6], false /* animate */);
        //设置SystemUiVisibility
        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
                fullscreenStackBounds, dockedStackBounds);
        //设置菜单键的可见性
        topAppWindowChanged(switches[2] != 0);
        // StatusBarManagerService has a back up of IME token and it's restored here.
        //根据输入法窗口的可见性调整导航栏的样式
        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
        //依次向系统状态栏区添加状态图标
        // Set up the initial icon state
        int N = iconSlots.size();
        for (int i=0; i < N; i++) {
            mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
        }
        //导航栏和状态栏启动完成
	...
	}
}

解析步骤2
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);

    @Override
    public void registerStatusBar(IStatusBar bar, List iconSlots,
            List iconList, int switches[], List binders,
            Rect fullscreenStackBounds, Rect dockedStackBounds) {
        //权限检查,安全为避免其他应用调用,因此调用者必须具有系统级权限android.Manifest.permission.STATUS_BAR_SERVICE
        enforceStatusBarService();
		//1、将bar参数保存在mBar成员中,mBar类型IStatusBar,就是CommandQueue的Bp端,后面StatusBarManagerService通过mBar与StatusBar 通信
        Slog.i(TAG, "registerStatusBar bar=" + bar);
        mBar = bar;
        try {
            mBar.asBinder().linkToDeath(new DeathRecipient() {
                @Override
                public void binderDied() {
                    mBar = null;
                    notifyBarAttachChanged();
                }
            }, 0);
        } catch (RemoteException e) {
        }
        notifyBarAttachChanged();
        //2、接下来依次为调用者返回信息副本,系统状态区的图标列表
        synchronized (mIcons) {
            for (String slot : mIcons.keySet()) {
                iconSlots.add(slot);
                iconList.add(mIcons.get(slot));
            }
        }
        //switches中的杂项
        synchronized (mLock) {
            switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
            switches[1] = mSystemUiVisibility;
            switches[2] = mMenuVisible ? 1 : 0;
            switches[3] = mImeWindowVis;
            switches[4] = mImeBackDisposition;
            switches[5] = mShowImeSwitcher ? 1 : 0;
            switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
            switches[7] = mFullscreenStackSysUiVisibility;
            switches[8] = mDockedStackSysUiVisibility;
            binders.add(mImeToken);
            fullscreenStackBounds.set(mFullscreenStackBounds);
            dockedStackBounds.set(mDockedStackBounds);
        }
    }

StatusBarManagerService的工作方式
当他接受到操作状态栏和导航栏的请求时,首先将请求信息保存到副本中,然后将mBar发送给StatusBar
1、StatusBarManagerService是状态栏和导航栏在system_server中的代理。所有对状态栏和导航栏的需求都通过此实例操作
2、StatusBarManagerService保存了状态栏和导航栏所需的信息副本,用于在SystemUI意外退出后恢复

设置系统状态区图标

    @Override
    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
            String contentDescription) {
        enforceStatusBar();

        synchronized (mIcons) {
            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
                    iconLevel, 0, contentDescription);
            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
            //1、将图标信息保存在副本中
            mIcons.put(slot, icon);
			//2、将设置请求发送给StatusBar
            if (mBar != null) {
                try {
                    mBar.setIcon(slot, icon);
                } catch (RemoteException ex) {
                }
            }
        }
    }

7.1.4 SystemUI的体系结构
系统服务-SystemUI9.0(1)_第2张图片
1、SystemUIService,一个普通的android服务,它以一个容器的角色运行于SystemUI进程中。在其内部运行着多个服务,包括StatusBar
2、IStatusBarService,即系统服务StatusBarManagerService是状态栏和导航栏向外界提供服务的接口,运行在system_server进程中
3、IStatusBar,即SystemUI中的CommandQueue是联系StatusBarManagerService与StatusBar的桥梁
4、SystemUI还包括ImageWallpaper等功能实现。通过startService、startActivity

布局图
系统服务-SystemUI9.0(1)_第3张图片

StatusBar.java
super_status_bar.xml
	@+id/status_bar_container
	status_bar.xml
		@+id/notification_lights_out
		@+id/status_bar_contents
			@+id/clock
			@+id/netspeed
	status_bar_expanded.xml
		qs_frame
	brightness_mirror.xml

顶部状态栏
status_bar.xml
	@+id/notification_lights_out
	@+id/status_bar_contents
		heads_up_status_bar_layout
		@+id/status_bar_left_side
			@+id/clock
			@+id/notification_icon_area
		@+id/system_icon_area
			@+id/netspeed
			system_icons

@+id/notification_lights_out
一个ImageView,一般不可见。在SystemUIVisiblity中有一个名为SYSTEM_UI_FLAG_LOW_PROFILE的标记。当应用程序希望用户注意力集中在它所显示的内容上,可以在其SystemUIVisiblity中添加这一标记。SYSTEM_UI_FLAG_LOW_PROFILE会使状态栏和导航栏进入低辨识度模式。低辨识度模式下状态栏不会显示任何信息,只是在黑色背景中显示一个灰色圆点。这个黑色圆点为notification_lights_out

@+id/status_bar_contents
一个LinearLayout,状态栏上各种信息的显示场所
	@+id/status_bar_contents
		heads_up_status_bar_layout
		@+id/status_bar_left_side
			@+id/clock
			@+id/notification_icon_area
		@+id/system_icon_area
			@+id/netspeed
			system_icons

@+id/notification_icon_area
通知信息
@+id/system_icon_area

7.2 深入理解状态栏
作为一个将所有信息集中显示的场所,状态栏对需要显示的信息分为以下5种
1、通知信息:它可以在状态栏左侧显示一个图标以引起用户的注意,并在下拉栏中为用户显示更加详细的信息。这是状态栏所能提供的信息显示服务之中最灵活的一种功能。它对信息种类以及来源没有做任何限制。使用者可以通过StatusBarManagerService所提供的接口向状态栏中添加或移除一条通知信息
2、时间信息:显示在状态栏最右侧的一个小型数字时钟,是一个名为Clock的继承自TextView的控件。它监听了几个和时间相关的广播:ACTION_TIME_TICK、ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_CONFIGURATION_CHANGED。当其中一个广播到来时从Calendar类中获取当前的系统时间,然后进行字符串格式化后显示出来。时间信息的维护工作在状态栏内部完成,因此外界无法通过api修改时间信息和显示行为
3、电量信息:显示在数字时钟左侧的电池图标,用于提示当前的电量情况。它是一个被BatteryController类所管理的ImageView。BatteryController通过监听android.action.BATTERY_CHANGED广播从BetteryService中获取电量信息,并根据电量信息 选择一个合适的电池图标显示在ImageView上。同时间信息一样,这也是在状态栏内部维护的,外界无法干预状态栏对电量信息的显示行为
4、信号信息:显示在电量信息的左侧的一系列ImageView,用于显示系统当前的WIFI、移动网络的信息的范畴。他们被NetworkController类维护,NetworkController监听一系列与信号相关的广播如WIFI_STATE_CHANGED_ACTION、ACTION_SIM_STATE_CHANGED、ACTION_AIRPLANE_MODE_CHANGED等,并在这些广播到来时显示、更改或移除相关的ImageView
5、系统状态图标区:这个区域用一系列图标标识系统当前的状态,位于信号左侧。StatusBarManagerService通过setIcon() 为外界提供修改系统状态图标区的途径

7.2.1 状态栏窗口的创建与控件数结构
1 状态栏窗口的创建

public void createAndAddWindows() {
    addStatusBarWindow();
}

private void addStatusBarWindow() {
    makeStatusBarView();
    mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
    mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
            new RemoteInputController.Delegate() {
                public void setRemoteInputActive(NotificationData.Entry entry,
                        boolean remoteInputActive) {
                    mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                    entry.row.notifyHeightChanged(true /* needsAnimation */);
                    updateFooter();
                }
                public void lockScrollTo(NotificationData.Entry entry) {
                    mStackScroller.lockScrollTo(entry.row);
                }
                public void requestDisallowLongPressAndDismiss() {
                    mStackScroller.requestDisallowLongPress();
                    mStackScroller.requestDisallowDismiss();
                }
            });
    mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);
    4 通过 mStatusBarWindowManager.add创建状态栏的窗口
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}

2 为状态栏创建WindowManager.LayoutParams
   public void add(View statusBarView, int barHeight) {
        // 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,//状态栏的宽度为充满整个屏幕宽度
                barHeight,//getStatusBarHeight()
                WindowManager.LayoutParams.TYPE_STATUS_BAR,//窗口类型,可以显示在应用窗口之上
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//状态栏不接受按键事件
                        //这一标记将使得状态栏接受导致设备唤醒的触摸事件,通常这一事件会在interceptMotionBeforeQueueing()
                        //的过程中被用于唤醒设备(或从变暗状态下恢复),而InputDispatcher会阻止这一事件发送给窗口
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        //允许状态栏支持触摸事件序列的拆分
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        //状态栏的Surface像素格式为支持透明度
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
    }

当用户按下状态栏导致窗帘下拉时,StatusBarWindowManager通过mWindowManager.updateViewLayout(mStatusBarView, mLp)修改窗口的LayoutParams高度为MATCH_PARENT,即全屏显示,移除WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,可以监听back键收起窗帘

状态栏的高度,资源定义在frameworks\base\core\res\res\values\dimens.xml

1 通过getStatusBarHeight()方法获取状态栏的高度
    public int getStatusBarHeight() {
        if (mNaturalBarHeight < 0) {
            final Resources res = mContext.getResources();
            mNaturalBarHeight =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        }
        return mNaturalBarHeight;
    }
3 创建状态栏的控件树
protected void makeStatusBarView() {
...
inflateStatusBarWindow(context);
...
FragmentHostManager.get(mStatusBarWindow)
        .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
            CollapsedStatusBarFragment statusBarFragment =
                    (CollapsedStatusBarFragment) fragment;
            statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
            mStatusBarView = (PhoneStatusBarView) fragment.getView();
            mStatusBarView.setBar(this);
            mStatusBarView.setPanel(mNotificationPanel);
            mStatusBarView.setScrimController(mScrimController);
            mStatusBarView.setBouncerShowing(mBouncerShowing);
            if (mHeadsUpAppearanceController != null) {
                // This view is being recreated, let's destroy the old one
                mHeadsUpAppearanceController.destroy();
            }
            mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                    mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
            setAreThereNotifications();
            checkBarModes();
        }).getFragmentManager()
        .beginTransaction()
        .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                CollapsedStatusBarFragment.TAG)
        .commit();
}

加载了状态栏根布局 super_status_bar.xml
 protected void inflateStatusBarWindow(Context context) {
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
  }

状态栏根布局 StatusBarWindowView,截获ACTION_DOWN的触摸事件后,会修改窗口的高度为MATCH_PARENT,然后将其他跟随触摸,实现卷帘的下拉效果。
系统服务-SystemUI9.0(1)_第4张图片
顶部状态栏 status_bar_container FrameLayout
包括:notification statusIcon signal 电池 时钟的添加和更新

statusIcon的初始化与更新
系统状态的显示,比如蓝牙、闹铃、定位、省流量开关
frameworks/base/core/res/res/values/config.xml


ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java

Fragement加载的布局 status_bar.xml
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        Bundle savedInstanceState) {
        return inflater.inflate(R.layout.status_bar, container, false);
  }

在CollapsedStatusBarFragment.java onViewCreated中加载statusIcons
@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.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
    }
    mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
    mDarkIconManager.setShouldLog(true);
    Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
    mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
    mClockView = mStatusBar.findViewById(R.id.clock);
    showSystemIconArea(false);
    showClock(false);
    initEmergencyCryptkeeperText();
    initOperatorName();
}

顶部状态栏Fragement加载的布局 status_bar.xml
布局包括:
status_bar_container FrameLayout 顶部状态栏
status_bar_expanded.xml QuickSettings NotificationExpand
brightness_mirror.xml亮度调节

顶部状态栏:notification statusIcon signal 电池 时钟的添加和更新
顶部状态栏创建fragment、CollapsedStatusBarFragment
其中加载status_bar.xml 自定义组合控件
LinearLayout 横向布局包括
status_bar_cotents->
clock
notification_icon_area 显示的都是notifications,如三方和系统的一些通知
system_icons.xml->
statusIcons显示的一些系统状态,如蓝牙、闹铃
signal_cluster_view.xml显示的是信号相关的view,如wifi,cell信号格 battery 独立图标,电池电量显示、时间显示
系统服务-SystemUI9.0(1)_第5张图片
系统服务-SystemUI9.0(1)_第6张图片

	clock
	notification_icon_area 显示的都是notifications,如三方和系统的一些通知
	system_icons.xml-> 
		statusIcons显示的一些系统状态,如蓝牙、闹铃
		signal_cluster_view.xml显示的是信号相关的view,如wifi,cell信号格		   		
		battery 独立图标,电池电量显示、时间显示

注:
8.0 中顶部状态栏 notification icon 部分逻辑有了很大的改动。对比 7.0,多出了个NotificationInflater。并且,icon 和 expand 的逻辑流程是几乎相同的。
增加了分析 notification icon 相关流程的复杂度

StatusBarWindowView.java(包括 1、顶部状态栏 2、亮度调节 3、QuickSettings Notification Expand)
负责整体框架绘制,点击事件监听等

一说:顶部statusBar初始化、分块、statusIcon块的添加和更新
StatusIcon块,主要负责的是系统状态的显示,比如蓝牙、闹铃、定位、省流量开关等。
这些Icon,都是系统预定好了是哪些。并在一个配置文件定义了slot,或者说是标签。
如果你想加一个新类型图标,首先要修改的是这个文件中的config_statusBarIcons数组

ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java

在onViewCreated中,加载了statusIcons布局
87    @Override
88    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
89        super.onViewCreated(view, savedInstanceState);
90        mStatusBar = (PhoneStatusBarView) view;
91        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
92            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
93        }
            一说:顶部statusBar初始化、分块、statusIcon块的添加和更新
94        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
95        mDarkIconManager.setShouldLog(true);
96        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
            二说:内容计划是信号群icon与电池、时钟相关
97        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
98        mClockView = mStatusBar.findViewById(R.id.clock);
99        showSystemIconArea(false);
100      showClock(false);
101      initEmergencyCryptkeeperText();
102      initOperatorName();
103    }
ps:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
DarkIconManager为内部类构造方法,StatusBarIconController状态栏图标的控制类
    public static class DarkIconManager extends IconManager {
    。。。
	97       public DarkIconManager(LinearLayout linearLayout) {
	98            super(linearLayout);
	99            mIconHPadding = mContext.getResources().getDimensionPixelSize(
	100                    R.dimen.status_bar_icon_padding);
	101            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
	102        }
	。。。
    }

PhoneStatusBarView.java成为基类,顶部状态栏的view

二说:内容计划是信号群icon与电池、时钟相关
三说:notification icon相关

public interface StatusBarIconController {
...
public void addIconGroup(IconManager iconManager);
}

config_statusBarIcons res/values/Strings.xml
实现类 
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
        ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {

构造方法初始化所有图标
public StatusBarIconControllerImpl(Context context) {
    super(context.getResources().getStringArray(
            com.android.internal.R.array.config_statusBarIcons));
    Dependency.get(ConfigurationController.class).addCallback(this);
...
}
}

系统服务-SystemUI9.0(1)_第7张图片

StatusBarIconList.java 父类的构造方法

public class StatusBarIconList {
    private ArrayList mSlots = new ArrayList<>();
    mSlots 包含所有图标信息
    public StatusBarIconList(String[] slots) {
        final int N = slots.length;
        for (int i=0; i < N; i++) {
            mSlots.add(new Slot(slots[i], null));
        }
    }
}

@Override
public void addIconGroup(IconManager group) {
    mIconGroups.add(group);
    List allSlots = getSlots();
    for (int i = 0; i < allSlots.size(); i++) {
        Slot slot = allSlots.get(i);
        List holders = slot.getHolderListInViewOrder();
        boolean blocked = mIconBlacklist.contains(slot.getName());

        for (StatusBarIconHolder holder : holders) {
            int tag = holder.getTag();
            int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
            group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
        }
    }
}

Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
StatusBarIconController内部类
public static class IconManager implements DemoMode {

    public IconManager(ViewGroup group) {
    ... 图标的尺寸
        mIconSize = mContext.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.status_bar_icon_size);
    ...
    }
}

添加图标 根据类型
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;
}

Dependency.java

 Dependency.start()中初始化很多控制类
public class Dependency extends SystemUI {

@Override
public void start() {
    // TODO: Think about ways to push these creation rules out of Dependency to cut down
    // on imports.
    mProviders.put(TIME_TICK_HANDLER, () -> {
        HandlerThread thread = new HandlerThread("TimeTick");
        thread.start();
        return new Handler(thread.getLooper());
    });
    mProviders.put(BG_LOOPER, () -> {
        HandlerThread thread = new HandlerThread("SysUiBg",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        return thread.getLooper();
    });
    mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
    mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
    mProviders.put(ActivityStarterDelegate.class, () ->
            getDependency(ActivityStarter.class));

    mProviders.put(AsyncSensorManager.class, () ->
            new AsyncSensorManager(mContext.getSystemService(SensorManager.class)));

    mProviders.put(BluetoothController.class, () ->
            new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));

    mProviders.put(LocationController.class, () ->
            new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
...
}

StatusBar.java start()
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
在此完成所有StatusIcon的初始化和状态监听,完成图标的隐藏或添加

VPN提示、Ethernet图标、Wifi图标、Airplane提示、NoSim提示,移动网络图标等共6类

导航栏

BACK/HOME/RECENT

7.3.1 导航栏的创建
//1 是否需要导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
    createNavigationBar();
}

protected void createNavigationBar() {
    mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
        mNavigationBar = (NavigationBarFragment) fragment;
        if (mLightBarController != null) {
            mNavigationBar.setLightBarController(mLightBarController);
        }
        mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
    });
}

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        Bundle savedInstanceState) {
    return inflater.inflate(R.layout.navigation_bar, container, false);
}

是否使用导航栏位于 config_showNavigationBar 的取值,有物理按键的设备
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mNavigationBarView = (NavigationBarView) view;

    mNavigationBarView.setDisabledFlags(mDisabledFlags1);
    mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());
    mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
    mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
    if (savedInstanceState != null) {
        mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
    }
//1 prepareNavigationBarView()负责为NavigationBarView中的虚拟按键设置用于响应用户触摸事件的监听器
    prepareNavigationBarView();
    checkNavBarModes();

    setDisabled2Flags(mDisabledFlags2);

    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_USER_SWITCHED);
    getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
    notifyNavigationBarScreenOn();
    mOverviewProxyService.addCallback(mOverviewProxyListener);
}

NavigationBarView根控件定义了2套导航栏的控件树,水平方向@id/rot0,垂直方向@id/rot90

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");
    lp.accessibilityTitle = context.getString(R.string.nav_bar);
    lp.windowAnimations = 0;

    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;

    context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
    FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
    NavigationBarFragment fragment = new NavigationBarFragment();
    fragmentHost.getFragmentManager().beginTransaction()
            .replace(R.id.navigation_bar_frame, fragment, TAG)
            .commit();
    fragmentHost.addTagListener(TAG, listener);
    return navigationBarView;
}

横导航栏
navigation_bar_height
竖导航栏
navigation_bar_height_landscape
调整导航栏尺寸,全交给PhoneWindowManager
frameworks/base/core/res/res/values/dimens.xml

防误触的死区
res/values/dimens.xml
navigation_bar_deadzone_size

7.3.2 虚拟按键的工作原理
1 从触摸事件到按键事件的映射
KeyButtonView将触摸事件转换为按键事件
public boolean onTouchEvent(MotionEvent ev) {
    final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
    final int action = ev.getAction();
    int x, y;
    if (action == MotionEvent.ACTION_DOWN) {
        mGestureAborted = false;
    }
    if (mGestureAborted) {
        setPressed(false);
        return false;
    }

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            mDownTime = SystemClock.uptimeMillis();
            mLongClicked = false;
            setPressed(true);

            // Use raw X and Y to detect gestures in case a parent changes the x and y values
            mTouchDownX = (int) ev.getRawX();
            mTouchDownY = (int) ev.getRawY();
            if (mCode != 0) {
                sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
            } else {
                // Provide the same haptic feedback that the system offers for virtual keys.
                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
            }
            if (!showSwipeUI) {
                playSoundEffect(SoundEffectConstants.CLICK);
            }
            removeCallbacks(mCheckLongPress);
            postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
            break;
        case MotionEvent.ACTION_MOVE:
            x = (int)ev.getRawX();
            y = (int)ev.getRawY();

            boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
                    ? NavigationBarCompat.getQuickScrubTouchSlopPx()
                    : NavigationBarCompat.getQuickStepTouchSlopPx());
            boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
                    ? NavigationBarCompat.getQuickStepTouchSlopPx()
                    : NavigationBarCompat.getQuickScrubTouchSlopPx());
            if (exceededTouchSlopX || exceededTouchSlopY) {
                // When quick step is enabled, prevent animating the ripple triggered by
                // setPressed and decide to run it on touch up
                setPressed(false);
                removeCallbacks(mCheckLongPress);
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            setPressed(false);
            if (mCode != 0) {
                sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
            }
            removeCallbacks(mCheckLongPress);
            break;
        case MotionEvent.ACTION_UP:
            final boolean doIt = isPressed() && !mLongClicked;
            setPressed(false);
            final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
            if (showSwipeUI) {
                if (doIt) {
                    // Apply haptic feedback on touch up since there is none on touch down
                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                    playSoundEffect(SoundEffectConstants.CLICK);
                }
            } else if (doHapticFeedback && !mLongClicked) {
                // Always send a release ourselves because it doesn't seem to be sent elsewhere
                // and it feels weird to sometimes get a release haptic and other times not.
                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
            }
            if (mCode != 0) {
                if (doIt) {
                    sendEvent(KeyEvent.ACTION_UP, 0);
                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                } else {
                    sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
                }
            } else {
                // no key code, just a regular ImageView
                if (doIt && mOnClickListener != null) {
                    mOnClickListener.onClick(this);
                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                }
            }
            removeCallbacks(mCheckLongPress);
            break;
    }

    return true;
}

2 按键事件的发送
void sendEvent(int action, int flags, long when) {
    mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT)
            .setType(MetricsEvent.TYPE_ACTION)
            .setSubtype(mCode)
            .addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action)
            .addTaggedData(MetricsEvent.FIELD_FLAGS, flags));
    final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
    final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
            0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
            flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
            InputDevice.SOURCE_KEYBOARD);
    InputManager.getInstance().injectInputEvent(ev,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}

7.3.4 关于导航栏的其他话题
1 菜单键的可见性
PhoneWindow.generateLayout()

2 修改BACK的图标
StatusBar.java
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);

3 导航栏方向的选择

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (DEBUG) Log.d(TAG, String.format(
                "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));

    //检查NavigationBarView处于竖直状态或水平状态
    final boolean newVertical = w > 0 && h > w;
    if (newVertical != mVertical) {
        mVertical = newVertical;
        //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
        //当状态变化时,会在@id/rot0 @id/rot90 之间选择 
        reorient();
        notifyVerticalChangedListener(newVertical);
    }

    postCheckForInvalidLayout("sizeChanged");
    super.onSizeChanged(w, h, oldw, oldh);
}

public void reorient() {
    updateCurrentView();

    ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
    mDeadZone.onConfigurationChanged(mCurrentRotation);

    // force the low profile & disabled states into compliance
    mBarTransitions.init();
    setMenuVisibility(mShowMenu, true /* force */);

    if (DEBUG) {
        Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
    }

    // Resolve layout direction if not resolved since components changing layout direction such
    // as changing languages will recreate this view and the direction will be resolved later
    if (!isLayoutDirectionResolved()) {
        resolveLayoutDirection();
    }
    updateTaskSwitchHelper();
    updateNavButtonIcons();

    getHomeButton().setVertical(mVertical);
}

private void updateCurrentView() {
    //1 获取屏幕旋转角度0 90 180 270
    final int rot = mDisplay.getRotation();
    for (int i=0; i<4; i++) {
        mRotatedViews[i].setVisibility(View.GONE);
    }
    //选择对应方向为当前view,设置为可见
    mCurrentView = mRotatedViews[rot];
    mCurrentView.setVisibility(View.VISIBLE);
    mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
    mNavigationInflaterView.updateButtonDispatchersCurrentView();
    updateLayoutTransitionsEnabled();
    mCurrentRotation = rot;
}
mRotatedViews数组初始化位于onFinishInflate()

@Override
public void onFinishInflate() {
    mNavigationInflaterView = findViewById(R.id.navigation_inflater);
    mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);

    getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);

    DockedStackExistsListener.register(mDockedListener);
    updateRotatedViews();
}

private void updateRotatedViews() {
竖直
    mRotatedViews[Surface.ROTATION_0] =
            mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
水平
    mRotatedViews[Surface.ROTATION_270] =
            mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);

    updateCurrentView();
}

导航栏选择不同控件树时机在onSizeChanged(),然后reorient()通过mDisplay.getRotation()获取屏幕方向,在mRotatedViews数组中选择2种控件

7.3.5 导航栏总结
1 核心工作是将用户的触摸事件转换成相应的按键事件,并发送给InputDispatcher,主要实现者是导航栏内的KeyButtonView
2 PhoneWindowManager对导航栏进行布局

7.4 禁用状态栏和导航栏的功能
锁屏下,隐藏虚拟按键,通过密码或图案进行解锁保护,不希望用户拉出下来窗口
7.4.1 如何禁用状态栏和导航栏的功能

StatusBarManager.java
public void disable(int what) {
    try {
        final IStatusBarService svc = getService();
        if (svc != null) {
            svc.disable(what, mToken, mContext.getPackageName());
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
禁止下拉
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
隐藏状态栏中的通知图标
public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
禁止通知图标
public static final int DISABLE_NOTIFICATION_ALERTS
        = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
禁止Ticker
public static final int DISABLE_NOTIFICATION_TICKER
        = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
隐藏状态栏中系统状态图标区、信号区、电量、时钟
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;

@Deprecated
public static final int DISABLE_NAVIGATION = 
        View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;

public static final int DISABLE_NONE = 0x00000000;

使用这个功能需要系统权限 android.permission.STATUS_BAR

7.4.2 StatusBarManagerService对禁用标记的维护
@Override
public void disable(int what, IBinder token, String pkg) {
    disableForUser(what, token, pkg, mCurrentUserId);
}

@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
    enforceStatusBar();

    synchronized (mLock) {
        disableLocked(userId, what, token, pkg, 1);
    }
}

7.4.3 状态栏与导航栏对禁用标记的响应
7.5 理解SystemUIVisibility
第三方用户使用,提供给他们接口,用于隐藏 显示 导航栏 状态栏

在这里插入图片描述

通知栏Notification Panel
QuickSettings和Notification展示系统或应用通知内容。提供快速系统设置开关
Notification流程
初始化通知栏区域
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                    mStatusBarView = (PhoneStatusBarView) fragment.getView();
                    mStatusBarView.setBar(this);
                    mStatusBarView.setPanel(mNotificationPanel);传递 NotificationPanelView 显示下拉UI控制
                    ...
                    }

获取到 notification_icon_area,FrameLayout转为ViewGroup,调用 notificationIconAreaController 获取通知要显示的view(LinearLayout),
如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)

    public void initNotificationIconArea(NotificationIconAreaController
            notificationIconAreaController) {
        ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
        mNotificationIconAreaInner =
                notificationIconAreaController.getNotificationInnerAreaView();
        if (mNotificationIconAreaInner.getParent() != null) {
            ((ViewGroup) mNotificationIconAreaInner.getParent())
                    .removeView(mNotificationIconAreaInner);
        }
        notificationIconArea.addView(mNotificationIconAreaInner);
        // Default to showing until we know otherwise.
        showNotificationIconArea(false);
    }

动画隐藏通知栏
当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)

    public void hideNotificationIconArea(boolean animate) {
        animateHide(mNotificationIconAreaInner, animate);
    }

    public void showNotificationIconArea(boolean animate) {
        animateShow(mNotificationIconAreaInner, animate);
    }

getNotificationInnerAreaView()方法中看看通知栏icon对应的容器

    public View getNotificationInnerAreaView() {
        return mNotificationIconArea;
    }

    protected void initializeNotificationAreaViews(Context context) {
        reloadDimens(context);

        LayoutInflater layoutInflater = LayoutInflater.from(context);
        mNotificationIconArea = inflateIconArea(layoutInflater);
        mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
                R.id.notificationIcons);
        mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
    }

    protected View inflateIconArea(LayoutInflater inflater) {
        return inflater.inflate(R.layout.notification_icon_area, null);
    }

notification_icon_area.xml
notification_icon_area(FrameLayout) 中添加 notification_icon_area_inner(LinearLayout),
每一个通知对应的bean为 NotificationData,创建 Notification 添加到 NotificationIconContainer(FrameLayout)中

statusBar 的start()中注册 NotificationListenerWithPlugins 作为系统service监听通知消息

start()->
创建Notification的监听
        mNotificationListener =  Dependency.get(NotificationListener.class);
        ......
        mNotificationListener.setUpWithPresenter(this, mEntryManager);

public class NotificationListener extends NotificationListenerWithPlugins {

public void setUpWithPresenter(NotificationPresenter presenter,
            NotificationEntryManager entryManager) {
        mPresenter = presenter;
        mEntryManager = entryManager;

        try {
            registerAsSystemService(mContext,
                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
                    UserHandle.USER_ALL);
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to register notification listener", e);
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
        if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
            final String key = sbn.getKey();
            mPresenter.getHandler().post(() -> {
                mEntryManager.removeNotification(key, rankingMap);
            });
        }
    }
ps:NotificationEntryManager.java
@Override
    public void addNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
        try {
            addNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }

    private void addNotificationInternal(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) throws InflationException {
            ...            
        NotificationData.Entry shadeEntry = createNotificationViews(notification);
    }

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
            throws InflationException {
        if (DEBUG) {
            Log.d(TAG, "createNotificationViews(notification=" + sbn);
        }
        NotificationData.Entry entry = new NotificationData.Entry(sbn);
        Dependency.get(LeakDetector.class).trackInstance(entry);
        entry.createIcons(mContext, sbn);
        // Construct the expanded view.
        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
        return entry;
    }

private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                entry.notification.getUser().getIdentifier());

        final StatusBarNotification sbn = entry.notification;
        if (entry.row != null) {
            entry.reset();
            updateNotification(entry, pmUser, sbn, entry.row);
        } else {
            new RowInflaterTask().inflate(mContext, parent, entry,
                    row -> {
                        bindRow(entry, pmUser, sbn, row);
                        updateNotification(entry, pmUser, sbn, row);
                    });
        }
    }

ps:RowInflaterTask.java
    public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
            RowInflationFinishedListener listener) {
        if (TRACE_ORIGIN) {
            mInflateOrigin = new Throwable("inflate requested here");
        }
        mListener = listener;
        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
        mEntry = entry;
        entry.setInflationTask(this);
        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
    }
ps:NotificationEntryManager.java
    @Override
    public void updateNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
        try {
            updateNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }

屏蔽通知栏

ps:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

mHandler.post(new EnqueueNotificationRunnable(userId, r));

QuickSettings下拉栏

初始化QuickSettings

        // Set up the quick settings tile panel
        View container = mStatusBarWindow.findViewById(R.id.qs_frame);
        if (container != null) {
            FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
            ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
                    Dependency.get(ExtensionController.class)
                            .newExtension(QS.class)
                            .withPlugin(QS.class)
                            .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
                            .withDefault(QSFragment::new)
                            .build());
            final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                    mIconController);
            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
                    (visible) -> {
                        mBrightnessMirrorVisible = visible;
                        updateScrimController();
                    });
            fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                QS qs = (QS) f;
                if (qs instanceof QSFragment) {
                    ((QSFragment) qs).setHost(qsh);
                    mQSPanel = ((QSFragment) qs).getQsPanel();
                    mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
                    mKeyguardStatusBar.setQSPanel(mQSPanel);
                }
            });
        }

QSFragment中增加qs布局

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
        return inflater.inflate(R.layout.qs_panel, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mQSPanel = view.findViewById(R.id.quick_settings_panel);
        mQSDetail = view.findViewById(R.id.qs_detail);
        mHeader = view.findViewById(R.id.header);
        mFooter = view.findViewById(R.id.qs_footer);
        mContainer = view.findViewById(id.quick_settings_container);

        mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
        mQSAnimator = new QSAnimator(this,
                mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);

        mQSCustomizer = view.findViewById(R.id.qs_customize);
        mQSCustomizer.setQs(this);
        if (savedInstanceState != null) {
            setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
            setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
            int[] loc = new int[2];
            View edit = view.findViewById(android.R.id.edit);
            edit.getLocationInWindow(loc);
            int x = loc[0] + edit.getWidth() / 2;
            int y = loc[1] + edit.getHeight() / 2;
            mQSCustomizer.setEditLocation(x, y);
            mQSCustomizer.restoreInstanceState(savedInstanceState);
        }
        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
    }

在Fragment中添加view,QSPanle
1、QS排序
res/values/config.xml
ame="quick_settings_tiles_default" translatable="false">
        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane 

2、QS中的行列
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {}

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        final TunerService tunerService = Dependency.get(TunerService.class);
        tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);

        if (mHost != null) {
            setTiles(mHost.getTiles());
        }
        if (mBrightnessMirrorController != null) {
            mBrightnessMirrorController.addCallback(this);
        }
    }

    public void setTiles(Collection tiles) {
        setTiles(tiles, false);
    }

    public void setTiles(Collection tiles, boolean collapsedView) {
        if (!collapsedView) {
            mQsTileRevealController.updateRevealedTiles(tiles);
        }
        for (TileRecord record : mRecords) {
            mTileLayout.removeTile(record);
            record.tile.removeCallback(record.callback);
        }
        mRecords.clear();
        for (QSTile tile : tiles) {
            addTile(tile, collapsedView);
        }
    }

    protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
        final TileRecord r = new TileRecord();
        r.tile = tile;
        r.tileView = createTileView(tile, collapsedView);
        final QSTile.Callback callback = new QSTile.Callback() {
        ...
        }
}

ps:QSTileHost.java
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
        for (int i = 0; i < mQsFactories.size(); i++) {
            QSTileView view = mQsFactories.get(i).createTileView(tile, collapsedView);
            if (view != null) {
                return view;
            }
        }
        throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
    }

QSTileView.java
下拉图标的自定义控件布局

QSPanel是下拉栏的根 布局,在其中加载PagedTileLayout(ViewPager)

public class PagedTileLayout extends ViewPager implements QSTileLayout {
    @Override
    public boolean updateResources() {
        // Update bottom padding, useful for removing extra space once the panel page indicator is
        // hidden.
        setPadding(0, 0, 0,
                getContext().getResources().getDimensionPixelSize(
                        R.dimen.qs_paged_tile_layout_padding_bottom));

        boolean changed = false;
        for (int i = 0; i < mPages.size(); i++) {
            changed |= mPages.get(i).updateResources();
        }
        if (changed) {
            distributeTiles();
        }
        return changed;
    }
    
设置adapter
private void distributeTiles() {
        if (DEBUG) Log.d(TAG, "Distributing tiles");
        final int NP = mPages.size();
        for (int i = 0; i < NP; i++) {
            mPages.get(i).removeAllViews();
        }
        int index = 0;
        final int NT = mTiles.size();
        for (int i = 0; i < NT; i++) {
            TileRecord tile = mTiles.get(i);
            if (mPages.get(index).isFull()) {
                if (++index == mPages.size()) {
                    if (DEBUG) Log.d(TAG, "Adding page for "
                            + tile.tile.getClass().getSimpleName());
                    mPages.add((TilePage) LayoutInflater.from(getContext())
                            .inflate(R.layout.qs_paged_page, this, false));
                }
            }
            if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
                    + index);
            mPages.get(index).addTile(tile);
        }
        if (mNumPages != index + 1) {
            mNumPages = index + 1;
            while (mPages.size() > mNumPages) {
                mPages.remove(mPages.size() - 1);
            }
            if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
            mPageIndicator.setNumPages(mNumPages);
            setAdapter(mAdapter);
            mAdapter.notifyDataSetChanged();
            setCurrentItem(0, false);
        }
    }
    
内部类TileLayout 中定义行
public static class TilePage extends TileLayout {
        private int mMaxRows = 3;  行
        public TilePage(Context context, AttributeSet attrs) {
            super(context, attrs);
            updateResources();
        }

        @Override
        public boolean updateResources() {
            final int rows = getRows();
            boolean changed = rows != mMaxRows;
            if (changed) {
                mMaxRows = rows;
                requestLayout();
            }
            return super.updateResources() || changed;
        }

TileLayout.java
public boolean updateResources() {
        final Resources res = mContext.getResources();
        final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); 列
        mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
        mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
        mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
        mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
        mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
        if (mColumns != columns) {
            mColumns = columns;
            requestLayout();
            return true;
        }
        return false;
    }

3、QS中各个Tile实现
public abstract class QSTileImpl implements QSTile {}

LocationTile

1、首先tile需要继承QSTileImpl 
public class LocationTile extends QSTileImpl {}

2、添加interface 作为Controller类
public interface LocationController extends CallbackController {
boolean isLocationActive();
    boolean isLocationEnabled();
    boolean setLocationEnabled(boolean enabled);
    public interface LocationChangeCallback {
        default void onLocationActiveChanged(boolean active) {}
        default void onLocationSettingsChanged(boolean locationEnabled) {}
    }
}

3、接口的实现类ControllerImpl,这里一般都是用来接收广播信息来处理的流程
public class LocationControllerImpl extends BroadcastReceiver implements LocationController {}

4、在自定义的tile类中首先要在构造函数中加入Controller,然后还需要重写如下这些方法,其中handleUpdateState()方法是核心方法,这里去设置QS的state的各个状态等
public LocationTile(QSHost host) {
        super(host);
        mController = Dependency.get(LocationController.class);
        mKeyguard = Dependency.get(KeyguardMonitor.class);
}

QSTileImpl.java
    public abstract TState newTileState();
    abstract protected void handleClick();
    abstract protected void handleUpdateState(TState state, Object arg);

具体逻辑都在LocationTile.java

增加新的状态栏图标和控制逻辑

增加QS 手电筒和控制逻辑

信号塔刷新流程

public class MobileSignalController extends SignalController<
        MobileSignalController.MobileState, MobileSignalController.MobileIconGroup> {
内部类监听信号变化
class MobilePhoneStateListener extends PhoneStateListener {
    public MobilePhoneStateListener(int subId, Looper looper) {
        super(subId, looper);
    }

    @Override
    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
        if (DEBUG) {
            Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
                    ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
        }
        mSignalStrength = signalStrength;
        updateTelephony();
    }
}

你可能感兴趣的:(framework)