Sticky immersive mode disabled after soft keyboard shown
Taken from this sample app by Google, you need to append this to the end of your activity, before the last end bracket:
@Overridepublic void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); // When the window loses focus (e.g. the action overflow is shown), // cancel any pending hide action. When the window gains focus, // hide the system UI. if (hasFocus) { delayedHide(300); } else { mHideHandler.removeMessages(0); }}private void hideSystemUI() { getWindow().getDecorView().setSystemUiVisibility( 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_LOW_PROFILE | View.SYSTEM_UI_FLAG_IMMERSIVE );}private void showSystemUI() { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN );}private final Handler mHideHandler = new Handler() { @Override public void handleMessage(Message msg) { hideSystemUI(); }};private void delayedHide(int delayMillis) { mHideHandler.removeMessages(0); mHideHandler.sendEmptyMessageDelayed(0, delayMillis);}
And you should be good. :)
I put this code at onCreate() observer the layout changes
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect rect = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); int screenHeight = getWindow().getDecorView().getRootView().getHeight(); int keyboardHeight = screenHeight - rect.bottom; if (keyboardHeight > screenHeight * 0.15) { setToImmersiveMode(); } }});private void setToImmersiveMode() { // set to immersive getWindow().getDecorView().setSystemUiVisibility( 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); }
I suggest extending AppCompatActivity into a new class (ImmersiveAppCompatActivity). By doing this, any activity that you create using this class will have built in handling of immersive mode.
If you try and set immersive mode too quickly after the soft keyboard has appeared, it will not hide.
Also note that the handler has been improved by switching to a static handler - this will prevent leaks if the user leaves the activity before the GUI is hidden.
public abstract class ImmersiveAppCompatActivity extends AppCompatActivity { private HideHandler mHideHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // create a handler to set immersive mode on a delay mHideHandler = new HideHandler(this); } @Override protected void onResume() { super.onResume(); setToImmersiveMode(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if(hasFocus) { mHideHandler.removeMessages(0); mHideHandler.sendEmptyMessageDelayed(0, 300); } else mHideHandler.removeMessages(0); } private void setToImmersiveMode() { // set to immersive getWindow().getDecorView().setSystemUiVisibility( 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 class HideHandler extends Handler { private final WeakReference<ImmersiveAppCompatActivity> mActivity; HideHandler(ImmersiveAppCompatActivity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { ImmersiveAppCompatActivity activity = mActivity.get(); if(activity != null) activity.setToImmersiveMode(); } }}
Here's the Kotlin version:
abstract class ImmersiveAppCompatActivity : AppCompatActivity() { override fun onResume() { super.onResume() setToImmersiveMode() } override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) val runnable = Runnable { setToImmersiveMode() } val handler = Handler(Looper.getMainLooper()) handler.postDelayed(runnable, 300) } private fun setToImmersiveMode() { window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) }}
Now, create your activity using this class:
public class SettingsActivity extends ImmersiveAppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); }}
I've tested this in Android 5.1 and 7.0 to work in a full screen app that has no action bar.
Additionally, if you using the keyboard in an EditText be aware of the imeOptions. In landscape mode you can get strange full screen editing behavior. This can be disabled by setting imeOptions flags which are contained in the class EditorInfo:
<EditText android:layout_width="@dimen/pin_width" android:layout_height="wrap_content" android:inputType="numberPassword" android:imeOptions="flagNoExtractUi" android:ems="10" android:id="@+id/editTextPIN" android:textSize="@dimen/pin_large_text_size"/>
https://developer.android.com/reference/android/view/inputmethod/EditorInfo.html