How to disable status bar click and pull down in Android?
In my opinion, there are 2 options:
OPTION 1: You can lay a window over the status bar to disable any touch or pulling down.
OPTION 2: You can also override the OnWindowFocusChanged() method to close the notification panel immediately after it shows up.
How to:
OPTION 1: Define the following method(preventStatusBarExpansion) and class(CustomViewGroup) in your activity.
public static void preventStatusBarExpansion(Context context) {
WindowManager manager = ((WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
Activity activity = (Activity)context;
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
int resId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
int result = 0;
if (resId > 0) {
result = activity.getResources().getDimensionPixelSize(resId);
}
localLayoutParams.height = result;
localLayoutParams.format = PixelFormat.TRANSPARENT;
customViewGroup view = new customViewGroup(context);
manager.addView(view, localLayoutParams);
}
public static class customViewGroup extends ViewGroup {
public customViewGroup(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customViewGroup", "**********Intercepted");
return true;
}
}
And call the preventStatusBarExpansion method in the onCreate method of the activity. Done!
OPTION 2: First, add the following permission to the Androidmanifest.xml file:
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
Second, define the following class-scope variables in your activity:
// To keep track of activity's window focus
boolean currentFocus;
// To keep track of activity's foreground/background status
boolean isPaused;
Handler collapseNotificationHandler;
Third, override onWindowFocusChanged(boolean):
@Override
public void onWindowFocusChanged(boolean hasFocus) {
currentFocus = hasFocus;
if (!hasFocus) {
// Method that handles loss of window focus
collapseNow();
}
}
Forth, define the collapseNow(); method:
public void collapseNow() {
// Initialize 'collapseNotificationHandler'
if (collapseNotificationHandler == null) {
collapseNotificationHandler = new Handler();
}
// If window focus has been lost && activity is not in a paused state
// Its a valid check because showing of notification panel
// steals the focus from current activity's window, but does not
// 'pause' the activity
if (!currentFocus && !isPaused) {
// Post a Runnable with some delay - currently set to 300 ms
collapseNotificationHandler.postDelayed(new Runnable() {
@Override
public void run() {
// Use reflection to trigger a method from 'StatusBarManager'
Object statusBarService = getSystemService("statusbar");
Class<?> statusBarManager = null;
try {
statusBarManager = Class.forName("android.app.StatusBarManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method collapseStatusBar = null;
try {
// Prior to API 17, the method to call is 'collapse()'
// API 17 onwards, the method to call is `collapsePanels()`
if (Build.VERSION.SDK_INT > 16) {
collapseStatusBar = statusBarManager .getMethod("collapsePanels");
} else {
collapseStatusBar = statusBarManager .getMethod("collapse");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
collapseStatusBar.setAccessible(true);
try {
collapseStatusBar.invoke(statusBarService);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// Check if the window focus has been returned
// If it hasn't been returned, post this Runnable again
// Currently, the delay is 100 ms. You can change this
// value to suit your needs.
if (!currentFocus && !isPaused) {
collapseNotificationHandler.postDelayed(this, 100L);
}
}
}, 300L);
}
}
Finally, override onPause() and onResume methods:
@Override
protected void onPause() {
super.onPause();
// Activity's been paused
isPaused = true;
}
@Override
protected void onResume() {
super.onResume();
// Activity's been resumed
isPaused = false;
}
Done!
NOTE: I used the 1st option when putting an app in kiosk mode, which doesn't show the notification panel at all. On the other hand, the 2nd option works well, but it allows the notification panel to show up for a short time, and user can quickly click the settings icon on the top of the notification panel to quit the kiosk mode.
Hope this will help you. Cheers!
Disable Android StatusBar expand/pull-down
Hello, all, boy oh boy, I'm glad! After spending quite a long time, I am back with a delicious answer once again!
This solution is NOT out there! And this solution works! It is provided by Android
and if you are building a System App, you can use this solution, for User Apps, sorry, I cannot help, but try and see if it works.
And Yeah I used this before (You'll easily find on SO): public class BlockStatusBar {
and lots of others, tried to modify AOSP itself, but was more complicated.
But you know how BlockStatusBar
works, what with the onWindowFocusChange
and stuff! It lets the user pull it down and has delay. Now if you refer Android AOSP, you'll see that there is a public method public void disable(int what)
provided by StatusBarManager which takes FLAGs. I tried to reflect it, but was in vain!
The problem was the parameter type and getMethod(String methodName)
usage, which failed, but instead when I used the correct parameter specification, I was able to pull it off.
So here's a reference of Android AOSP 9.0r34
source android/frameworks/base/core/java/android/app/StatusBarManager.java
...
...
...
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;
...
...
...
/**
* 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());
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
...
...
...
So what I am doing is getting the right method, and then invoking the disable FLAGs on it.
Simply speaking anything like View.STATUS_BAR_DISABLE_EXPAND
should have worked, but for some reason, even though in Android Studio I can see the code, and it is public inside View
class, I cannot access it. However, I was able to use the actual HEX values. So finally, without much ado, Here's the solution:
But first, review the View.java
's FLAGS:
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to make the status bar not expandable. Unless you also
* set {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show.
*/
public static final int STATUS_BAR_DISABLE_EXPAND = 0x00010000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide notification icons and scrolling ticker text.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_ICONS = 0x00020000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to disable incoming notification alerts. This will not block
* icons, but it will block sound, vibrating and other visual or aural notifications.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_ALERTS = 0x00040000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the scrolling ticker. Note that
* {@link #STATUS_BAR_DISABLE_NOTIFICATION_ICONS} implies
* {@link #STATUS_BAR_DISABLE_NOTIFICATION_TICKER}.
*/
public static final int STATUS_BAR_DISABLE_NOTIFICATION_TICKER = 0x00080000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide the center system info area.
*/
public static final int STATUS_BAR_DISABLE_SYSTEM_INFO = 0x00100000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the home button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_HOME = 0x00200000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the back button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the clock. You might use this if your activity has
* its own clock making the status bar's clock redundant.
*/
public static final int STATUS_BAR_DISABLE_CLOCK = 0x00800000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to hide only the recent apps button. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_RECENT = 0x01000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to disable the global search gesture. Don't use this
* unless you're a special part of the system UI (i.e., setup wizard, keyguard).
*/
public static final int STATUS_BAR_DISABLE_SEARCH = 0x02000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the status bar is displayed in transient mode.
*/
public static final int STATUS_BAR_TRANSIENT = 0x04000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the navigation bar is displayed in transient mode.
*/
public static final int NAVIGATION_BAR_TRANSIENT = 0x08000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the hidden status bar would like to be shown.
*/
public static final int STATUS_BAR_UNHIDE = 0x10000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the hidden navigation bar would like to be shown.
*/
public static final int NAVIGATION_BAR_UNHIDE = 0x20000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the status bar is displayed in translucent mode.
*/
public static final int STATUS_BAR_TRANSLUCENT = 0x40000000;
/**
* @hide
*
* NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
* out of the public fields to keep the undefined bits out of the developer's way.
*
* Flag to specify that the navigation bar is displayed in translucent mode.
*/
public static final int NAVIGATION_BAR_TRANSLUCENT = 0x80000000;
/**
* @hide
*
* Makes navigation bar transparent (but not the status bar).
*/
public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000;
/**
* @hide
*
* Makes status bar transparent (but not the navigation bar).
*/
public static final int STATUS_BAR_TRANSPARENT = 0x00000008;
NEXT, the actual solution: I used two flags, namely, public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
& public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
, whose HEX you can find in View.java
public static void disableStatusBar(Context context) {
Log.d(Home.class.getCanonicalName(), "disableStatusBar: ");
// Read from property or pass it in function, whatever works for you!
boolean disable = SystemProperties.getBoolean(context, "supercool.status.bar.disable", true);
Object statusBarService = context.getSystemService("statusbar");
Class<?> statusBarManager = null;
try {
statusBarManager = Class.forName("android.app.StatusBarManager");
try {
final Method disable_statusBarFeatures = statusBarManager.getMethod("disable", int.class);
try {
disable_statusBarFeatures.setAccessible(true);
if (disable) {
disable_statusBarFeatures.invoke(statusBarService, 0x00010000 | 0x00040000);
} else {
disable_statusBarFeatures.invoke(statusBarService, 0x00000000);
}
} catch (Exception e) {
Log.e(Home.class.getCanonicalName(), "disableStatusBar: " + e.getMessage(), e);
}
} catch (Exception e) {
Log.e(Home.class.getCanonicalName(), "disableStatusBar: " + e.getMessage(), e);
}
} catch (Exception e) {
Log.e(Home.class.getCanonicalName(), "disableStatusBar: " + e.getMessage(), e);
}
}
Lastly, you'll need <uses-permission android:name="android.permission.STATUS_BAR" />
, or else, it will Throw
InvocationTargetException
& SecurityException
Now, the ultimate treat, I am using our Home App (Launcher) so, this is applied across the system, once and for all. Plus, I call this method in onResume()
so, if I modify the property at runtime, I can Re-Enable the StatusBar back. Please like & share!
Happy Coding!