22

Is there anyway to prevent users from sliding the status bar (expand) or collapsing back?

I'm trying out a lockscreen replacement and seems like it's a must-have feature. Is there any possible way to do it without requiring root privileges?

Ye Myat Min
  • 1,429
  • 2
  • 17
  • 29

9 Answers9

20

You can actually prevent the status bar from expanding, without rooting phone. See this link. This draws a window the height of the status bar and consumes all touch events.

Call the following code just after onCreate().

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;
    //https://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels
    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;
    }
}
Community
  • 1
  • 1
Kristy Welsh
  • 7,828
  • 12
  • 64
  • 106
  • Nice workaround, but the top area is disabled for click events, can you please suggest anything for that? – Samvel Kartashyan Oct 25 '15 at 10:19
  • @SamvelKartashyan - edited answer to make the window only draw over status bar. All other events should now be clickable. – Kristy Welsh Feb 26 '16 at 15:16
  • @tsiro, yes, definitely. – Kristy Welsh Sep 13 '17 at 19:22
  • 1
    how to restore the status bar expansion again ? – Mushahid Gillani Aug 07 '18 at 14:38
  • 4
    This solution was working great but after changing the target sdk level to 26 (from 23) I'm getting the error " android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@632ab9c -- permission denied for window type 2010". The app already has "" Any ideas how to resolve? – philcruz Jan 21 '19 at 00:26
  • Thanks for such great solution. I was looking to disable it. Still could not find a solution. And finally, I found yours solution and it works perfect. For that BadTokenException, we need to request permission for "Display overlay" in code. After that all are working fine. Thanks for the solution. – scsfdev Oct 23 '20 at 09:25
18

Short answer: this is impossible!

Now should you be interested to know why this is impossible:

There are two permissions for manipulating the system status bar, EXPAND_STATUS_BAR and STATUS_BAR. The former can be requested by any application, but the later is reserved for applications signed with the platform signature (system applications, not third-party). It is possible to expand/ collapse the system status bar (see "How to open or expand status bar through intent?") but note that reflection is required because the StatusBarManager class is not part of the SDK. The disable method, which is used by the Android dialer to prevent the status bar from being expanded, cannot be accessed by an application without the aforementioned STATUS_BAR permission.

Sources: personal experience :-)

Community
  • 1
  • 1
Tom
  • 6,947
  • 7
  • 46
  • 76
  • Thanks @farble1670 I am certain that it is correct. Without root/ building an app in /system/app you cannot call the StatusBarMananger.disable() method without getting a SecurityException. – Tom Jan 29 '12 at 22:11
15

First of all, it's impossible to modify the Status Bar if your app is not signed with the phone's rom certified, if you try to modify it you'll get an Security Exception.

Update: In new APIs the method is "collapsePanels" instead of "collapse".

The only way I've found after several hours of work is by overriding the "onWindowFocusChanged" method of the activity and when it loses the focus (maybe the user has touched the notifications bar), force to collapse the StatusBar, here is the code (working on a Defy+ 2.3.5).

You need to declare the following permission on the manifest:

 <uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/> 

And override the following method:

public void onWindowFocusChanged(boolean hasFocus)
{
        try
        {
           if(!hasFocus)
           {
                Object service  = getSystemService("statusbar");
                Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
                Method collapse = statusbarManager.getMethod(Build.VERSION.SDK_INT > 16 ? "collapsePanels" : "collapse");
                collapse.setAccessible(true);
                collapse.invoke(service);
           }
        }
        catch(Exception ex)
        {
        }
}

Update: You will have to use your own custom Alert Dialog by overriding it their onWindowFocusChanged method too, because Alert Dialogs have their own focus.

edga7eta
  • 3
  • 3
