Android 8.1 SystemUI之状态栏、下拉菜单通知、导航栏分析(一)

一、SystemUI简要介绍
作为Android系统核心应用,SystemUI负责反馈系统及应用状态并与用户保持大量的交互,功能代码逻辑也非常复杂。功能代码模块如下图:
在这里插入图片描述

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

接下来我们针对状态栏、导航栏、下拉列表通知栏做简要分析。

二、SystemUI代码功能模块简要说明
代码路径:framework/base/packages/SystemUI。那SystemUI是什么时候启动的呢,由谁来调用,答案是在 SystemServer 进程服务里。我们看一下相关代码:

mActivityManagerService.systemReady(() -> {

....................

....................

traceBeginAndSlog("StartSystemUI");

try {

startSystemUi(context, windowManagerF);

} catch (Throwable e) {

reportWtf("starting System UI", e);

}


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();

}

可以看到在系统启动完成后或调用startSystemUi方法。然后启动SystemUI。接下来看开SystemUIService中做了哪些操作。看onCreate方法里有一句 ((SystemUIApplication) getApplication()).startServicesIfNeeded(); 在看SystemUIApplication中的这个方法

/**
 * The classes of the stuff to start.
 */
private final Class<?>[] SERVICES = new Class[] {
        Dependency.class, //包含了很多各种数据操作类,单例获取
 NotificationChannels.class,
 CommandQueue.CommandQueueStart.class,
 KeyguardViewMediator.class,
 Recents.class, //近期任务
 VolumeUI.class,//音量相关
 Divider.class,//分屏
 SystemBars.class,//状态栏导航栏,下拉菜单通知面板
 StorageNotification.class,
 PowerUI.class, //电源界面
 RingtonePlayer.class,
 KeyboardUI.class,
 PipUI.class, //画中画
 ShortcutKeyDispatcher.class,
 VendorServices.class,
 GarbageMonitor.Service.class,
 LatencyTester.class,
 GlobalActionsComponent.class,
 RoundedCorners.class,
};
public void startServicesIfNeeded() {
    startServicesIfNeeded(SERVICES);
}
private void startServicesIfNeeded(Class<?>[] services) {
if (mServicesStarted) {
return;
}

if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}

Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin("StartServices");
final int N = services.length;
for (int i = 0; i < N; i++) {
Class<?> cl = services[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
log.traceBegin("StartServices" + cl.getSimpleName());
long ti = System.currentTimeMillis();
try {

Object newService = SystemUIFactory.getInstance().createInstance(cl);
mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}

mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
log.traceEnd();

// Warn if initialization of component takes too long
ti = System.currentTimeMillis() - ti;
if (ti > 1000) {
Log.w(TAG, "Initialization of " + cl.getName() + " took " + ti + " ms");
}
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
log.traceEnd();

....
}

上面的操作主要对各个模块功能进行创建启动,调用start方法。 这篇文章主要对SystemBars.class做主要分析,这个模块也是 SystemUI中最为复杂的模块,熟悉了找个模块基本了一半的功能。刚提到会调用SystemBar中的onCreate方法。我们看看onCreate方法中做了哪些操作

@Override
public void start() {
    if (DEBUG) Log.d(TAG, "start");
 createStatusBarFromConfig();
}
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方法。所以针对本篇文章主题主要对StartBar这个类扩展然后进行简要分析。

三、状态栏、通知逻辑分析
接着上节的分析,我们看StartBar 的start方法做了哪些操作,这个类非常复杂,有7000多行代码。所以我简要挑重点来讲一下。

@Override
public void start() {
    mNetworkController = Dependency.get(NetworkController.class); //网络控制,实现类NetworkControllerImpl 主要对手机wifi、移动网络、以太网、是否飞行模式状态逻辑处理判断
 mUserSwitcherController = Dependency.get(UserSwitcherController.class);用户切换功能逻辑控制,安卓是多用户系统,里面有用户添加切换删除等逻辑判断
 mScreenLifecycle = Dependency.get(ScreenLifecycle.class);//屏幕周期判断,是否屏幕亮屏关屏
 mScreenLifecycle.addObserver(mScreenObserver);
 mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);//手机休眠状态回调判断
 mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
 mBatteryController = Dependency.get(BatteryController.class);//电池电量,是否省电模式
 mAssistManager = Dependency.get(AssistManager.class);//SystemUI帮助类,在这主要是一些动画控制
 mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);在这判断是否在dreaming状态,手机空闲状态
 mOverlayManager = IOverlayManager.Stub.asInterface(
            ServiceManager.getService(Context.OVERLAY_SERVICE)); //判断设置安卓是否是暗黑模式

 mColorExtractor = Dependency.get(SysuiColorExtractor.class);
 mColorExtractor.addOnColorsChangedListener(this);//壁纸颜色监听

 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

 mForegroundServiceController = Dependency.get(ForegroundServiceController.class);//前台服务通知管理

 mDisplay = mWindowManager.getDefaultDisplay();
 updateDisplaySize();

 Resources res = mContext.getResources();
 mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);//这个带有scrim的都是一些阴影效果功能控制
 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);//是否有删除所有通知功能
 mAlwaysExpandNonGroupedNotification =
            res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);同一个组的通知是否要一直展开

 DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));//日期
 putComponent(StatusBar.class, this);

 // start old BaseStatusBar.start().
 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
            Context.DEVICE_POLICY_SERVICE);//锁屏中的一些逻辑判断,锁屏是否可以打开摄像头等

 mNotificationData = new NotificationData(this);//通知的实体类,所有通知的数据存储
 mMessagingUtil = new NotificationMessagingUtil(mContext);//通知信息帮助工具

 mAccessibilityManager = (AccessibilityManager)
            mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); //主要一些辅助性的功能,帮助盲人

 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 

 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
 mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);//观察用户是否完成了新手引导
 mContext.getContentResolver().registerContentObserver(
            Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
 mSettingsObserver);//勿打扰模式观察
 mContext.getContentResolver().registerContentObserver(
            Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
 mLockscreenSettingsObserver,
 UserHandle.USER_ALL);
 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
 false,
 mSettingsObserver,
 UserHandle.USER_ALL);//锁屏是否显示通知观察
 }

    mContext.getContentResolver().registerContentObserver(
            Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
 true,
 mLockscreenSettingsObserver,
 UserHandle.USER_ALL);//锁屏通知隐藏一些重要数据观察

 mBarService = IStatusBarService.Stub.asInterface(
            ServiceManager.getService(Context.STATUS_BAR_SERVICE));//这个重点了,这个是我们监听StausBarService的一些数据回调

 mRecents = getComponent(Recents.class);//近期任务

 final Configuration currentConfig = res.getConfiguration();
 mLocale = currentConfig.locale;
 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);//layout方向,从左到右,从右到左

 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);//安卓用户管理
 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);//锁屏管理
 mLockPatternUtils = new LockPatternUtils(mContext);

 // Connect in to the status bar manager service
 mCommandQueue = getComponent(CommandQueue.class);//继承 IStatusBar.Stub
