¿Cómo mantengo el modo inmersivo en los cuadros de diálogo?

¿Cómo mantengo el nuevo Modo inmersivo cuando mis actividades muestran un cuadro de diálogo personalizado?

Estoy usando este código para mantener el modo de inmersión en cuadros de diálogo, pero con esta solución, la barra de navegación aparece durante menos de un segundo cuando inicio mi cuadro de diálogo personalizado, luego desaparece.

Aquí hay un video que explica mejor el problema (vea la parte inferior de la pantalla cuando aparece la barra de navegación): http://youtu.be/epnd5ghey8g

¿Cómo evito este comportamiento?

CÓDIGO

El padre de todas las actividades en mi aplicación:

public abstract class ImmersiveActivity extends Activity { @SuppressLint("NewApi") private void disableImmersiveMode() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN); } } @SuppressLint("NewApi") private void enableImmersiveMode() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 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_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); } } /** * Set the Immersive mode or not according to its state: enabled or not. */ protected void updateSystemUiVisibility() { // Retrieve if the Immersive mode is enabled or not. boolean enabled = getSharedPreferences(Util.PREF_NAME, 0).getBoolean( "immersive_mode_enabled", true); if (enabled) enableImmersiveMode(); else disableImmersiveMode(); } @Override public void onResume() { super.onResume(); updateSystemUiVisibility(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); updateSystemUiVisibility(); } } 


Todos mis onCreate(. . .) personalizados llaman a este método en su método onCreate(. . .) :

 /** * Copy the visibility of the Activity that has started the dialog {@link mActivity}. If the * activity is in Immersive mode the dialog will be in Immersive mode too and vice versa. */ @SuppressLint("NewApi") private void copySystemUiVisibility() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { getWindow().getDecorView().setSystemUiVisibility( mActivity.getWindow().getDecorView().getSystemUiVisibility()); } } 


EDITAR – LA SOLUCIÓN (gracias a Beaver6813, mira su respuesta para más detalles):