PoOk
  • 645
  • 7
  • 17
  • 1
    This method is such a hack, it lets the user expand the status bar to a certain extent then pulls it back up. Its better to just cover the status bar with a View that consumes all onTouchEvents. – Tom Jun 21 '12 at 03:34
  • 1
    I tried to use that workaround, but I couldn't. Could you tell us how did you make it? – PoOk Jul 09 '12 at 12:39
  • Grandland Chew's answer does exactly what you want, you overlay the status bar and accept all touch input. – Tom Jul 09 '12 at 14:34
  • I've used a similar code but it does not work, what API are you using? – PoOk Jul 10 '12 at 07:25
  • These is no "API" for disabling status bar expansion that non-system apps can use. Grandland's method works just fine, placing a window of the status bar that consumes touch events. – Tom Jul 10 '12 at 16:46
  • 1
    It works just fine on ANY API level 4 and up. You use the WindowManager class which has NOT changed its fundamental implementation in a while. – Tom Jul 11 '12 at 15:42
  • 1
    It works very well, but you forgot to say that it requires – CelinHC Sep 21 '12 at 15:14
  • Not working on my android 4.2.2 galaxy tab 8", throwing exception java.lang.NoSuchMethodException: collapse [] – Yasitha Waduge Mar 14 '14 at 06:00
  • Also not working for on Tab3 with 4.2.2. I do not receive any errors but nothing happens. Status expands as normal and do not collapse. Any thoughts? – Ole_S Jun 12 '14 at 16:20
  • throwing exception java.lang.NoSuchMethodException: collapse – codeRider Jul 22 '15 at 11:37
  • If I have the ROM certificate signed APK then How can we Disable the Notification Panel with Quick Settings also on Lock Screen? – Sanat Pandey Sep 05 '16 at 09:36
  • Great answer. What about bottom navigation bar? – Duna Nov 20 '18 at 19:46
13

This actually can be done via a little hack that I accidentally discovered, but requires the permission android.permission.SYSTEM_ALERT_WINDOW:

What you do is add a view the exact size of the status bar directly to the WindowManager with the certain parameters that covers the status bar and prevents it from receiving touch events:

View disableStatusBarView = new View(context);

WindowManager.LayoutParams handleParams = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.FILL_PARENT,
    <height of the status bar>,
    // This allows the view to be displayed over the status bar
    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
    // this is to keep button presses going to the background window
    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,
    PixelFormat.TRANSLUCENT);

handleParams.gravity = Gravity.TOP;
context.getWindow().addView(disableStatusBarView, handleParams);

This will basically create a transparent view over the status bar that will receive all the touch events and block the events from reaching the status bar and therefore prevents it from being expanded.

NOTE: This code is untested, but the concept works.

Maciej Ciemięga
  • 10,125
  • 1
  • 41
  • 48
Grantland Chew
  • 2,620
  • 26
  • 26
  • 2
    This works, I can confirm. With a few tweaks, call to system resources to retrieve the exact height, and default fallbacks this work like a charm. – Tom Dec 05 '11 at 01:57
  • 1
    Some working code would be VERY VERY helpful! As well, eclipse is telling me to use addContentView instead of addView? – MindWire Dec 07 '11 at 22:59
  • this does not seems to be working for me at the moment; while I can draw over the status bar area I can not prevent the status bar expansion nor capture touch events – ademar Oct 06 '12 at 19:41
  • 1
    @ademar: Things might have have changed slightly in Jelly Bean, is that what you're testing on? – Grantland Chew Oct 08 '12 at 17:23
  • @Tom I tried on a tablet and doesn't work. Anyone get success on tablet? – Derzu Feb 26 '13 at 13:18
  • @Derzu the status bar is on the bottom for tablets, did you try Gravity.BOTTOM? – Nathan Schwermann Mar 18 '13 at 19:24
  • @Tom, yes, I tried with the Gravity.BOTTOM, but still does not work. – Derzu Mar 19 '13 at 03:33
  • @Derzu You CANNOT draw over the tablet "system bar" at the bottom of the screen. This is for security reasons, concerns similar to drawing over the navigation bar. Doing this prevents the user from being able to exit an app, an obvious concern with malicious apps. – Tom Mar 20 '13 at 13:22
  • @Tom It is not for a malicious app, it is for a kiosk mode app. – Derzu Mar 20 '13 at 20:31
  • @Derzu I didn't mean to imply that it was, but without special permissions (your app living in /system/app and having system permissions) this cannot be done. – Tom Mar 21 '13 at 00:53
  • This does not work for me also. I am trying on a Jelly Bean device and it gives me a force close. Too bad, I thought it was a good idea. – Sandra May 10 '13 at 13:11
  • It says: "Unable to add window -- Permission denied for this window type" – Sandra May 13 '13 at 13:24
  • 1
    This does not work for me either, it does not cover the status bar – Jesse Jin Jun 12 '13 at 21:44
  • that's not a hack. it's known and intented. Thus it is not wanted. – JacksOnF1re Mar 23 '15 at 18:30
  • For me its cover top but dont prevent action of slidedown from status bar. (API 23, Moto X) – rcorbellini Feb 19 '16 at 11:49
  • This will only work if your app is running as a system process. Also TYPE_SYSTEM_ALERT was deprecated in API level 26. – Mark Kuczmarski Dec 13 '20 at 05:27
