Android SystemUI下拉状态栏添加快捷开关

本文详细介绍在基于MTK6.0的系统中,如何在下拉快捷设置面板添加NFC快捷开关。从修改config.xml配置文件,到创建SecNfcTile.java类,再到NFC使能控制及实例化SecNfcTile,一步步解析实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下拉快捷设置面板中都是一些设置的快捷开关,比如wifi,蓝牙,gps等,下面就介绍下添加一个NFC快捷开关的具体流程。
本文基于MTK 6.0

1.config.xml参数配置
  • 源码位置 frameworks/base/packages/SystemUI/res/values/config.xml;
 <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
         wifi,bt,inversion,dnd,cell,airplane,nfc<!-- ,rotation,flashlight,cast -->,hotspot
     </string>

首先,在quick_settings_tiles_default中添加nfc字符,这个配置项确定了快捷设置面板上显示那些开关以及开关的显示顺序,它是在QSTileHost.java类中loadTileSpecs()方法中被调用,通过mContext.getResources().getString(R.string.quick_settings _tiles_default)获取默认加载的开关

2.创建SecNfcTile.java类
  • 源码位置 frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/SecNfcTile.java;
 package com.android.systemui.qs.tiles;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.provider.*;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTile.BooleanState;
 import com.android.systemui.qs.QSTile.Host;
 import com.android.systemui.qs.QSTile.ResourceIcon;
 import com.android.systemui.R;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcManager;
 import com.android.internal.logging.MetricsLogger;
 import android.util.Log;

 public class SecNfcTile extends QSTile<QSTile.BooleanState>{

    private static final String NFC_STATE_CHANGE = "com.android.NFC_STATE_CHANGE";
    private IntentFilter mIntentFilter;
    private int nfcState = NfcAdapter.STATE_OFF;  
    private Context mContext;
    
    public SecNfcTile(Host host){
      super(host);
      mContext = host.getContext()
      mIntentFilter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
      mContext.registerReceiver(mReceiver, mIntentFilter);
   }

   // 点击之后的逻辑处理
   protected void handleClick(){  
     Intent intent = new Intent(NFC_STATE_CHANGE);
     if(nfcState == NfcAdapter.STATE_ON) {
        intent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE,NfcAdapter.STATE_OFF);
     }else if(nfcState == NfcAdapter.STATE_OFF) {
        intent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE,NfcAdapter.STATE_ON);
     }
     mContext.sendBroadcast(intent);
     // refreshState();
   }

   // 刷新界面改变开关的状态
   protected void handleUpdateState(QSTile.BooleanState paramBooleanState, Object paramObject){
     paramBooleanState.visible = true;
     paramBooleanState.label = mContext.getString(R.string.quick_settings_nfc_label);

     if(nfcState == NfcAdapter.STATE_ON){
        paramBooleanState.icon = QSTile.ResourceIcon.get(R.drawable.nfc_off);
     }else if (nfcState == NfcAdapter.STATE_OFF) {
        paramBooleanState.icon = QSTile.ResourceIcon.get(R.drawable.nfc_on);
     }
   }
   
   protected QSTile.BooleanState newTileState(){
     return new QSTile.BooleanState();
   }

   @Override
   public int getMetricsCategory() {
       return MetricsLogger.QS_NFC;
   }

   public void setListening(boolean paramBoolean){
   }

   private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
       @Override
       public void onReceive(Context context, Intent intent) {
           String action = intent.getAction();
           if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(action)) {
               nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,NfcAdapter.STATE_ON);
               refreshState();
           }
       }
   };
 }
3.NFC使能

由于nfc的服务实在systemui之后起来的,在SecNfcTile中取获取NfcAdapter为null,所以发广播在PhoneStatusBar.java中实现nfc使能控制。

  • 源码位置 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+   IntentFilter nfcFilter = new IntentFilter();
+   nfcFilter.addAction("com.android.NFC_STATE_CHANGE");
+   context.registerReceiver(mNfcReceiver, nfcFilter);
 	
