Search code examples
androiddialogpopupfullscreennavbar

How do I hide the navigation bar despite having window popups, overlays or dialogs that appear?


I am trying to create a fullscreen app that launches at specific times, and when it launches, it should automatically become full screen. I have no problems launching it with full screen. However, if there are popups or windows that exist, and then my activity gets launched, the navbar never gets hidden (in other words, full screen state is not achieved). I have created a 5 second video to demonstrate this problem.

You can view the bug here: http://tinypic.com/player.php?v=2qkplwp%3E&s=9#.V3fZ8KLA3Fk

I've tried everything with flags, but can't seem to get it done. I have looked around the Playstore and it seems many lockscreen apps especially can easily hide the navbar despite any popups\other dialogs existing (SlideLock, Echo Lock Screen).

Below is my simple activity code:

In manifest: <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

windowlayout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#a9e487">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Exit"
    android:id="@+id/button"
    android:layout_gravity="center"
    android:layout_marginTop="100dp" />
</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    final static WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION |
            WindowManager.LayoutParams.FLAG_FULLSCREEN |
            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, //Covers status bar
            PixelFormat.TRANSPARENT);

    final static int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);

        final View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(flags);

        final ViewGroup viewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.windowlayout, null);
        final WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        windowManager.addView(viewGroup, params);

        viewGroup.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                windowManager.removeView(viewGroup);
            }
        });

    }

}

Solution

  • Probably I obtained a behavior similar to your goal, but for sore my answer needs to be reviewed. Anyway, here it's my solution.

    I added two components:

    1. a method to collapse the statusBar panel

    2. a listener to fire a brand new activity in front to all the activities

    The first part is quite easy: you just this method in your activity:

    private void collapseStatusBar(){
        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();
        }
    
    }
    

    For the second part, you fire a programmed intent to a receiver in order to launch your activity at the top of your stack

     Intent startIntent = new Intent(this, LaunchWakeupReceiver.class);
     PendingIntent wakeupIntent = PendingIntent.getBroadcast(this, 0, startIntent,PendingIntent.FLAG_UPDATE_CURRENT );
     AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
     alarm.set(AlarmManager.RTC, (new Date()).getTime()+10000, wakeupIntent);
    

    and this is the receiver:

    public class LaunchWakeupReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(final Context context, Intent intent) {
    
            intent = new Intent(context,MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }
    

    So, for example, your main activity could be like this:

    public class MainActivity extends AppCompatActivity {
    
        ViewGroup viewGroup;
        WindowManager windowManager;
    
        final static WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION |
                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, //Covers status bar
                PixelFormat.TRANSPARENT);
    
        final static int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    
        private static final String LAUNCHED = "launched";
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            addCoverView();
    
        }
    
    
        private void addCoverView(){
    
            final View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(flags);
    
            viewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.windowlayout, null);
            windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    
            final SharedPreferences shP = PreferenceManager.getDefaultSharedPreferences(this);
            if (shP.contains(LAUNCHED)) {
                collapseStatusBar();
                windowManager.addView(viewGroup, params);
            }
    
            viewGroup.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    windowManager.removeView(viewGroup);
    
                }
            });
    
            Intent startIntent = new Intent(this, LaunchWakeupReceiver.class);
            PendingIntent wakeupIntent = PendingIntent.getBroadcast(this, 0, startIntent,PendingIntent.FLAG_UPDATE_CURRENT );
            AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
            alarm.set(AlarmManager.RTC, (new Date()).getTime()+10000, wakeupIntent);
    
            shP.edit().putBoolean(LAUNCHED,true).apply();
    
    
        }
    
    
        private void collapseStatusBar(){
            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();
            }
    
        }
    }
    

    I'm aware that this solution is quite rough but, in my tests, it's working. Let me know what do you think about.

    EDIT

    The receiver part is actually just for test purposes. So the "LockActivity" should be just like this:

    public class LockActivity extends AppCompatActivity {
    
        ViewGroup viewGroup;
        WindowManager windowManager;
    
        final static WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
                        WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION |
                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, //Covers status bar
                PixelFormat.TRANSPARENT);
    
        final static int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            addCoverView();
    
        }
    
    
        private void addCoverView(){
    
            final View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(flags);
    
            viewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.windowlayout, null);
            windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    
            collapseStatusBar();
            windowManager.addView(viewGroup, params);
    
            viewGroup.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    windowManager.removeView(viewGroup);
    
                }
            });
    
    
        }
    
    
        private void collapseStatusBar(){
            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();
            }
    
        }
    }