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 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. + *
+ * This flag isn't used on newer devices because the action + * bar, 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 SystemUiHelper} was instantiated with. + */ + public boolean isShowing() { + return mImpl.isShowing(); + } + + /** + * Show the system UI. What this means depends on the mode this {@link SystemUiHelper} was + * instantiated with. + * + *
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 SystemUiHelper} was + * instantiated with. + * + *
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. + * + *
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.
+ */
+ 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();
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java
new file mode 100644
index 000000000..c7195457e
--- /dev/null
+++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java
@@ -0,0 +1,93 @@
+/*
+ * 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.hippo.util;
+
+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;
+ }
+}
diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java
new file mode 100644
index 000000000..dcf2e249b
--- /dev/null
+++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java
@@ -0,0 +1,57 @@
+/*
+ * 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.hippo.util;
+
+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;
+ }
+}
diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java
new file mode 100644
index 000000000..26fb0c1f0
--- /dev/null
+++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java
@@ -0,0 +1,90 @@
+/*
+ * 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.hippo.util;
+
+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);
+ }
+}
diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java
new file mode 100644
index 000000000..8bc5be6c3
--- /dev/null
+++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java
@@ -0,0 +1,50 @@
+/*
+ * 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.hippo.util;
+
+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;
+ }
+
+}
diff --git a/app/src/main/res/layout/activity_gallery.xml b/app/src/main/res/layout/activity_gallery.xml
index 6cf2a661e..656555576 100644
--- a/app/src/main/res/layout/activity_gallery.xml
+++ b/app/src/main/res/layout/activity_gallery.xml
@@ -20,6 +20,7 @@
android:layout_height="match_parent">