mCommandQueue.addCallbacks(this);//添加数据监听回调

int[] switches = new int[9];//一些禁用列表
ArrayList<IBinder> binders = new ArrayList<>();
ArrayList<String> iconSlots = new ArrayList<>();图标名称
ArrayList<StatusBarIcon> icons = new ArrayList<>();图标
Rect fullscreenStackBounds = new Rect();
Rect dockedStackBounds = new Rect();
try {
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);//向服务注册
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}

createAndAddWindows();//这个重点方法,创建相关的视图

mSettingsObserver.onChange(false); // set up
mCommandQueue.disable(switches[0], switches[6], false /* animate */);
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));
} 
// Set up the initial notification state. 重点注册通知,其他程序通过NotificationManager发送通知我这里接受到,注册监听
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}


if (DEBUG) {
Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
icons.size(),
switches[0],
switches[1],
switches[2],
switches[3]
));
}

mCurrentUserId = ActivityManager.getCurrentUser();
setHeadsUpUser(mCurrentUserId);
//用户状态广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiver(mBaseBroadcastReceiver, filter);
//通知相关广播
IntentFilter internalFilter = new IntentFilter();
internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
internalFilter.addAction(BANNER_ACTION_CANCEL);
internalFilter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
//屏幕锁屏状态
IntentFilter allUsersFilter = new IntentFilter();
allUsersFilter.addAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();

IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
try {
vrManager.registerListener(mVrStateCallbacks);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}

mNonBlockablePkgs = new HashSet<>();
Collections.addAll(mNonBlockablePkgs, res.getStringArray(
com.android.internal.R.array.config_nonBlockableNotificationPackages));
// end old BaseStatusBar.start().

mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
// TODO: use MediaSessionManager.SessionListener to hook us up to future updates
// in session state
//系统图标管理也比较重要,如wifi、蓝牙、闹钟等
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
mSettingsObserver.onChange(false); // set up
//弹出式的通知
mHeadsUpObserver.onChange(true); // set up
if (ENABLE_HEADS_UP) {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
mHeadsUpObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
mHeadsUpObserver);
}
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mUnlockMethodCache.addListener(this);
startKeyguard();

KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
putComponent(DozeHost.class, mDozeServiceHost);

notifyUserAboutHiddenNotifications();
//屏幕pinning请求
mScreenPinningRequest = new ScreenPinningRequest(mContext);
mFalsingManager = FalsingManager.getInstance(mContext);

Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
//屏幕配置简体
Dependency.get(ConfigurationController.class).addCallback(this);
}

在onCreate中做了很多初始化的操作,里面包含了很多种状态的监听控制还是很复杂的,接下来我们分析状态栏是怎么加载处理的,还有通知的一些控制实现。进入到 createAndAddWindows()开始分析吧。

public void createAndAddWindows() {
    addStatusBarWindow();
}

private void addStatusBarWindow() {
    makeStatusBarView();
 mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);//管理状态栏,导航栏、面板的状态切换
 mRemoteInputController = new RemoteInputController(mHeadsUpManager);//弹出式通知快捷输入
 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//这里把整个View添加到window中去
}

我们重点看看makeStatusBarView() 这个方法

protected void makeStatusBarView() {
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();

inflateStatusBarWindow(context); //mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); 创建个布局,总的布局,等下我们具体分析下

.....

 mNotificationIconAreaController = SystemUIFactory.getInstance()
            .createNotificationIconAreaController(context, this);//状态栏左半部分的通知栏控制器
 inflateShelf();//初始化面板通知栏的子布局
mNotificationIconAreaController.setupShelf(mNotificationShelf);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(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);
 mStatusBarView.setScrimController(mScrimController);
 mStatusBarView.setBouncerShowing(mBouncerShowing);
 setAreThereNotifications();
 checkBarModes();
 /// M: add for plmn display feature @{
 attachPlmnPlugin();
 ///@}
 }).getFragmentManager()
        .beginTransaction()
        .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
 CollapsedStatusBarFragment.TAG)
        .commit();

...
}

我们先看上面这一小段代码,其他先不分析,上面可以看到把super_status_bar中的status_bar_container替换成了CollapsedStatusBarFragment,这个Fragment其实就是状态栏相关的布局了。状态栏中布局主要包含以下

//根布局 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"
 android:background="@drawable/system_bar_background"
 android:orientation="vertical"
 android:focusable="false"
 android:descendantFocusability="afterDescendants"
 >

 <ImageView
 android:id="@+id/notification_lights_out"
 android:layout_width="@dimen/status_bar_icon_size"
 android:layout_height="match_parent"
 android:paddingStart="6dip"
 android:paddingBottom="2dip"
 android:src="@drawable/ic_sysbar_lights_out_dot_small"
 android:scaleType="center"
 android:visibility="gone"
 />

 <LinearLayout android:id="@+id/status_bar_contents"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingStart="6dp"
 android:paddingEnd="8dp"
 android:orientation="horizontal"
 >

 <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->//状态栏坐车通知栏
 <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"
 >
//系统图标相关的布局
 <include layout="@layout/system_icons" />