Todos mis cuadros de diálogo personalizados anulan el método show de esta manera:

 /** * An hack used to show the dialogs in Immersive Mode (that is with the NavBar hidden). To * obtain this, the method makes the dialog not focusable before showing it, change the UI * visibility of the window like the owner activity of the dialog and then (after showing it) * makes the dialog focusable again. */ @Override public void show() { // Set the dialog to not focusable. getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); copySystemUiVisibility(); // Show the dialog with NavBar hidden. super.show(); // Set the dialog to focusable again. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); } 

Después de una gran cantidad de investigaciones sobre el tema hay una solución hacky para esto, que involucró desgarrar la clase Dialog para encontrarla. La barra de navegación se muestra cuando la ventana de diálogo se agrega al Administrador de ventanas, incluso si configura la visibilidad de la interfaz de usuario antes de agregarlo al administrador. En el ejemplo de Android Immersive , comentó que:

 // * Uses semi-transparent bars for the nav and status bars // * This UI flag will *not* be cleared when the user interacts with the UI. // When the user swipes, the bars will temporarily appear for a few seconds and then // disappear again. 

Creo que eso es lo que estamos viendo aquí (que una interacción del usuario se desencadena cuando se agrega una nueva vista de ventana enfocable al administrador).

¿Cómo podemos solucionar esto? Haga que el diálogo no sea enfocable cuando lo creemos (para que no desencadene una interacción del usuario) y luego haga que se pueda enfocar después de que se muestre.

 //Here's the magic.. //Set the dialog to not focusable (makes navigation ignore us adding the window) dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); //Show the dialog! dialog.show(); //Set the dialog to immersive dialog.getWindow().getDecorView().setSystemUiVisibility( context.getWindow().getDecorView().getSystemUiVisibility()); //Clear the not focusable flag from the window dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); 

Claramente, esto no es ideal, pero parece ser un error de Android, deberían verificar si la ventana tiene un conjunto inmersivo.

He actualizado mi código de prueba de trabajo (perdona el desorden hacky) a Github . Probé con el emulador Nexus 5, probablemente explote con algo menos que KitKat, pero solo como prueba de concepto.

Si desea usar onCreateDialog () , intente con esta clase. Funciona bastante bien para mí…

 public class ImmersiveDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog alertDialog = new AlertDialog.Builder(getActivity()) .setTitle("Example Dialog") .setMessage("Some text.") .create(); // Temporarily set the dialogs window to not focusable to prevent the short // popup of the navigation bar. alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); return alertDialog; } public void showImmersive(Activity activity) { // Show the dialog. show(activity.getFragmentManager(), null); // It is necessary to call executePendingTransactions() on the FragmentManager // before hiding the navigation bar, because otherwise getWindow() would raise a // NullPointerException since the window was not yet created. getFragmentManager().executePendingTransactions(); // Hide the navigation bar. It is important to do this after show() was called. // If we would do this in onCreateDialog(), we would get a requestFeature() // error. getDialog().getWindow().getDecorView().setSystemUiVisibility( getActivity().getWindow().getDecorView().getSystemUiVisibility() ); // Make the dialogs window focusable again. getDialog().getWindow().clearFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ); } } 

Para mostrar el diálogo, haga lo siguiente en su actividad …

 new ImmersiveDialogFragment().showImmersive(this); 

Para su información, gracias a la respuesta de @ Beaver6813, he podido hacer que esto funcione usando DialogFragment.

en el método onCreateView de mi DialogFragment, acabo de agregar lo siguiente:

  getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); getDialog().getWindow().getDecorView().setSystemUiVisibility(getActivity().getWindow().getDecorView().getSystemUiVisibility()); getDialog().setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { //Clear the not focusable flag from the window getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); //Update the WindowManager with the new attributes (no nicer way I know of to do this).. WindowManager wm = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(getDialog().getWindow().getDecorView(), getDialog().getWindow().getAttributes()); } }); 

Combinando las respuestas aquí hice una clase abstracta que funciona en todos los casos:

 public abstract class ImmersiveDialogFragment extends DialogFragment { @Override public void setupDialog(Dialog dialog, int style) { super.setupDialog(dialog, style); // Make the dialog non-focusable before showing it dialog.getWindow().setFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); } @Override public void show(FragmentManager manager, String tag) { super.show(manager, tag); showImmersive(manager); } @Override public int show(FragmentTransaction transaction, String tag) { int result = super.show(transaction, tag); showImmersive(getFragmentManager()); return result; } private void showImmersive(FragmentManager manager) { // It is necessary to call executePendingTransactions() on the FragmentManager // before hiding the navigation bar, because otherwise getWindow() would raise a // NullPointerException since the window was not yet created. manager.executePendingTransactions(); // Copy flags from the activity, assuming it's fullscreen. // It is important to do this after show() was called. If we would do this in onCreateDialog(), // we would get a requestFeature() error. getDialog().getWindow().getDecorView().setSystemUiVisibility( getActivity().getWindow().getDecorView().getSystemUiVisibility() ); // Make the dialogs window focusable again getDialog().getWindow().clearFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ); } } 

Cuando está creando su propio DialogFragment, solo necesita reemplazar este método.

 @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); return dialog; } 

Esto también funciona con el método onDismiss de su fragmento de diálogo. Y dentro de ese método, llame al método de la actividad a la que está conectado para establecer nuevamente los indicadores de pantalla completa.

 @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); Logger.e(TAG, "onDismiss"); Log.e("CallBack", "CallBack"); if (getActivity() != null && getActivity() instanceof LiveStreamingActivity) { ((YourActivity) getActivity()).hideSystemUI(); } } 

Y en tu actividad agrega este método:

 public void hideSystemUI() { // Set the IMMERSIVE flag. // Set the content to appear under the system bars so that the content // doesn't resize when the system bars hide and show. 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 // hide nav bar | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } 

Sé que esta es una publicación anterior, pero mi respuesta puede ayudar a otros.

A continuación se muestra la solución hacky para el efecto Immersive en Diálogos:

 public static void showImmersiveDialog(final Dialog mDialog, final Activity mActivity) { //Set the dialog to not focusable mDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility()); mDialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { //Clear the not focusable flag from the window mDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); //Update the WindowManager with the new attributes WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(mDialog.getWindow().getDecorView(), mDialog.getWindow().getAttributes()); } }); mDialog.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int visibility) { if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility()); } } }); } public static int setSystemUiVisibility() { return 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; }