From 2fab00eb835206d671d9bc29a84b432c8e969c1c Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Mon, 6 Apr 2020 15:06:07 -0700 Subject: [PATCH] Fix AlertDialogs built by platform views (#17511) --- .../platform/SingleViewPresentation.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index 63bf92555c410..993f88efb2a8c 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -9,6 +9,7 @@ import static android.view.View.OnFocusChangeListener; import android.annotation.TargetApi; +import android.app.AlertDialog; import android.app.Presentation; import android.content.Context; import android.content.ContextWrapper; @@ -102,6 +103,9 @@ static class PresentationState { private boolean startFocused = false; + // The context for the application window that hosts FlutterView. + private final Context outerContext; + /** * Creates a presentation that will use the view factory to create a new platform view in the * presentation's onCreate, and attach it. @@ -120,6 +124,7 @@ public SingleViewPresentation( this.viewId = viewId; this.createParams = createParams; this.focusChangeListener = focusChangeListener; + this.outerContext = outerContext; state = new PresentationState(); getWindow() .setFlags( @@ -147,6 +152,7 @@ public SingleViewPresentation( viewFactory = null; this.state = state; this.focusChangeListener = focusChangeListener; + this.outerContext = outerContext; getWindow() .setFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, @@ -173,7 +179,8 @@ protected void onCreate(Bundle savedInstanceState) { // Our base mContext has already been wrapped with an IMM cache at instantiation time, but // we want to wrap it again here to also return state.windowManagerHandler. - Context context = new PresentationContext(getContext(), state.windowManagerHandler); + Context context = + new PresentationContext(getContext(), state.windowManagerHandler, outerContext); if (state.platformView == null) { state.platformView = viewFactory.create(context, viewId, createParams); @@ -304,15 +311,34 @@ public Context createDisplayContext(Display display) { private static class PresentationContext extends ContextWrapper { private @NonNull final WindowManagerHandler windowManagerHandler; private @Nullable WindowManager windowManager; + private final Context flutterAppWindowContext; - PresentationContext(Context base, @NonNull WindowManagerHandler windowManagerHandler) { + PresentationContext( + Context base, + @NonNull WindowManagerHandler windowManagerHandler, + Context flutterAppWindowContext) { super(base); this.windowManagerHandler = windowManagerHandler; + this.flutterAppWindowContext = flutterAppWindowContext; } @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { + if (isCalledFromAlertDialog()) { + // Alert dialogs are showing on top of the entire application and should not be limited to + // the virtual + // display. If we detect that an android.app.AlertDialog constructor is what's fetching + // the window manager + // we return the one for the application's window. + // + // Note that if we don't do this AlertDialog will throw a ClassCastException as down the + // line it tries + // to case this instance to a WindowManagerImpl which the object returned by + // getWindowManager is not + // a subclass of. + return flutterAppWindowContext.getSystemService(name); + } return getWindowManager(); } return super.getSystemService(name); @@ -324,6 +350,17 @@ private WindowManager getWindowManager() { } return windowManager; } + + private boolean isCalledFromAlertDialog() { + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + for (int i = 0; i < stackTraceElements.length && i < 11; i++) { + if (stackTraceElements[i].getClassName().equals(AlertDialog.class.getCanonicalName()) + && stackTraceElements[i].getMethodName().equals("")) { + return true; + } + } + return false; + } } /*