//时间
 <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>

 <ViewStub
 android:id="@+id/emergency_cryptkeeper_text"
 android:layout_width="wrap_content"
 android:layout_height="match_parent"
 android:layout="@layout/emergency_cryptkeeper_text"
 />

</com.android.systemui.statusbar.phone.PhoneStatusBarView>

首先分析通知图标怎么加载出来,看上面代码有一句

statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); 我们跟进去看这里面的操作

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);
}
可以看到NotificationIconAreaController 有个getNotificationInnerAreaView()方法把通知的View添加进去了,我简要的分析找个类的一些功能

public NotificationIconAreaController(Context context, StatusBar statusBar) {
mStatusBar = statusBar;
mNotificationColorUtil = NotificationColorUtil.getInstance(context);
mContext = context;

initializeNotificationAreaViews(context);
}

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

/**
* Initializes the views that will represent the notification area.
*/
protected void initializeNotificationAreaViews(Context context) {
reloadDimens(context);

LayoutInflater layoutInflater = LayoutInflater.from(context);
mNotificationIconArea = inflateIconArea(layoutInflater);



//这个就是具体的去加载的通知区域的View了.即上面返回的View,这个是个自定义的FrameLayout
mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
R.id.notificationIcons);

mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
}

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

我们看看通知的数据从哪里来的,我们发现了这个类的下面方法

/**
 * Updates the notifications with the given list of notifications to display. NotificationData前面已经说过了所有通知数据都封装在找个类中
 */
public void updateNotificationIcons(NotificationData notificationData) {

    updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
 false /* showAmbient */);
 updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
 NotificationShelf.SHOW_AMBIENT_ICONS);

 applyNotificationIconsTint();
}
//这个就是具体的根据数据来更新通知icon了 

private void updateIconsForLayout(NotificationData notificationData,
 Function<NotificationData.Entry, StatusBarIconView> function,
 NotificationIconContainer hostLayout, boolean showAmbient) {
    ArrayList<StatusBarIconView> toShow = new ArrayList<>(
            mNotificationScrollLayout.getChildCount());

 // Filter out ambient notifications and notification children.
 for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
        View view = mNotificationScrollLayout.getChildAt(i);
 if (view instanceof ExpandableNotificationRow) {
            NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
 if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
                toShow.add(function.apply(ent));
 }
        }
    }

    // In case we are changing the suppression of a group, the replacement shouldn't flicker
 // and it should just be replaced instead. We therefore look for notifications that were
 // just replaced by the child or vice-versa to suppress this.

 ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>();
 ArrayList<View> toRemove = new ArrayList<>();
 for (int i = 0; i < hostLayout.getChildCount(); i++) {
        View child = hostLayout.getChildAt(i);
 if (!(child instanceof StatusBarIconView)) {
            continue;
 }
        if (!toShow.contains(child)) {
            boolean iconWasReplaced = false;
 StatusBarIconView removedIcon = (StatusBarIconView) child;
 String removedGroupKey = removedIcon.getNotification().getGroupKey();
 for (int j = 0; j < toShow.size(); j++) {
                StatusBarIconView candidate = toShow.get(j);
 if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))
                        && candidate.getNotification().getGroupKey().equals(removedGroupKey)) {
                    if (!iconWasReplaced) {
                        iconWasReplaced = true;
 } else {
                        iconWasReplaced = false;
 break;
 }
                }
            }
            if (iconWasReplaced) {
                ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey);
 if (statusBarIcons == null) {
                    statusBarIcons = new ArrayList<>();
 replacingIcons.put(removedGroupKey, statusBarIcons);
 }
                statusBarIcons.add(removedIcon.getStatusBarIcon());
 }
            toRemove.add(removedIcon);
 }
    }
    // removing all duplicates
 ArrayList<String> duplicates = new ArrayList<>();
 for (String key : replacingIcons.keySet()) {
        ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key);
 if (statusBarIcons.size() != 1) {
            duplicates.add(key);
 }
    }
    replacingIcons.removeAll(duplicates);
 hostLayout.setReplacingIcons(replacingIcons);

 final int toRemoveCount = toRemove.size();
 for (int i = 0; i < toRemoveCount; i++) {
        hostLayout.removeView(toRemove.get(i));
 }

    final FrameLayout.LayoutParams params = generateIconLayoutParams();
 for (int i = 0; i < toShow.size(); i++) {
        View v = toShow.get(i);
 // The view might still be transiently added if it was just removed and added again
 hostLayout.removeTransientView(v);
 if (v.getParent() == null) {
            hostLayout.addView(v, i, params);
 }
    }

    hostLayout.setChangingViewPositions(true);
 // Re-sort notification icons
 final int childCount = hostLayout.getChildCount();
 for (int i = 0; i < childCount; i++) {
        View actual = hostLayout.getChildAt(i);
 StatusBarIconView expected = toShow.get(i);
 if (actual == expected) {
            continue;
 }
        hostLayout.removeView(expected);
 hostLayout.addView(expected, i);
 }
    hostLayout.setChangingViewPositions(false);
 hostLayout.setReplacingIcons(null);
}
谁去调用找个方法呢,答案是StatuBar这个类中,这个类在第一节onCreate中初始化了一堆数据,其中就包含了注册用户发送notification的监听回调

