System status bar status bar analysis (Android o)
1 Overview
In the system UI, the status bar provides users with some quick information display and function operation. The user can display more information on the screen in a reasonable way through the drop-down bar. On the 848 platform, there are two configurable status bars: StatusBar and TvStatusBar. The default configuration uses the StatusBar suitable for small devices, but the platform does not use the status bar. Here we mainly analyze the process of creating and starting the StatusBar status bar of common small screen devices (mobile phones).
2 status bar analysis
2.1 status bar startup
SystemBars is responsible for starting the status bar component service, which is located in the list of SERVICES array constant SERVICES maintained in the SystemUIApplication class. The status bar is mainly through calling SystemBars Start () method.
Before the status bar starts, it will get config Create an instance of StatusBar from the XML configuration file, and then call StatusBar The start () method is used to create the status bar. Here you can modify config Configure the custom StatusBar in the XML file.
// frameworks\base\packages\SystemUI\res\values\config.xml <!-- Customizable StatusBar,Alternative configurations are also available, such as com.android.systemui.statusbar.tv.TvStatusBar --> <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
// frameworks\base\packages\SystemUI\src\com\android\systemui\SystemBars.java public class SystemBars extends SystemUI { private SystemUI mStatusBar; @Override public void start() { createStatusBarFromConfig(); } private void createStatusBarFromConfig() { // Get the above config XML configuration and create a StatusBar instance according to the full class name 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; cls = mContext.getClassLoader().loadClass(clsName); mStatusBar = (SystemUI) cls.newInstance(); mStatusBar.mContext = mContext; mStatusBar.mComponents = mComponents; mStatusBar.start(); // Call the start() method of StatusBar } ... }
StatusBar inherits from the base class SystemUI and implements many interfaces. The key interface here is CommandQueue Callbacks and CommandQueue inherit from istatusbar Stub, which internally maintains an array variable of callback type in StatusBar Start() will add the instance of StatusBar to this array to facilitate subsequent proxy calls to StatusBar.
In statusbar Start () is not only responsible for the creation of the status bar window, but also involves other related processes such as navigation bar and notification bar. Here, only the related processes of the status bar are analyzed for the time being.
StatusBar. The start () method mainly does several things:
- Initialize mWindowManager and mWindowManagerService variables
- Get the client agent instance of the StatusBarManagerService service. It is responsible for receiving the system service's request for the status bar and forwarding the request to the StatusBar. At the same time, it saves a copy of the relevant information that needs to be displayed in the status bar. This is to prevent information loss when the system UI crashes unexpectedly.
- Get the CommandQueue instance, add the StatusBar instance to the Callbacks array, and register the CommandQueue instance with the StatusBarManagerService, which is equivalent to maintaining a CommandQueue in the StatusBarManagerService instance, and then the CommandQueue inherits from istatusbar Stub, so the operation on the status bar will be handed over to CommandQueue for processing.
- Create a window for the status bar and navigation bar
- Handle the copied information of the status bar
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java public class StatusBar extends SystemUI implements CommandQueue.Callbacks,... { public void start() { ... // The status bar and navigation bar windows do not belong to any Activity, but are created by WindowManager mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = mWindowManager.getDefaultDisplay(); updateDisplaySize(); // Get WMS mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); // Get IStatusBarService instance mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); //CommandQueue inherits from istatiusbar Stub is the bridge between IStatusBarService and StatusBar mCommandQueue = getComponent(CommandQueue.class); // At commandqueuestart start() -> putComponent() mCommandQueue.addCallbacks(this); // Add the StatusBar instance to the Callbacks array of CommandQueue int[] switches = new int[9]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); ArrayList<String> iconSlots = new ArrayList<>(); ArrayList<StatusBarIcon> icons = new ArrayList<>(); Rect fullscreenStackBounds = new Rect(); Rect dockedStackBounds = new Rect(); // Register the StatusBar into the IStatusBarService system service, and take out the copy of relevant information that needs to be displayed and processed in the status bar // mCommandQueue is injected into IStatusBarService as a parameter mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds); // Create a window for the status bar and navigation bar, which will be analyzed later createAndAddWindows(); // Handle the copied information of the status bar int N = iconSlots.size(); int viewIndex = 0; for (int i=0; i < N; i++) { mCommandQueue.setIcon(iconSlots.get(i), icons.get(i)); } ... } }
// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java public void registerStatusBar(IStatusBar bar, List<String> iconSlots, List<StatusBarIcon> iconList, int switches[], List<IBinder> binders, Rect fullscreenStackBounds, Rect dockedStackBounds) { enforceStatusBarService(); // Save bar(CommandQueue) in mBar, and mBar will transfer some operation requests to the status bar to mBar later mBar = bar; ... // Populates the relevant copy information back into the parameters synchronized (mIcons) { for (String slot : mIcons.keySet()) { iconSlots.add(slot); iconList.add(mIcons.get(slot)); } } synchronized (mLock) { switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1); switches[1] = mSystemUiVisibility; switches[2] = mMenuVisible ? 1 : 0; ... } }
As a system service, StatusBarManagerService is in systemserver Run() - > startotherservices() this phase creates a start.
// frameworks\base\services\java\com\android\server\SystemServer.java private void startOtherServices() { ... if (!disableSystemUI) { // config.disable_systemui try { statusBar = new StatusBarManagerService(context, wm); ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); } catch (Throwable e) { reportWtf("starting StatusBarManagerService", e); } } ... }
So how does StatusBar establish a communication connection with StatusBarManagerService? It has also been analyzed above. Generally speaking, it is to maintain a CommandQueue instance in IStatusBarService, which is inherited from istatusbar Stub, so many requests for the status bar will actually be forwarded to the CommandQueue for processing after being forwarded to the StatusBarManagerService. That is to say, the CommandQueue is equivalent to the communication bridge between IStatusBarService and StatusBar. What's more, the requests received and processed by CommandQueue will be transferred to the internally maintained array variable mCallbacks of Callbacks type for related calls, and finally the related methods in the StatusBar instance will be called.
Here, take the call of collapsePanels() method provided by StatusBarManagerService as an example.
// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java private volatile IStatusBar mBar; @Override public void collapsePanels() { enforceExpandStatusBar(); if (mBar != null) { try { mBar.animateCollapsePanels(); // } catch (RemoteException ex) { } } }
CommandQueue class analysis
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\CommandQueue.java public class CommandQueue extends IStatusBar.Stub { private ArrayList<Callbacks> mCallbacks = new ArrayList<>(); public interface Callbacks { ... default void animateCollapsePanels(int flags) { } // ... default void toggleRecentApps() { } default void toggleSplitScreen() { } } public void animateCollapsePanels() { synchronized (mLock) { mHandler.removeMessages(MSG_COLLAPSE_PANELS); mHandler.obtainMessage(MSG_COLLAPSE_PANELS, 0, 0).sendToTarget(); } } private final class H extends Handler { public void handleMessage(Message msg) { final int what = msg.what & MSG_MASK; switch (what) { case MSG_COLLAPSE_PANELS: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).animateCollapsePanels(msg.arg1); } break; ... } } } }
Finally, it is called to statusbar Animatecollapsepanels() instance method
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java public void animateCollapsePanels(int flags) { animateCollapsePanels(flags, false /* force */, false /* delayed */, 1.0f /* speedUpFactor */); }
Take a brief look at the schematic diagram
2.2 status bar window creation
Then analyze how the status bar window is created and displayed
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java public void createAndAddWindows() { addStatusBarWindow(); } private void addStatusBarWindow() { makeStatusBarView(); // 1 create status bar control tree // Get the StatusBarWindowManager, which encapsulates the relevant logic of status bar window status management mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 2 add status bar window }
2.2.1 loading status bar control tree
To load the status bar control tree, you need to load super_status_bar.xml layout, and then analyze the status bar control tree from this layout file.
protected void makeStatusBarView() { final Context context = mContext; ... // Load layout r.layout super_ status_ bar // Mstatusbarwindow = (statusbarwindowview) view will be executed inflate(context,R.layout.super_status_bar, null); inflateStatusBarWindow(context); mStatusBarWindow.setService(this); // Set touch event listener mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); ... FragmentHostManager.get(mStatusBarWindow) .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { CollapsedStatusBarFragment statusBarFragment = (CollapsedStatusBarFragment) fragment; // R.layout.status_bar statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); mStatusBarView = (PhoneStatusBarView) fragment.getView(); mStatusBarView.setBar(this); mStatusBarView.setPanel(mNotificationPanel); mStatusBarView.setScrimController(mScrimController); setAreThereNotifications(); checkBarModes(); }).getFragmentManager() .beginTransaction() .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), CollapsedStatusBarFragment.TAG) .commit(); ... // QS settings panel View container = mStatusBarWindow.findViewById(R.id.qs_frame); if (container != null) { FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); fragmentHostManager.getFragmentManager().beginTransaction() .replace(R.id.qs_frame, new QSFragment(), QS.TAG) // .commit(); new PluginFragmentListener(container, QS.TAG, QSFragment.class, QS.class) .startListening(); final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mIconController); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); 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); } }); } }
2.2.2 create status bar window
Use StatusBarWindow as a control tree to create a status bar window. barHeight is the height of the status bar, which is obtained through getStatusBarHeight() method in frameworks \ base \ core \ res \ res \ values \ dimensions It can be set in the XML file. The default value is 25dp.
//frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBarWindowManager.java public void add(View statusBarView, int barHeight) { mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, // width barHeight, // Status bar height WindowManager.LayoutParams.TYPE_STATUS_BAR, // Window type FIRST_SYSTEM_WINDOW WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // The status bar does not accept key events | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING // Receive a touch event that causes the device to wake up | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH // The window will accept touch events beyond its boundaries and send them to other windows that also support split touch | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, // Responsible for drawing the logo of the system bar background. If set, the system bar will be drawn with a transparent background PixelFormat.TRANSLUCENT); // Support transparency mLp.token = new Binder(); mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); //Via WindowManager Addview() creates a window for the status bar ... }
2.3 status bar control tree analysis
2.3.1 super_status_bar.xml
StatusBarWindowView, as the root layout control, mainly contains status_bar.xml and status_bar_expanded.xml these two important layouts.
status_bar.xml is mainly used as the layout of the top icon bar, such as the display of system notification icon, system power, signal, time and other related icons.
status_bar_expanded.xml is the layout after the status bar is pulled down, such as the display of related layouts such as the top system icon, icon brightness bar, shortcut setting panel, bottom function editing bar, notification bar and so on.
// frameworks\base\packages\SystemUI\res\layout\super_status_bar.xml <com.android.systemui.statusbar.phone.StatusBarWindowView 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"> ... <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" android:layout_height="match_parent" android:importantForAccessibility="no" sysui:ignoreRightInset="true" sysui:scrimColor="@color/scrim_behind_color"/> <!-- Top icon bar frameworks\base\packages\SystemUI\res\layout\status_bar.xml--> <FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- Brightness bar frameworks\base\packages\SystemUI\res\layout\brightness_mirror.xml--> <include layout="@layout/brightness_mirror" /> <!-- Drop down status bar frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml--> <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> ... </com.android.systemui.statusbar.phone.StatusBarWindowView>
status_bar.xml
The layout is mainly responsible for displaying several kinds of information: notification information, time information, power information, signal information and system status icon area:
// frameworks\base\packages\SystemUI\res\layout\status_bar.xml <com.android.systemui.statusbar.phone.PhoneStatusBarView 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" ...> ... <LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" ...> <!-- Notification icon --> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal"> <!-- System Icon frameworks\base\packages\SystemUI\res\layout\system_icons.xml--> <include layout="@layout/system_icons" /> <!-- time --> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:layout_width="wrap_content" android:layout_height="match_parent" android:singleLine="true" android:paddingStart="@dimen/status_bar_clock_starting_padding" android:paddingEnd="@dimen/status_bar_clock_end_padding" android:gravity="center_vertical|start" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout> ... </com.android.systemui.statusbar.phone.PhoneStatusBarView>
system_icons.xml
Status bar system icon layout
// frameworks\base\packages\SystemUI\res\layout\system_icons.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/system_icons" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical"> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="horizontal"/> <!-- Signal correlation frameworks\base\packages\SystemUI\res\layout\signal_cluster_view.xml--> <include layout="@layout/signal_cluster_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/signal_cluster_margin_start"/> <!-- Electricity correlation --> <com.android.systemui.BatteryMeterView android:id="@+id/battery" android:layout_height="match_parent" android:layout_width="wrap_content" /> </LinearLayout>
2.3.2 status_bar_expanded.xml
Status bar drop-down layout
// frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml <com.android.systemui.statusbar.phone.NotificationPanelView android:id="@+id/notification_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" > ... <!-- Drop down shortcut settings panel layout --> <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:id="@+id/notification_container_parent" android:clipToPadding="false" android:clipChildren="false"> <!-- Quick settings panel frameworks\base\packages\SystemUI\res\layout\qs_panel.xml--> <FrameLayout android:id="@+id/qs_frame" android:layout="@layout/qs_panel" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:clipToPadding="false" android:clipChildren="false" systemui:viewType="com.android.systemui.plugins.qs.QS" /> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:layout_marginBottom="@dimen/close_handle_underlap" /> ... </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> ... </com.android.systemui.statusbar.phone.NotificationPanelView>
qs_panel.xml
<com.android.systemui.qs.QSContainerImpl xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/quick_settings_container" android:layout_width="match_parent" android:layout_height="wrap_content" ...> ... <!-- QS panel --> <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" android:layout_marginTop="28dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:elevation="4dp" android:background="#00000000" android:layout_marginBottom="48dp" /> <!-- Top status bar icon frameworks\base\packages\SystemUI\res\layout\quick_status_bar_expanded_header.xml--> <include layout="@layout/quick_status_bar_expanded_header" /> <!-- bottom toolbars frameworks\base\packages\SystemUI\res\layout\qs_footer.xml--> <include android:id="@+id/qs_footer" layout="@layout/qs_footer" /> ... <!-- QS Custom panel frameworks\base\packages\SystemUI\res\layout\qs_customize_panel.xml--> <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel" android:visibility="gone" /> </com.android.systemui.qs.QSContainerImpl>
2.4 status bar drop-down
2.4.1 pull down status
After understanding the layout of the status bar, let's take a look at the drop-down process of the status bar. The status bar drop-down can be divided into three states: folded state, extended state and QS panel extended state:
-
Folded state. In this state, the gesture clicks the status bar to lift, the state changes to extended, and then automatically returns to the folded state. This process is called peeping animation.
-
Extended status. In this state, drag down a certain distance to change the state to QS panel expansion state, and drag up to restore to folding state.
-
QS panel extension status. In this state, drag up the specified distance to restore the extended state and folded state.
2.4.2 drop down event
Respond to the status bar drop-down by listening for gestures, and analyze the View call chain in response to gesture events using AS Android debugger:
The relevant processing of the status bar drop-down is mainly in PanelView In ontouchevent(), the gesture response here can be divided into two types: one is the automatic recovery after clicking the drop-down, also known as peek peeping process, and the other is dragging the drop-down / pull-up. There are many code logics here, which do not need in-depth analysis.
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PanelView.java public boolean onTouchEvent(MotionEvent event) { ... // Capture the touch event and update the extended height according to the user's finger // If you just click, the drop-down status bar panel (PEEP) will be quickly displayed and closed int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { pointerIndex = 0; mTrackingPointer = event.getPointerId(pointerIndex); } final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { // Mgesturewaitfotouchslope will be used to mark whether the drop-down height of the drop-down status bar is 0 at each down event mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures(); } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Press // Initialize mInitialTouchX=x, InitialTouchY=y, mInitialOffsetOnTouch=mExpandedHeight=mExpandedHeight(0 when not pulled down) startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); mJustPeeked = false; // Indicates whether only peek animation is executed mMinExpandHeight = 0.0f; // Indicates whether the down event occurs when the drop-down status bar is closed (mxpandedfraction < = 0.0F or mxpandedheight = = 0.0F) mPanelClosedOnDown = isFullyCollapsed(); ... // Judge whether the drop-down height of the drop-down status bar is 0 if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning) || mPeekAnimator != null) { // mTouchSlopExceeded indicates whether the conservative value of finger movement distance is exceeded. Once it is exceeded, other operations can be done, such as expanding QS mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning) || mPeekAnimator != null; cancelHeightAnimator(); // Cancel drop-down animation cancelPeek(); // Unpick animation onTrackingStarted(); // Triggered when clicking, dragging and pulling (automatic recovery will not be triggered) } if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()) { startOpening(); // Triggered when you click the drop-down (perform peek animation, operate the drop-down height) } break; case MotionEvent.ACTION_MOVE: // drag trackMovement(event); float h = y - mInitialTouchY; // Check whether the y component of the gesture exceeds the conservative value of the finger movement distance if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX) || mIgnoreXTouchSlop)) { mTouchSlopExceeded = true; if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) { if (!mJustPeeked && mInitialOffsetOnTouch != 0f) { // If no folding or pull-down occurs startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); h = 0; } cancelHeightAnimator(); // Cancel drop-down animation onTrackingStarted(); } } float newHeight = Math.max(0, h + mInitialOffsetOnTouch); if (newHeight > mPeekHeight) { // Judge whether the drop-down height exceeds the drop-down height of peeping animation if (mPeekAnimator != null) { mPeekAnimator.cancel();// Unpick animation } mJustPeeked = false; // Set snoop flag to false } else if (mPeekAnimator == null && mJustPeeked) { // Peeping has been completed (peeping height has been reached), but it has not been dragged far mInitialOffsetOnTouch = mExpandedHeight; mInitialTouchY = y; mMinExpandHeight = mExpandedHeight; mJustPeeked = false; } newHeight = Math.max(newHeight, mMinExpandHeight); // Update height ... if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) { setExpandedHeightInternal(newHeight); // Drop down to the height of the gesture drag } break; case MotionEvent.ACTION_CANCEL: trackMovement(event); // Decide whether to execute the peeping animation according to the event of pressing and lifting. For example, pressing and lifting quickly will execute the peeping animation endMotionEvent(event, x, y, false /* forceCancel */); break; } return !mGestureWaitForTouchSlop || mTracking; }
3 customization of status bar
3.1 pull down width adaptation
Change the width of the drop-down status bar to fill the entire screen
android:layout_width = "@ dimension / notification_panel_width" changed to match_parent
// frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml <!-- android:layout_width="@dimen/notification_panel_width"--> <FrameLayout android:id="@+id/qs_frame" android:layout="@layout/qs_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" ... /> <!-- android:layout_width="@dimen/notification_panel_width" --> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="match_parent" android:layout_height="match_parent" ... />
android:layout_width = "@ dimension / notification_panel_width" changed to match_parent
Brightness bar width adaptation
// frameworks\base\packages\SystemUI\res\layout\brightness_mirror.xml <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/brightness_mirror" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="@integer/notification_panel_layout_gravity" android:visibility="invisible"> ... </FrameLayout>
3.2 volume bar addition
Add a volume bar below the brightness bar
// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\QSPanel.java protected final View mBrightnessView; protected final View mVolumeView; public QSPanel(Context context, AttributeSet attrs) { // Construction method super(context, attrs); mContext = context; setOrientation(VERTICAL); mBrightnessView = LayoutInflater.from(context).inflate(R.layout.quick_settings_brightness_dialog, this, false); addView(mBrightnessView); // Add volume bar control+ mVolumeView = LayoutInflater.from(context).inflate( R.layout.quick_settings_volume_dialog, this, false); addView(mVolumeView); ... // Here, you can customize the volume bar control function according to your needs }
Add volume bar layout file quick_settings_volume_dialog.xml, customized as needed
// frameworks\base\packages\SystemUI\res\layout\quick_settings_volume_dialog.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="48dp" android:layout_width="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp"> <ImageView android:id="@+id/volume_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="8dp" android:src="@drawable/ic_volume_remote" android:contentDescription="@null" android:visibility="gone" /> <com.android.systemui.settings.ToggleSliderView android:id="@+id/volume_slider" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" ... /> </LinearLayout>
3.3 QS icon addition
Add a screenshot function icon in the QS panel list icon, click the full screen screenshot, and long press the area screenshot.
Methods related to creating icons:
// SystemUI\src\com\android\systemui\qs\QSTileHost.java onTuningChanged(); 1. loadTileSpecs(); res.getString(R.string.quick_settings_tiles_default); 2. createTile(); return new ScreenshotTile(mHost);
- Add a screenshot string at the end of the relevant configuration. When creating an icon, it will be taken out for segmentation and created.
// frameworks\base\packages\SystemUI\res\values\config.xml <string name="quick_settings_tiles_default" translatable="false"> wifi,bt,cast,airplane,screenshot </string> <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,screenshot </string>
- Added ScreenshotTile class, which can modify relevant methods according to AirplaneModeTile.
// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\tileimpl\QSFactoryImpl.java public QSTile createTile(String tileSpec) { if (tileSpec.equals("wifi")) return new WifiTile(mHost); else if (tileSpec.equals("bt")) return new BluetoothTile(mHost); else if (tileSpec.equals("cell")) return new CellularTile(mHost); else if (tileSpec.equals("dnd")) return new DndTile(mHost); else if (tileSpec.equals("inversion")) return new ColorInversionTile(mHost); else if (tileSpec.equals("airplane")) return new AirplaneModeTile(mHost); ... else if (tileSpec.equals("screenshot")) return new ScreenshotTile(mHost); // Add ScreenshotTile class+ else { Log.w(TAG, "Bad tile spec: " + tileSpec); return null; } }
ScreenshotTile inherits from QSTileImpl and can modify icon related functions by rewriting methods, such as icon / text display, click / long press response, etc.
For the code calling the system screenshot function, please refer to frameworks \ base \ services \ core \ Java \ com \ Android \ server \ policy \ phonewindowmanager java .
// frameworks\base\packages\SystemUI\src\com\android\systemui\qs\tiles\ScreenshotTile.java public class ScreenshotTile extends QSTileImpl<BooleanState> { @Override public void handleClick() { // Click action mHost.collapsePanels(); mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); // Full screen shot mHandler.postDelayed(mScreenshotRunnable, SCREENSHOT_DELAY_MILLS); } @Override protected void handleLongClick() { // Long press operation mHost.collapsePanels(); mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_SELECTED_REGION); //Area screenshot mHandler.postDelayed(mScreenshotRunnable, SCREENSHOT_DELAY_MILLS); } @Override public CharSequence getTileLabel() { // Icon text display return mContext.getString(R.string.quick_settings_screenshot_label); } @Override protected void handleUpdateState(BooleanState state, Object arg) { // Status refresh state.icon = mIcon; // Icon state.label = mContext.getString(R.string.quick_settings_screenshot_label); // written words state.contentDescription = mContext.getString(R.string.quick_settings_screenshot_label); ... } ... }
3.4 QS bottom toolbar icon hiding
// frameworks\base\packages\SystemUI\res\layout\qs_footer.xml <com.android.systemui.qs.QSFooter xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/header" android:layout_width="match_parent" android:layout_height="48dp" ...> <!-- Date display --> <include android:id="@+id/date_time_alarm_group" layout="@layout/status_bar_alarm_group" android:layout_marginStart="16dp" android:layout_marginEnd="8dp" .../> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" ... android:gravity="end"> <!-- Hide multi-user toggle button --> <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="48dp" android:layout_height="48dp" ...> <ImageView android:id="@+id/multi_user_avatar" android:layout_width="@dimen/multi_user_avatar_expanded_size" android:layout_height="@dimen/multi_user_avatar_expanded_size" android:layout_gravity="center" android:scaleType="centerInside"/> </com.android.systemui.statusbar.phone.MultiUserSwitch> <!--Hide customization QS Icon entry button --> <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@android:id/edit" android:layout_width="48dp" android:layout_height="48dp" android:background="?android:attr/selectableItemBackgroundBorderless" .../> <!-- Set button --> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/settings_button_container" android:layout_width="48dp" android:layout_height="48dp"> <com.android.systemui.statusbar.phone.SettingsButton android:id="@+id/settings_button" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ripple_drawable" .../> <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/tuner_icon" android:layout_width="match_parent" android:layout_height="match_parent" ... android:visibility="invisible"/> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> <!-- Status bar extension Icon --> <com.android.systemui.statusbar.phone.ExpandableIndicator android:id="@+id/expand_indicator" android:layout_width="48dp" android:layout_height="48dp" ... /> </LinearLayout> <!-- The split line will QS Separate from notice --> <include layout="@layout/qs_divider" android:layout_width="match_parent" android:layout_height="1dp" android:layout_gravity="bottom" /> </com.android.systemui.qs.QSFooter>
Setting android:visibility = "gone" in the layout file will not hide successfully. It needs to be hidden in relevant code as needed.
frameworks\base\packages\SystemUI\src\com\android\systemui\qs\QSFooter.java protected void onFinishInflate() { super.onFinishInflate(); Resources res = getResources(); // Hide these controls as needed mEdit = findViewById(android.R.id.edit); mDateTimeGroup = findViewById(id.date_time_alarm_group); mDate = findViewById(R.id.date); mExpandIndicator = findViewById(R.id.expand_indicator); mExpandIndicator.setVisibility( res.getBoolean(R.bool.config_showQuickSettingsExpandIndicator) ? VISIBLE : GONE); mSettingsButton = findViewById(R.id.settings_button); mSettingsContainer = findViewById(R.id.settings_button_container); mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed); mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar); ... }
private void updateVisibilities() { updateAlarmVisibilities(); mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE); final boolean isDemo = UserManager.isDeviceInDemoMode(mContext); /*mMultiUserSwitch.setVisibility((mExpanded || mAlwaysShowMultiUserSwitch) && mMultiUserSwitch.hasMultipleUsers() && !isDemo ? View.VISIBLE : View.INVISIBLE); if (mShowEditIcon) { mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE); }*/ // Hide multi-user toggle button mMultiUserSwitch.setVisibility(GONE); // Hide custom QS icon entry button mEdit.setVisibility(GONE); ... }