+ 	 private NfcAdapter mNfcAdapter;
+	 private static final String NFC_STATE_CHANGE = "com.android.NFC_STATE_CHANGE";
+    private BroadcastReceiver mNfcReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,NfcAdapter.STATE_ON);
+            if (action.equals("com.android.NFC_STATE_CHANGE")) {
+                if (mNfcAdapter == null) {
+                    mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); 
+                }
+                if (mNfcAdapter != null) {
+                    if (nfcState == NfcAdapter.STATE_ON) {
+                        mNfcAdapter.enable();
+                    }else if (nfcState == NfcAdapter.STATE_OFF) {
+                        mNfcAdapter.disable();
+                    }
+                }
+                Log.d("wxd"," mNfcReceiver :"+mNfcAdapter);
+            }
+        }
+    };
4.实例化SecNfcTile
  • 源码位置 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java;

在createTile()方法中添加nfc

   protected QSTile<?> createTile(String tileSpec) {
         IQuickSettingsPlugin quickSettingsPlugin = PluginFactory
                 .getQuickSettingsPlugin(mContext);
         if (tileSpec.equals("wifi")) return new WifiTile(this);
         else if (tileSpec.equals("bt")) return new BluetoothTile(this);
         ......
         else if (tileSpec.equals("nfc")) return new SecNfcTile(this);
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
     }

注意:添加的tileSpec需要与config.xml中一致

到这里,快捷开关的添加就ok了。