private final NotificationListenerWithPlugins mNotificationListener =
        new NotificationListenerWithPlugins() {

//每次有新的通知都会回调找个方法,添加或者更新通知
    @Override
 public void onNotificationPosted(final StatusBarNotification sbn,
 final RankingMap rankingMap) {
        if (true/**DEBUG*/) Log.d(TAG, "onNotificationPosted: " + sbn);
 if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            mHandler.post(new Runnable() {
                @Override
 public void run() {
                    processForRemoteInput(sbn.getNotification());
 String key = sbn.getKey();
 mKeysKeptForRemoteInput.remove(key);
 boolean isUpdate = mNotificationData.get(key) != null;
 // In case we don't allow child notifications, we ignore children of
 // notifications that have a summary, since we're not going to show them
 // anyway. This is true also when the summary is canceled,
 // because children are automatically canceled by NoMan in that case.
 if (!ENABLE_CHILD_NOTIFICATIONS
 && mGroupManager.isChildInGroupWithSummary(sbn)) {
                        if (DEBUG) {
                            Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
 }

                        // Remove existing notification to avoid stale data.
 if (isUpdate) {
                            removeNotification(key, rankingMap);
 } else {
                            mNotificationData.updateRanking(rankingMap);
 }
                        return;
 }
                    try {
                        if (isUpdate) {
                            updateNotification(sbn, rankingMap);
 } else {
                            addNotification(sbn, rankingMap);
 }
                    } catch (InflationException e) {
                        handleInflationException(sbn, e);
 }
                }
            });
 }
    }
//通知移除,
    @Override
 public void onNotificationRemoved(StatusBarNotification sbn,
 final RankingMap rankingMap) {
        if (true/**DEBUG*/) Log.d(TAG, "onNotificationRemoved: " + sbn);
 if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
            final String key = sbn.getKey();
 mHandler.post(() -> removeNotification(key, rankingMap));
 }
    }
//通知排序更新
    @Override
 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onRankingUpdate");
 if (rankingMap != null) {
            RankingMap r = onPluginRankingUpdate(rankingMap);
 mHandler.post(() -> updateNotificationRanking(r));
 }
    }

};

大家感兴趣再去研究 removeNotification、addNotification等相关方法,反正最终会调用上面提到的方法去更新状态栏通知,找个逻辑也要讲半天时间紧迫。再接下来看开系统通知怎么加载的吧。

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">
//系统icon容器
 <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"/>
//信号相关,手机卡信号,是否飞行模式,wifi
 <include layout="@layout/signal_cluster_view"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
//电池
 <com.android.systemui.BatteryMeterView android:id="@+id/battery"
 android:layout_height="match_parent"
 android:layout_width="wrap_content"
 />
</LinearLayout>

