Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
SystemUiHelper
/*
* Copyright (C) 2014 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 com.example.android.systemuivis;
import android.app.Activity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
/**
* Helper for controlling the visibility of the System UI across the various API levels. To use
* this API, instantiate an instance of this class with the required level. The level specifies the
* extent to which the System UI's visibility is changed when you call {@link #hide()}
* or {@link #toggle()}.
*/
public final class SystemUiHelper {
/**
* In this level, the helper will toggle low profile mode.
*/
public static final int LEVEL_LOW_PROFILE = 0;
/**
* In this level, the helper will toggle the visibility of the status bar.
* If there is a navigation bar, it will toggle low profile mode.
*/
public static final int LEVEL_HIDE_STATUS_BAR = 1;
/**
* In this level, the helper will toggle the visibility of the navigation bar
* (if present and if possible) and status bar. In cases where the navigation
* bar is present but cannot be hidden, it will toggle low profile mode.
*/
public static final int LEVEL_LEAN_BACK = 2;
/**
* In this level, the helper will toggle the visibility of the navigation bar
* (if present and if possible) and status bar, in an immersive mode. This means that the app
* will continue to receive all touch events. The user can reveal the system bars with an
* inward swipe along the region where the system bars normally appear.
*
* <p>The {@link #FLAG_IMMERSIVE_STICKY} flag can be used to control how the system bars are
* displayed.
*/
public static final int LEVEL_IMMERSIVE = 3;
/**
* When this flag is set, the
* {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}
* flag will be set on older devices, making the status bar "float" on top
* of the activity layout. This is most useful when there are no controls at
* the top of the activity layout.
* <p>
* This flag isn't used on newer devices because the <a
* href="http://developer.android.com/design/patterns/actionbar.html">action
* bar</a>, the most important structural element of an Android app, should
* be visible and not obscured by the system UI.
*/
public static final int FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES = 0x1;
/**
* Used with {@link #LEVEL_IMMERSIVE}. When this flag is set, an inward swipe in the system
* bars areas will cause the system bars to temporarily appear in a semi-transparent state,
* but no flags are cleared, and your system UI visibility change listeners are not triggered.
* The bars automatically hide again after a short delay, or if the user interacts with the
* middle of the screen.
*/
public static final int FLAG_IMMERSIVE_STICKY = 0x2;
private static final String LOG_TAG = SystemUiHelper.class.getSimpleName();
private final SystemUiHelperImpl mImpl;
private final Handler mHandler;
private final Runnable mHideRunnable;
/**
* Construct a new SystemUiHelper.
*
* @param activity The Activity who's system UI should be changed
* @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE},
* {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or
* {@link #LEVEL_IMMERSIVE}
* @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and
* {@link #FLAG_IMMERSIVE_STICKY}
*/
public SystemUiHelper(Activity activity, int level, int flags) {
this(activity, level, flags, null);
}
/**
* Construct a new SystemUiHelper.
*
* @param activity The Activity who's system UI should be changed
* @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE},
* {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or
* {@link #LEVEL_IMMERSIVE}
* @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and
* {@link #FLAG_IMMERSIVE_STICKY}
* @param listener A listener which is called when the system visibility is changed
*/
public SystemUiHelper(Activity activity, int level, int flags,
OnVisibilityChangeListener listener) {
mHandler = new Handler(Looper.getMainLooper());
mHideRunnable = new HideRunnable();
// Create impl
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mImpl = new SystemUiHelperImplKK(activity, level, flags, listener);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mImpl = new SystemUiHelperImplJB(activity, level, flags, listener);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mImpl = new SystemUiHelperImplICS(activity, level, flags, listener);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mImpl = new SystemUiHelperImplHC(activity, level, flags, listener);
} else {
mImpl = new SystemUiHelperImplBase(activity, level, flags, listener);
}
}
/**
* @return true if the system UI is currently showing. What this means depends on the mode this
* {@link android.example.android.systemuivis.SystemUiHelper} was instantiated with.
*/
public boolean isShowing() {
return mImpl.isShowing();
}
/**
* Show the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was
* instantiated with.
*
* <p>Any currently queued delayed hide requests will be removed.
*/
public void show() {
// Ensure that any currently queued hide calls are removed
removeQueuedRunnables();
mImpl.show();
}
/**
* Hide the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was
* instantiated with.
*
* <p>Any currently queued delayed hide requests will be removed.
*/
public void hide() {
// Ensure that any currently queued hide calls are removed
removeQueuedRunnables();
mImpl.hide();
}
/**
* Request that the system UI is hidden after a delay.
*
* <p>Any currently queued delayed hide requests will be removed.
*
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
*/
public void delayHide(long delayMillis) {
// Ensure that any currently queued hide calls are removed
removeQueuedRunnables();
mHandler.postDelayed(mHideRunnable, delayMillis);
}
/**
* Toggle whether the system UI is displayed.
*/
public void toggle() {
if (mImpl.isShowing()) {
mImpl.hide();
} else {
mImpl.show();
}
}
private void removeQueuedRunnables() {
// Ensure that any currently queued hide calls are removed
mHandler.removeCallbacks(mHideRunnable);
}
/**
* A callback interface used to listen for system UI visibility changes.
*/
public interface OnVisibilityChangeListener {
/**
* Called when the system UI visibility has changed.
*
* @param visible True if the system UI is visible.
*/
public void onVisibilityChange(boolean visible);
}
static abstract class SystemUiHelperImpl {
final Activity mActivity;
final int mLevel;
final int mFlags;
final OnVisibilityChangeListener mOnVisibilityChangeListener;
boolean mIsShowing = true;
SystemUiHelperImpl(Activity activity, int level, int flags,
OnVisibilityChangeListener onVisibilityChangeListener) {
mActivity = activity;
mLevel = level;
mFlags = flags;
mOnVisibilityChangeListener = onVisibilityChangeListener;
}
abstract void show();
abstract void hide();
boolean isShowing() {
return mIsShowing;
}
void setIsShowing(boolean isShowing) {
mIsShowing = isShowing;
if (mOnVisibilityChangeListener != null) {
mOnVisibilityChangeListener.onVisibilityChange(mIsShowing);
}
}
}
/**
* Base implementation. Used on API level 10 and below.
*/
static class SystemUiHelperImplBase extends SystemUiHelperImpl {
SystemUiHelperImplBase(Activity activity, int level, int flags,
OnVisibilityChangeListener onVisibilityChangeListener) {
super(activity, level, flags, onVisibilityChangeListener);
if ((mFlags & SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) != 0) {
mActivity.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
}
@Override
void show() {
if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) {
mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setIsShowing(true);
}
}
@Override
void hide() {
if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) {
mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setIsShowing(false);
}
}
}
private class HideRunnable implements Runnable {
@Override
public void run() {
hide();
}
}
}
/*
* Copyright (C) 2014 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 com.example.android.systemuivis;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl
implements View.OnSystemUiVisibilityChangeListener {
final View mDecorView;
SystemUiHelperImplHC(Activity activity, int level, int flags,
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
super(activity, level, flags, onVisibilityChangeListener);
mDecorView = activity.getWindow().getDecorView();
mDecorView.setOnSystemUiVisibilityChangeListener(this);
}
@Override
void show() {
mDecorView.setSystemUiVisibility(createShowFlags());
}
@Override
void hide() {
mDecorView.setSystemUiVisibility(createHideFlags());
}
@Override
public final void onSystemUiVisibilityChange(int visibility) {
if ((visibility & createTestFlags()) != 0) {
onSystemUiHidden();
} else {
onSystemUiShown();
}
}
protected void onSystemUiShown() {
ActionBar ab = mActivity.getActionBar();
if (ab != null) {
ab.show();
}
mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setIsShowing(true);
}
protected void onSystemUiHidden() {
ActionBar ab = mActivity.getActionBar();
if (ab != null) {
ab.hide();
}
mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setIsShowing(false);
}
protected int createShowFlags() {
return View.STATUS_BAR_VISIBLE;
}
protected int createHideFlags() {
return View.STATUS_BAR_HIDDEN;
}
protected int createTestFlags() {
return View.STATUS_BAR_HIDDEN;
}
}
/*
* Copyright (C) 2014 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 com.example.android.systemuivis;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.view.View;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
class SystemUiHelperImplICS extends SystemUiHelperImplHC {
SystemUiHelperImplICS(Activity activity, int level, int flags,
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
super(activity, level, flags, onVisibilityChangeListener);
}
@Override
protected int createShowFlags() {
return View.SYSTEM_UI_FLAG_VISIBLE;
}
@Override
protected int createTestFlags() {
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
// Intentionally override test flags.
return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
return View.SYSTEM_UI_FLAG_LOW_PROFILE;
}
@Override
protected int createHideFlags() {
int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
return flag;
}
}
/*
* Copyright (C) 2014 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 com.example.android.systemuivis;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.os.Build;
import android.view.View;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
class SystemUiHelperImplJB extends SystemUiHelperImplICS {
SystemUiHelperImplJB(Activity activity, int level, int flags,
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
super(activity, level, flags, onVisibilityChangeListener);
}
@Override
protected int createShowFlags() {
int flag = super.createShowFlags();
if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) {
flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
}
return flag;
}
@Override
protected int createHideFlags() {
int flag = super.createHideFlags();
if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) {
flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN;
if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) {
flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
}
return flag;
}
@Override
protected void onSystemUiShown() {
if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
// Manually show the action bar when in low profile mode.
ActionBar ab = mActivity.getActionBar();
if (ab != null) {
ab.show();
}
}
setIsShowing(true);
}
@Override
protected void onSystemUiHidden() {
if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
// Manually hide the action bar when in low profile mode.
ActionBar ab = mActivity.getActionBar();
if (ab != null) {
ab.hide();
}
}
setIsShowing(false);
}
}
/*
* Copyright (C) 2014 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 com.example.android.systemuivis;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.view.View;
@TargetApi(Build.VERSION_CODES.KITKAT)
class SystemUiHelperImplKK extends SystemUiHelperImplJB {
SystemUiHelperImplKK(Activity activity, int level, int flags,
SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) {
super(activity, level, flags, onVisibilityChangeListener);
}
@Override
protected int createHideFlags() {
int flag = super.createHideFlags();
if (mLevel == SystemUiHelper.LEVEL_IMMERSIVE) {
// If the client requested immersive mode, and we're on Android 4.4
// or later, add relevant flags. Applying HIDE_NAVIGATION without
// IMMERSIVE prevents the activity from accepting all touch events,
// so we only do this on Android 4.4 and later (where IMMERSIVE is
// present).
flag |= ((mFlags & SystemUiHelper.FLAG_IMMERSIVE_STICKY) != 0)
? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
: View.SYSTEM_UI_FLAG_IMMERSIVE;
}
return flag;
}
}
@hvisser

This comment has been minimized.

Copy link

hvisser commented Aug 29, 2014

Nitpicking: private static final String LOG_TAG = SystemUiHelper.class.getSimpleName(); will turn logging on Proguarded builds pretty useless. Then again, shouldn't log in production builds. So yeah. Nitpicking.

@AKiniyalocts

This comment has been minimized.

Copy link

AKiniyalocts commented Aug 29, 2014

This is great! Thanks!

@angeldevil

This comment has been minimized.

Copy link

angeldevil commented Sep 1, 2014

@Override
protected void onSystemUiShown() {
    if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
        // Manually show the action bar when in low profile mode.
        ActionBar ab = mActivity.getActionBar();
        if (ab != null) {
            ab.show();
        }
    }

    setIsShowing(false); // Should be true ?
}
@agustinsivoplas

This comment has been minimized.

Copy link

agustinsivoplas commented Sep 18, 2014

Hi, I am trying to hide "for ever" the bottom bar but in your example, If I touch the screen the bars appear again. The idea is use devices to show information to normal users, I do not want that the users interact with the tablet. Do you have any idea? All the best, Agustin

@alexdzeshko

This comment has been minimized.

Copy link

alexdzeshko commented Sep 19, 2014

angeldevil commented 18 days ago

@Override
protected void onSystemUiShown() {
    if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
        // Manually show the action bar when in low profile mode.
        ActionBar ab = mActivity.getActionBar();
        if (ab != null) {
            ab.show();
        }
    }

    setIsShowing(false); // Should be true ?
}

Yes, i think there is a mistake. And in

@Override
    protected void onSystemUiHidden() {
        if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) {
            // Manually hide the action bar when in low profile mode.
            ActionBar ab = mActivity.getActionBar();
            if (ab != null) {
                ab.hide();
            }
        }

        setIsShowing(true); //should be false
    }

as well. Please, amend this.
Anyway, thanks for great stuff :)

@b95505017

This comment has been minimized.

Copy link

b95505017 commented Nov 22, 2014

maybe it would be better to add reset() to clear all flags :)

@thierryd

This comment has been minimized.

Copy link

thierryd commented Dec 2, 2014

Any update for Lolipop?

@hurelhuyag

This comment has been minimized.

Copy link

hurelhuyag commented Dec 11, 2014

How to use? Below example is right?

    SystemUiHelper uiHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.video);
        uiHelper = new SystemUiHelper(this, SystemUiHelper.LEVEL_LEAN_BACK, 0);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_UP:
                if (uiHelper.isShowing()){
                    uiHelper.hide();
                }else{
                    uiHelper.show();
                }
                break;
        }
        return super.onTouchEvent(event);
    }
@joshallenit

This comment has been minimized.

Copy link

joshallenit commented Feb 9, 2015

Hi, great code, thanks! The visibility callback is reversed for JellyBean+. Merge my fork for the fix.

https://gist.github.com/joshallenit/03b7966d2e19f3d94313

@ghost

This comment has been minimized.

Copy link

ghost commented Jul 17, 2015

Hi, very useful!! Thanks!

@vuhung3990

This comment has been minimized.

Copy link

vuhung3990 commented Sep 23, 2015

how to use ? please show a sample code, thank you

@kaoruAngel

This comment has been minimized.

@DreaminginCodeZH

This comment has been minimized.

Copy link

DreaminginCodeZH commented Nov 13, 2015

Note that sometimes View.setSystemUiVisibility() will not function until next layout. You need to add mDecorView.requestLayout() to SystemUiHelperImplHC.show|hide() to fix this.

@DreaminginCodeZH

This comment has been minimized.

Copy link

DreaminginCodeZH commented Dec 1, 2015

I've packaged this gist with the fix above, and a sample on how to use Toolbar with immersive mode.

In case someone is interested, please checkout DreaminginCodeZH/SystemUiHelper.

@IvanPosohov

This comment has been minimized.

Copy link

IvanPosohov commented Dec 17, 2015

Can someone explain me why method createShowFlags() in SystemUiHelperImplJB.java in some cases returns flags View.SYSTEM_UI_FLAG_LAYOUT_STABLE, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION (lines 37-42)?

@rajeshkumar12345

This comment has been minimized.

Copy link

rajeshkumar12345 commented Mar 21, 2016

hai, I am stuck with status bar hiding. Let me explain I am Implementing activity and also place manifest file in android:windowSoftInputMode="adjustResize" for keyboard place below edittext, it is working fine but i want also hide navigation bar, so i am using "Theme.Holo.Light.NoActionBar.Fullscreen" theme. But when keyboard appearing app navigation bar also scrolling. How can i restrict scrolling navigation bar and hide notification bar when keyboard appearing. And also i am using SystemUiHelper systemUiHelper = new SystemUiHelper(this,SystemUiHelper.LEVEL_IMMERSIVE,SystemUiHelper.FLAG_IMMERSIVE_STICKY);
systemUiHelper.toggle(); like that. But i didn't get any chance to resolve my problem. Please tell me.
screenshot_2016-03-19-12-13-56
screenshot_2016-03-19-12-16-17

@mohocp

This comment has been minimized.

Copy link

mohocp commented Jan 6, 2017

The RecycleView layout in landscape orientation shift to right after I used uiHelper.show(), Please any one can help to over come this problem

@deshan

This comment has been minimized.

Copy link

deshan commented May 18, 2017

Any help for getting this work for Android 7.0?
Checked in S8 +, but see huge blank area in the top and bottom

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.