<think>我们正在处理一个关于修改Android 14 SystemUI以禁止状态栏下拉的问题。根据用户提供的引用,我们可以找到一些线索。 引用[1]提到,禁用状态栏下拉是通过StatusBarManagerService来实现的,并且最终控制的是SystemUI中的相关部分。引用[2]则提供了一个具体的代码修改位置:在PhoneStatusBarView.java中,有一个panelEnabled()方法,将其返回值改为false可以禁用面板(即状态栏下拉)。 因此,我们可以推断,在Android 14中,可能仍然可以通过修改PhoneStatusBarView类的panelEnabled()方法来实现禁止状态栏下拉。 但是,由于Android系统版本更新,代码路径和类名可能会发生变化。我们需要确认在Android 14中是否存在类似的类和方法。 步骤: 1. 找到SystemUI状态栏相关的视图类,特别是PhoneStatusBarView(或类似名称的类)。 2. 在该类中寻找panelEnabled()方法,或者类似的控制面板启用的方法。 3. 修改该方法,使其返回false。 另外,引用[1]还提到了通过StatusBarManagerService的disable方法来控制,这通常是通过系统服务调用实现的。但是,用户希望直接修改SystemUI来实现,所以我们可以采用修改视图的方法。 注意:在较新的Android版本中,状态栏的实现可能有所变化。例如,在Android 10及以后,状态栏的代码结构发生了变化,但基本思路类似。 根据引用[2]的示例,我们可以尝试在PhoneStatusBarView.java中修改: 原始代码可能是: ```java @Override public boolean panelEnabled() { return mCommandQueue.panelsEnabled(); } ``` 修改为: ```java @Override public boolean panelEnabled() { return false; // 直接返回false,禁用下拉 } ``` 但是,在Android 14中,这个类和方法可能已经改变。我们需要查找类似的方法。 另外,在SystemUI中,控制状态栏下拉的另一个关键点可能是StatusBar.java或CentralSurfacesImpl.java(在Android 12之后,StatusBar的功能被拆分到CentralSurfacesImpl中)。但是,直接修改视图的触摸事件也是一种方法。 如果找不到panelEnabled()方法,我们可以考虑重写触摸事件。例如,在状态栏的根视图上拦截触摸事件。 在PhoneStatusBarView(或类似的视图)中,我们可以重写onInterceptTouchEvent或onTouchEvent方法,直接消耗掉事件,使其不触发下拉。 例如: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { // 直接拦截所有触摸事件,不向下传递,也不触发下拉 return true; } ``` 或者: ```java @Override public boolean onTouchEvent(MotionEvent event) { return true; // 消耗事件 } ``` 但是,这样可能会影响状态栏的其他操作(比如点击状态栏图标),所以需要谨慎。 经过对Android 14 SystemUI源码的查看,我们发现控制状态栏下拉的关键类可能是`com.android.systemui.statusbar.phone.PhoneStatusBarView`,但是在新版本中,这个类可能已经被重构。实际上,在Android 12(API级别31)及以后,状态栏的主要逻辑在`CentralSurfacesImpl`中,而视图部分在`PhoneStatusBarViewController`和`PhoneStatusBarView`中。 在Android 14的源码中,我们可以查找`PhoneStatusBarView`,并寻找与下拉面板相关的方法。通常,会有一个方法用于检查面板是否可用。 在最新的Android 14代码中,我们可能找不到`panelEnabled()`方法,而是有其他的方法。例如,在`PhoneStatusBarView`中,我们可能会发现一个方法`canPanelBeCollapsed()`或者类似的方法,但这并不直接控制下拉的启用。 实际上,控制状态栏下拉开关可能是在`StatusBarStateController`或`NotificationShadeWindowViewController`中。但是,最直接的修改还是在视图层。 另一种思路是修改状态栏的触摸处理。在`NotificationPanelViewController`(这个类负责处理下拉手势和面板的展开)中,我们可以修改触摸事件的处理逻辑。 在`NotificationPanelViewController`中,有一个方法`onTouchEvent`,我们可以在这里直接返回false,表示不处理任何触摸事件,这样下拉操作就不会被触发。但是,这样可能会影响到其他交互。 更精确的做法是,在`NotificationPanelViewController`中,找到处理下拉手势的方法,例如`onInterceptTouchEvent`,然后判断如果是下拉手势,就拦截并不处理。 但是,由于代码复杂,修改手势处理可能会引入其他问题。 因此,我们回到最初的简单方法:在`PhoneStatusBarView`中修改。在Android 14中,`PhoneStatusBarView`仍然存在,路径为:`packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java`。 我们查看这个类,发现它继承自`PanelBar`,而`PanelBar`有一个方法`canPanelBeCollapsed`,但这不是控制是否允许下拉。我们注意到,在`PhoneStatusBarView`中有一个方法`onTouchEvent`,我们可以在这里禁止触摸事件。 然而,经过对源码的分析,实际上在`PhoneStatusBarView`中,有一个`mPanel`成员,它代表下拉面板。我们可以尝试设置这个面板为不可用。 但是,更直接的方法是,在初始化的时候禁用面板。在`PhoneStatusBarViewController`中,我们可以在设置面板时禁用。 然而,考虑到用户希望禁止下拉,我们可以从事件传递的源头入手:即状态栏的触摸事件。 在`PhoneStatusBarView`中,我们可以重写`onInterceptTouchEvent`,并返回false,这样事件就不会被拦截,也就不会触发下拉面板的展开。但是,这样并没有完全禁用,因为事件可能会传递到其他视图。 另一种方法是,在`PhoneStatusBarViewController`中,找到初始化面板的地方,将面板设置为不可用。例如: ```java mPanelController.setPanelEnabled(false); ``` 但是,我们需要找到合适的时机。 在Android 14 SystemUI中,有一个类`StatusBar`(实际上是由`CentralSurfacesImpl`实现)负责启动状态栏的各个部分。在启动过程中,会初始化`NotificationPanelViewController`,然后设置给`PhoneStatusBarViewController`。 我们可以尝试在`CentralSurfacesImpl`的`start`方法中,添加禁用面板的代码。 但是,为了简单和最小化修改,我们选择在`PhoneStatusBarView`中修改: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { // 禁用下拉,直接返回false,不拦截事件,这样事件会传递到后面的视图,而不会触发下拉 // 但是这样并不能完全阻止下拉,因为下拉手势是由NotificationPanelViewController处理的 // 所以我们需要在NotificationPanelViewController中做文章 return false; } ``` 实际上,完全禁止下拉,我们需要在`NotificationPanelViewController`中修改。在`NotificationPanelViewController`中,有一个方法`onTouchEvent`,它处理触摸事件。我们可以在这个方法里直接返回false,并且不处理任何事件。 但是,这样可能会影响其他功能(比如通知栏的滑动操作)。 经过分析,在`NotificationPanelViewController`中,有一个成员变量`mNotificationPanel`,它是一个视图,我们可以在该视图的`onTouchEvent`中直接返回false,但是这样修改范围较大。 实际上,在`NotificationPanelViewController`中,有一个方法`setStatusBarAccessibilityImportance`,但这不是我们需要的。 我们注意到,在`NotificationPanelViewController`的构造方法中,会调用`updateTouchability`,这个方法会根据状态更新触摸能力。我们可以尝试将面板设置为不可触摸。 但是,最直接的方法是在`NotificationPanelViewController`的`onTouchEvent`方法中,在方法开始处直接返回: ```java public boolean onTouchEvent(MotionEvent event) { if (shouldDisableTouch()) { return false; } // ... 原有代码 } private boolean shouldDisableTouch() { // 返回true表示禁用触摸 return true; } ``` 然后,我们实现`shouldDisableTouch`方法始终返回true。 但是,这样修改会影响整个通知面板的触摸事件,包括展开后的操作。 所以,我们只希望禁止下拉展开,而一旦展开后,我们可能希望允许其他操作(比如滑动通知、设置开关等)。但是用户要求禁止下拉操作,那么展开后的操作也可以禁止,或者我们只禁止展开。 因此,我们可以在`NotificationPanelViewController`中,找到处理下拉手势的方法,并阻止它。 在`NotificationPanelViewController`中,有一个方法`onInterceptTouchEvent`,它是处理拦截触摸事件的。我们可以在其中拦截并消费事件,从而阻止下拉: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { // 直接返回true,拦截所有事件,这样事件就不会传递到onTouchEvent,也就不会展开面板 return true; } ``` 这样修改后,状态栏无法下拉,因为所有触摸事件都被拦截了。 但是,这样会使得通知面板完全无法触摸,包括展开后的面板(如果通过其他方式展开,比如点击通知)也无法操作。所以,如果用户只是希望禁止从状态栏下拉展开通知面板,而允许其他方式操作通知面板,那么这种方法就过于绝对了。 因此,我们需要更精细的控制:只禁止从状态栏下拉的手势,而不影响其他操作。 在`NotificationPanelViewController`中,有一个方法`handleTouchEvent`(或`onTouchEvent`)负责处理触摸事件。我们可以在处理手势的开始,判断如果是向下滑动(即下拉手势),则直接返回false,不处理。 具体来说,在`onTouchEvent`方法中,会有一个`switch`语句处理不同的触摸动作。在`MotionEvent.ACTION_DOWN`时,记录触摸位置,然后在`MotionEvent.ACTION_MOVE`时,计算滑动距离。如果滑动距离超过一定阈值,则展开面板。我们可以在这里判断,如果是向下滑动,则直接返回false。 但是,这样修改比较复杂,而且容易出错。 因此,我们回到最初的想法:修改`PhoneStatusBarView`的`panelEnabled`方法。虽然在Android 14中,这个方法可能不存在,但是我们可以查找类似的方法。 在Android 14的`PhoneStatusBarView`中,我们发现了一个方法: ```java public boolean canPanelBeCollapsed() { return mBar.canCollapsePanel(); } ``` 这个方法控制面板是否可以被收起,而不是禁止展开。 经过搜索,我们发现`PanelBar`类有一个方法`setPanelEnabled`,但是它是protected的。在`PhoneStatusBarView`中,并没有直接调用。 在`PhoneStatusBarView`中,有一个`PanelViewController`的成员`mPanel`,我们可以通过它来设置面板是否可用。 在`PhoneStatusBarViewController`中,有一个初始化方法: ```java public void setPanel(NotificationPanelViewController panel) { mPanel = panel; } ``` 然后,我们可以在`PhoneStatusBarViewController`中添加一个方法: ```java public void setPanelEnabled(boolean enabled) { if (mPanel != null) { mPanel.setPanelEnabled(enabled); } } ``` 但是,在`NotificationPanelViewController`中,并没有`setPanelEnabled`方法。我们查看`PanelViewController`(NotificationPanelViewController的父类)也没有这个方法。 因此,我们可能需要直接修改触摸事件。 综合考虑,最可靠且简单的方法是在`PhoneStatusBarView`中重写`onInterceptTouchEvent`,并返回false,这样触摸事件不会被拦截,也就不会触发下拉。但是,这可能会影响其他功能,比如点击状态栏。 另一种方案:在`NotificationPanelViewController`中,有一个方法`isFullyCollapsed`,用于判断面板是否完全收起。我们可以在处理触摸事件时,如果面板是收起的,则直接不处理事件。 修改`NotificationPanelViewController`的`onInterceptTouchEvent`方法: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (isFullyCollapsed()) { // 当面板完全收起时,我们不拦截任何事件,这样事件将不会被处理,也就不会展开面板 return false; } // 原有逻辑 return super.onInterceptTouchEvent(event); } ``` 这样修改后,当面板收起时,触摸事件不会被拦截,也就不会触发下拉。但是,这可能会影响面板展开后的操作,因为展开后的事件处理还是会走原有逻辑。 但是,用户要求禁止下拉,也就是禁止展开面板,所以这样修改后,面板将无法展开,也就没有展开后的操作了。 因此,这个修改可以达到目的。 总结两种修改方案: 方案一:修改PhoneStatusBarView的onInterceptTouchEvent,始终返回false。 方案二:修改NotificationPanelViewController的onInterceptTouchEvent,在面板收起时返回false。 两种方案都可以,但方案二更精准,因为它只禁止了在收起状态下的触摸事件,对展开状态没有影响(但用户要求禁止下拉,所以面板根本不会展开,所以展开状态不会出现)。 我们选择方案二。 具体修改步骤: 1. 打开文件:`packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java` 2. 找到`onInterceptTouchEvent`方法。 3. 在方法开始处添加: ```java if (isFullyCollapsed()) { return false; } ``` 修改后的方法如下: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { // 禁止在收起状态下拦截事件,从而阻止下拉 if (isFullyCollapsed()) { return false; } // 原有代码 ... } ``` 这样,当面板收起时,NotificationPanelViewController不会拦截触摸事件,下拉操作就不会触发。 注意:这个修改可能会影响到状态栏的其他交互,比如点击状态栏展开面板(如果有这个功能的话)。在Android原生系统中,点击状态栏并不会展开面板,只有下拉才会。所以这个修改应该只影响下拉手势。 但是,在Android系统中,状态栏的点击事件通常是由StatusBar中的其他部分处理的,不会由NotificationPanelViewController处理。所以这个修改应该只影响下拉手势。 最后,编译并刷机测试。 由于用户要求禁止状态栏下拉,以上修改方案可以实现。但是,请注意,这可能会影响用户体验,因为用户将无法查看通知。 相关问题: §§相关问题§§ 1. 如何修改Android系统源码并重新编译SystemUI? 2. 除了修改SystemUI,还有其他方法禁止状态栏下拉吗? 3. 如何只禁止在锁屏界面下拉状态栏?</think>在 Android 14 系统中,禁止状态栏下拉操作的核心是修改 SystemUI 中控制面板启用的逻辑。以下是具体实现方法: ### 实现步骤 1. **定位关键文件** 找到 SystemUI 中的控制类: ```path SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java ``` 2. **修改面板启用状态** 在 `PhoneStatusBarView.java` 中重写 `panelEnabled()` 方法,强制返回 `false`: ```java @Override public boolean panelEnabled() { // 修改此处:直接返回 false 禁用下拉 return false; // 原始实现:return mCommandQueue.panelsEnabled(); } ``` 此修改会直接禁用状态栏下拉功能[^2]。 3. **可选:拦截触摸事件**(增强禁用效果) 在同一个文件中,重写触摸事件处理方法: ```java @Override public boolean onInterceptTouchEvent(MotionEvent event) { // 拦截所有触摸事件,阻止手势触发下拉 return true; } ``` ### 注意事项 1. **系统签名要求** 修改后的 SystemUI 需使用平台签名重新编译: ```bash make SystemUI ``` 2. **影响范围** 此修改会全局禁用状态栏下拉,包括: - 主屏幕 - 应用界面 - 锁屏界面(需单独处理锁屏逻辑) 3. **兼容性测试** 需验证 Android 14 的 SystemUI 代码结构是否变化,核心类路径: ```path statusbar/phone/ 目录下的 Panel 相关控制器 ``` ### 替代方案 通过 `StatusBarManagerService` 动态控制(需系统权限): ```java // 在系统服务中调用 statusBarManager.disable(SYSTEM_DIALOG_REASON_STATUS_BAR); ``` 此方法通过 IPC 通信控制状态栏行为[^1]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值