我简要分析系统icon数据怎么加载到这个容器中,其他的不做讲解了。看到 CollapsedStatusBarFragment中的onViewCreated方法

@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));//把系统icon容器传到DarkIConManager中,
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); 然后StatusBarIconControl添加找个icon管理器,这两句代码很重要,我们从这两句代码去分析整个加载流程
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
// Default to showing until we know otherwise.
showSystemIconArea(false);
initEmergencyCryptkeeperText();
}

首先看开DarkIconManager,这个是StatusBarIconController的内部类,主要管理创建系统icon控件StatusBarIconView,并把它添加到容器中去

public static class DarkIconManager extends IconManager {
    private final DarkIconDispatcher mDarkIconDispatcher;
 private int mIconHPadding;

 public DarkIconManager(LinearLayout linearLayout) {
        super(linearLayout);
 mIconHPadding = mContext.getResources().getDimensionPixelSize(
                R.dimen.status_bar_icon_padding);
 mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
 }
//根据系统icon名称 icon数据添加创建View
    @Override
 protected void onIconAdded(int index, String slot, boolean blocked,
 StatusBarIcon icon) {
        StatusBarIconView v = addIcon(index, slot, blocked, icon);
 mDarkIconDispatcher.addDarkReceiver(v);
 }

    @Override
 protected LayoutParams onCreateLayoutParams() {
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
 lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
 return lp;
 }

    @Override
 protected void destroy() {
        for (int i = 0; i < mGroup.getChildCount(); i++) {
            mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i));
 }
        mGroup.removeAllViews();
 }

    @Override
 protected void onRemoveIcon(int viewIndex) {
        mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex));
 super.onRemoveIcon(viewIndex);
 }

    @Override
 public void onSetIcon(int viewIndex, StatusBarIcon icon) {
        super.onSetIcon(viewIndex, icon);
 mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex));
 }
}
/**
 * Turns info from StatusBarIconController into ImageViews in a ViewGroup.
 */
public static class IconManager {
    protected final ViewGroup mGroup;
 protected final Context mContext;
 protected final int mIconSize;

 public IconManager(ViewGroup group) {
        mGroup = group;
 mContext = group.getContext();
 mIconSize = mContext.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.status_bar_icon_size);
 }

    protected void onIconAdded(int index, String slot, boolean blocked,
 StatusBarIcon icon) {
        addIcon(index, slot, blocked, icon);
 }

    protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
 StatusBarIcon icon) {
        StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
 view.set(icon);
 mGroup.addView(view, index, onCreateLayoutParams());
 return view;
 }

    @VisibleForTesting
 protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
        return new StatusBarIconView(mContext, slot, null, blocked);
 }

    protected LinearLayout.LayoutParams onCreateLayoutParams() {
        return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
 }

    protected void destroy() {
        mGroup.removeAllViews();
 }

    protected void onIconExternal(int viewIndex, int height) {
        ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex);
 imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
 imageView.setAdjustViewBounds(true);
 setHeightAndCenter(imageView, height);
 }

    protected void onDensityOrFontScaleChanged() {
        for (int i = 0; i < mGroup.getChildCount(); i++) {
            View child = mGroup.getChildAt(i);
 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
 child.setLayoutParams(lp);
 }
    }

    private void setHeightAndCenter(ImageView imageView, int height) {
        ViewGroup.LayoutParams params = imageView.getLayoutParams();
 params.height = height;
 if (params instanceof LinearLayout.LayoutParams) {
            ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
 }
        imageView.setLayoutParams(params);
 }

    protected void onRemoveIcon(int viewIndex) {
        mGroup.removeViewAt(viewIndex);
 }

    public void onSetIcon(int viewIndex, StatusBarIcon icon) {
        StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
 view.set(icon);
 }
}
那我们哪些系统icon数据怎么产生的呢,在第一节StatusBar中onCreate中有一句

mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);PhoneStatusBarPolicy这个类就管理了一系列的系统相关图标的状态加载,我们进去看这个类做了哪些操作

在构造方法中初始化了许多操作,并监听系统交互各种广播状态变化,以便对其显示或隐藏icon