8

I tried the solutions mentioned by GrantLand and PoOk but both didn't work in my case. Though, Another solution Clear/Hide Recent Tasks List did the trick for me. I am writing a launcher app for which I had to disable a recent applications menu so user cannot open a locked app from it. Also, I had to prevent against notification bar expansion and this trick made me achieve both. Override OnWindowFocusChanged method in your activity and check if this is what u wanted.

public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

        Log.d("Focus debug", "Focus changed !");

    if(!hasFocus) {
        Log.d("Focus debug", "Lost focus !");

        Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        sendBroadcast(closeDialog);
    }
}
Community
  • 1
  • 1
Abdul Rehman
  • 373
  • 6
  • 15
3

For a lockscreen Why don't you just use the following in your main activity:

@Override
public void onAttachedToWindow() {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}

If the user doesn't have a secure lockscreen set the app will let the user pull the status bar down and open activities but that doesn't matter as the user obviously doesn't want a secure screen anyway.

If the user does have a secure locksreen set then the app will show the status bar but will not allow interactions with it. This is because the phone is still actually locked and only your activity is allowed to operate until the user unlocks the phone. Also closing your app in anyway will open the standard secure lockscreen. All this is highly desirable because you don't have to spend all that time coding secure features that you can't guarantee will be as secure as the stock ones.

If you really don't want the user to be able to interact with the status bar, maybe you can leave out the flag FLAG_DISMISS_KEYGUARD call. Then just before you are about to unlock the phone set the flag like I showed in the first block. I don't know if this last part works but it's worth a try.

Hope that helps

Kyle O.
  • 376
  • 2
  • 7
1

What you can do is collapse the status bar in a loop, so when user tries to expand it, it automatically will collapse, here is the code, just add it in your MainActivity.java file:

    private Handler mHandler = new Handler();
    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                @SuppressLint("WrongConstant") Object service = getSystemService("statusbar");
                Class<?> statusBarManager = Class.forName("android.app.StatusBarManager");
                Method collapse = statusBarManager.getMethod(Build.VERSION.SDK_INT > 16 ? "collapsePanels" : "collapse");
                collapse.setAccessible(true);
                collapse.invoke(service);
            } catch (Exception ex) {
                // handle exception
            }
            mHandler.postDelayed(mRunnable, 100);
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        mHandler.postDelayed(mRunnable, 100);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mHandler.removeCallbacks(mRunnable);
    }
Said Torres
  • 581
  • 3
  • 14
1

Sorry but it does not work. using FLAG_LAYOUT_IN_SCREEN prevents you from catching any touch events.

And BTW: to add a view to the window use the window manager:

WindowManager winMgr = (WindowManager)getSystemService(WINDOW_SERVICE);
winMgr.addView(disableStatusBar, handleParams); 
amalBit
  • 12,041
  • 6
  • 77
  • 94
Gil SH
  • 3,789
  • 1
  • 27
  • 25
-4
requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                            WindowManager.LayoutParams.FLAG_FULLSCREEN);
Basic Coder
  • 10,882
  • 6
  • 42
  • 75
  • This will make the app fullscreen and hence hide the status bar. I do not want this as this will bring more work for me by having to implement by own status bar. – Ye Myat Min Sep 18 '11 at 14:48
  • status bar can be still made visible and thus can be shown even in full screen... – DritanX May 04 '15 at 22:20