本文亲测有效,如有疑问,请在下方留言。
禁止锁屏下拉分两种情况,一种是像我这样可以修改系统源码的,一种是在上面开发的第三方app的(没有系统权限)。两种方式的处理不一样;
再说具体怎么实现前我们先探索一下源码(不感兴趣可以直接跳过);
有先时候有需求需要屏蔽状态栏下拉,例如com.android.phone中的紧急呼叫时(sim卡锁住),此时源码设计是状态栏下拉不了的。
Android中有许多隐藏的Service,StatusBarManager就是其中一个,在Context.java中可以看到:
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.StatusBarManager} for interacting with the status bar.
*
* @see #getSystemService
* @see android.app.StatusBarManager
* @hide
*/
public static final String STATUS_BAR_SERVICE = "statusbar";
表明该service不对外提供,如果要调用的话需要在源码上编译,才能调用到标记为@hide的接口。也可以将源码编译出来的classes.jar包添加到工程里面。
StatusBarManager提供了一些有用的接口,像disable()方法正是我们需要的,一些系统级的应用也是调用的该方法禁止StatusBar下拉的,比如电话、锁屏模块。想要调用该方法,你还需要以下权限:
<uses-permissionandroid:name="android.permission.STATUS_BAR" />
<uses-permissionandroid:name="android.permission.EXPAND_STATUS_BAR"/>
获取StatusBarManager实例
mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
禁止下拉和解除禁止
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND); //禁止下拉
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE); //解除禁止
整个系统不可下拉
第一种就很简单了,只需要修改系统远吗就能实现所有的状态栏不能下拉:(我这里用的5.1的源码)
/**
* Disable some features in the status bar. Pass the bitwise-or of the DISABLE_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
*/
public void disable(int what) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
//svc.disable(what, mToken, mContext.getPackageName());//--remove this
mService.disable(DISABLE_EXPAND, mToken, mContext.getPackageName()); //++added this
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
}
然后参照这篇文章《修改framework后刷入系统》开机重启,一气呵成,搞定,拉不下来了。
应用运行中不可下拉(系统应用)
如果是系统级应用,也就是手机厂家植入的应用,可以使用调用StatusBarManager 的disable(int)的方法来进行屏蔽,参数如下:
public static final int DISABLE_EXPAND = 0x00000001;
public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002;
public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004;
public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
public static final int DISABLE_NONE = 0x00000000;
具体是这样实现的:
private StatusBarManager mStatusBarManager;
mStatusBarManager = (StatusBarManager)getSystemService(Context.STATUS_BAR_SERVICE);
@Override
protected void onResume() {
if (DBG) log("onResume()...");
super.onResume();
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
...
}
应用运行中不可下拉(非系统应用)
但是如果是在应用层上的,disable方法因为权限问题无法使用(如果一定要使用必须具有系统签名)。这个时候可以使用collapse()方法,现在的小米锁屏和360锁屏都是使用该方法。
public void onWindowFocusChanged(boolean hasFocus) {
disableStatusBar();
super.onWindowFocusChanged(hasFocus);
}
public void disableStatusBar(){
try {
Object service = getSystemService("statusbar");
Class<?> claz = Class.forName("android.app.StatusBarManager");
Method expand = claz.getMethod("disable");
expand.invoke(service);
} catch (Exception e) {
e.printStackTrace();
}
为了方便大家理解,我把StatusBarManager 这个类贴出来:
vim frameworks/base/core/java/android/app/StatusBarManager.java
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.ServiceManager;
import android.util.Slog;
import android.view.View;
import com.android.internal.statusbar.IStatusBarService;
/**
* Allows an app to control the status bar.
*
* @hide
*/
public class StatusBarManager {
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
public static final int DISABLE_NOTIFICATION_ALERTS
= View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
@Deprecated
public static final int DISABLE_NOTIFICATION_TICKER
= View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
@Deprecated
public static final int DISABLE_NAVIGATION =
View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
public static final int DISABLE_NONE = 0x00000000;
public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS
| DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER
| DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
| DISABLE_SEARCH;
public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
public static final int WINDOW_STATUS_BAR = 1;
public static final int WINDOW_NAVIGATION_BAR = 2;
public static final int WINDOW_STATE_SHOWING = 0;
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;
private Context mContext;
private IStatusBarService mService;
private IBinder mToken = new Binder();
StatusBarManager(Context context) {
mContext = context;
}
private synchronized IStatusBarService getService() {
if (mService == null) {
mService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (mService == null) {
Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
}
}
return mService;
}
/**
* 敲黑板:重点就在这里
* Disable some features in the status bar. Pass the bitwise-or of the DISABLE_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
*/
public void disable(int what) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
//replace what to disable
svc.disable(what, mToken, mContext.getPackageName());
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
/**
* Expand the notifications panel.
*/
public void expandNotificationsPanel() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.expandNotificationsPanel();
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
/**
* Collapse the notifications and settings panels.
*/
public void collapsePanels() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.collapsePanels();
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
/**
* Expand the settings panel.
*/
public void expandSettingsPanel() {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.expandSettingsPanel();
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
contentDescription);
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
public void removeIcon(String slot) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.removeIcon(slot);
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
public void setIconVisibility(String slot, boolean visible) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setIconVisibility(slot, visible);
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
/** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
return "WINDOW_STATE_UNKNOWN";
}
}
示范案例
例子1:
公司有个需求当拨打电话在前台执行时,状态栏下拉不了,而在后台执行时,状态栏则可以下拉,此时需要修改的是phone应用。
在电话的呼出流程中,我们最后需要按下拨号键,才能将电话拨打出去,那么在按下拨号键之后,我们可以看到会弹出一个界面
(1),显示拨号信息以及一些其他信息,这个界面就是我们的InCallScreen界面。当然,在来电
(2)的时候,弹出的界面依然是InCallScreen,在我们接通电话
(3)之后显示的那个界面仍然是InCallScreen。也就是说在通话过程中,我们一直可见并操作的那个界面就是InCallScreen
在类com.android.phone. InCallScreen.java
private StatusBarManager mStatusBarManager;
mStatusBarManager = (StatusBarManager)getSystemService(Context.STATUS_BAR_SERVICE);
@Override
protected void onResume() {
if (DBG) log("onResume()...");
super.onResume();
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
...
}
@Override
protected void onPause() {
if (DBG) log("onPause()...");
super.onPause();
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
...
}
例子2:公司有个需求在安全模式下状态栏下拉不了,android模式下状态栏可以下拉(两个模式的切换广播通知),这个需求我们可以使用上述方法,也可以直接在systemui中修改,此时我们使用第二种方法。
status_bar.xml:下拉部分就是触摸这个View,因此可以在这个视图中查看触摸事件。(状态栏部分:包括浮动通知,耳机、蓝牙、电池、信号、闹钟等图标)
packagecom.android.systemui.statusbar.phone;
public classPhoneStatusBarView extends PanelBar {
@Override
public boolean panelsEnabled() { //可以在此处修改
return ((mBar.mDisabled & StatusBarManager.DISABLE_EXPAND) == 0);
}
}
上述方法中可以修改为如下:
public boolean panelsEnabled() {
if(安全模式){
return false;
}else{
return ((mBar.mDisabled &StatusBarManager.DISABLE_EXPAND) == 0);
}
}
package com.android.systemui.statusbar.phone;
public classPanelBar extends FrameLayout {
@Override
publicboolean onTouchEvent(MotionEvent event) {
// Allow subclasses to implement enable/disable semantics
if (!panelsEnabled()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Slog.v(TAG,String.format("onTouch: all panels disabled, ignoring touch at(%d,%d)",
(int)event.getX(), (int) event.getY()));
}
return false;
}
public booleanpanelsEnabled() {//父类PhoneStatusBarView重写了
return true;
}
...
}