public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
    mContext = context;
 mIconController = iconController;
 mCast = Dependency.get(CastController.class);//是否有截屏录屏、无线投屏这两个功能状态的监听
 mHotspot = Dependency.get(HotspotController.class);//热点状态控制监听
 mBluetooth = Dependency.get(BluetoothController.class);//蓝牙功能状态监听
 mNextAlarm = Dependency.get(NextAlarmController.class);//闹钟功能状态监听
 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
 mUserInfoController = Dependency.get(UserInfoController.class);//用户信息获取监听
 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 mRotationLockController = Dependency.get(RotationLockController.class);//是否是自动旋屏
 mDataSaver = Dependency.get(DataSaverController.class);//后台限制数据流量
 mZenController = Dependency.get(ZenModeController.class);//勿打扰模式监听控制
 mProvisionedController = Dependency.get(DeviceProvisionedController.class);//用户是否完成新手引导监听
 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);//锁屏状态监听
 mLocationController = Dependency.get(LocationController.class);//定位服务状态监听控制
//下面这些状态就是系统图标的标识字符串
 mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
 mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
 mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
 mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
 mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
 mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
 mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
 mSlotManagedProfile = context.getString(
            com.android.internal.R.string.status_bar_managed_profile);
 mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
 mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
 mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
 mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
//监听一些广播与上面状态有关
 // listen for broadcasts
 IntentFilter filter = new IntentFilter();
 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
 filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
 filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

 // listen for user / profile change.
 try {
        ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
 } catch (RemoteException e) {
        // Ignore
 }
//文字传语音状态
    // TTY status
 updateTTY();
//蓝牙
 // bluetooth status
 updateBluetooth();
//这个mIconController就是StatusBarIconControllerImpl对象。设置icon和控制icon是否可见,等下我们跟进去看看具体的怎么添加
 // Alarm clock
 mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
 mIconController.setIconVisibility(mSlotAlarmClock, false);

 // zen
 mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);
 mIconController.setIconVisibility(mSlotZen, false);

 // volume
 mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
 mIconController.setIconVisibility(mSlotVolume, false);
 updateVolumeZen();

 // cast
 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
 mIconController.setIconVisibility(mSlotCast, false);

 // hotspot
 mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
 mContext.getString(R.string.accessibility_status_bar_hotspot));
 mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());

 // managed profile
 mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
 mContext.getString(R.string.accessibility_managed_profile));
 mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);

 // data saver
 mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
 context.getString(R.string.accessibility_data_saver_on));
 mIconController.setIconVisibility(mSlotDataSaver, false);
//对上面的添加状态回调,以便设置icon是否可见
 mRotationLockController.addCallback(this);
 mBluetooth.addCallback(this);
 mProvisionedController.addCallback(this);
 mZenController.addCallback(this);
 mCast.addCallback(mCastCallback);
 mHotspot.addCallback(mHotspotCallback);
 mNextAlarm.addCallback(mNextAlarmCallback);
 mDataSaver.addCallback(this);
 mKeyguardMonitor.addCallback(this);
 mLocationController.addCallback(this);

 SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
 SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener);

 // Clear out all old notifications on startup (only present in the case where sysui dies)
 NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
 for (StatusBarNotification notification : noMan.getActiveNotifications()) {
        if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) {
            noMan.cancel(notification.getTag(), notification.getId());
 }
    }
    DockedStackExistsListener.register(exists -> {
        mDockedStackExists = exists;
 updateForegroundInstantApps();
 });
}

接下来看看setIcon方法怎么设置的,先看其构造方法,这个类继承自StatusBarIconList,管理系统图标、系统标识的添加移除


public StatusBarIconControllerImpl(Context context) {

//设置有哪些系统图标标识,从配置文件中加载出来
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
Dependency.get(ConfigurationController.class).addCallback(this);

//这个是是否是暗黑主题相关,以便更新系统图标颜色
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mContext = context;

loadDimens();

SysUiServiceProvider.getComponent(context, CommandQueue.class)
.addCallbacks(this);
Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
}

然后去看setIcon方法

@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
//根据标识文字获取到标识的下标,根据标识的下标来得到一个图标数据封装类StatusBarIcon
    int index = getSlotIndex(slot);
 StatusBarIcon icon = getIcon(index);
 if (icon == null) {
        icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
 Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
 setIcon(slot, icon);
 } else {
        icon.icon = Icon.createWithResource(mContext, resourceId);
 icon.contentDescription = contentDescription;
 handleSet(index, icon);
 }
}
我们在看看setIcon方法

@Override
public void setIcon(String slot, StatusBarIcon icon) {
    setIcon(getSlotIndex(slot), icon);
}

@Override
public void setIcon(int index, StatusBarIcon icon) {
if (icon == null) {
removeIcon(index);
return;
}

//如果icon没有添加就去添加,否则只是更新这个状态
boolean isNew = getIcon(index) == null;
super.setIcon(index, icon);//保存在List中以便下次使用
if (isNew) {
addSystemIcon(index, icon);
} else {
handleSet(index, icon);
}
}

//找个就是具体的添加了

private void addSystemIcon(int index, StatusBarIcon icon) {
String slot = getSlot(index);
int viewIndex = getViewIndex(index);

//是否是需要阻断的不添加
boolean blocked = mIconBlacklist.contains(slot);

mIconLogger.onIconVisibility(getSlot(index), icon.visible);

//这个就是在Fragment设置进去的DarkIconManager,调用其onIconAdded方法实现添加
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));
}



我们看看setIconVisibility方法,其实根据标识找到icon然后更新其的可见性

public void setIconVisibility(String slot, boolean visibility) {
    int index = getSlotIndex(slot);
 StatusBarIcon icon = getIcon(index);
 if (icon == null || icon.visible == visibility) {
        return;
 }
    icon.visible = visibility;
 handleSet(index, icon);
}
//更新icon

private void handleSet(int index, StatusBarIcon icon) {
int viewIndex = getViewIndex(index);
mIconLogger.onIconVisibility(getSlot(index), icon.visible);
mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));
}

到目前为止我们就分析完了状态栏的相关功能流程了。还是比较复杂的。由于时间问题,对下拉通知栏和导航栏等下次再讲了

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要禁止Android系统中的状态栏下拉,可以通过修改系统UI(SystemUI)来实现。 首先,需要有root权限才能修改SystemUI,因为这涉及到系统的核心功能。你可以在手机上安装一个root管理器应用,如Magisk Manager或SuperSU,以获取root权限。 一旦你获得了root权限,就可以开始修改SystemUI。首先,你需要通过文件管理器找到SystemUI.apk文件的位置。通常,它位于/system/priv-app/SystemUI/目录下,但不同设备可能会有所不同。 在找到SystemUI.apk文件后,将其复制到另一个地方,以备份。然后,使用一个APK解压软件,如APKTool或WinRAR,打开SystemUI.apk文件并提取其中的资源文件。 在提取的资源文件中,你需要找到StatusBarManager.java或StatusBar.java文件。这个文件负责处理状态栏的各种功能和行为。 一旦你找到了该文件,可以通过修改其源代码来禁止状态栏下拉。具体来说,你可以在源代码中找到与下拉手势相关的代码段,并将其注释掉或删除。这样,在应用修改后重新编译并安装SystemUI.apk文件后,用户将无法下拉状态栏。 完成修改后,重新压缩SystemUI文件夹,并将其命名为SystemUI.apk。然后,将修改后的SystemUI.apk文件复制回原始位置,即/system/priv-app/SystemUI/目录下,覆盖原有的SystemUI.apk文件。 最后,重启你的设备以使修改生效。一旦设备重新启动,你将看到无法下拉状态栏的变化。请注意,这个修改可能需要额外的维护和更新,以确保在系统更新或恢复出厂设置后仍然有效。 尽管这种修改可能提供一种禁用状态栏下拉的方法,但请谨慎操作,并确保你了解你所做的修改对系统可能产生的影响。在进行任何修改之前,最好备份你的设备并了解如何还原或修复可能发生的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值