Skip to content

Commit

Permalink
Improve platform views performance (flutter#31198)
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Garcia authored Feb 12, 2022
1 parent 34984e7 commit 9f54167
Show file tree
Hide file tree
Showing 25 changed files with 927 additions and 1,612 deletions.
3 changes: 1 addition & 2 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1189,10 +1189,9 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java
FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java
FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java
FILE: ../../../flutter/shell/platform/android/io/flutter/util/Predicate.java
Expand Down
3 changes: 1 addition & 2 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,9 @@ android_java_sources = [
"io/flutter/plugin/platform/PlatformViewFactory.java",
"io/flutter/plugin/platform/PlatformViewRegistry.java",
"io/flutter/plugin/platform/PlatformViewRegistryImpl.java",
"io/flutter/plugin/platform/PlatformViewWrapper.java",
"io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java",
"io/flutter/plugin/platform/PlatformViewsController.java",
"io/flutter/plugin/platform/SingleViewPresentation.java",
"io/flutter/plugin/platform/VirtualDisplayController.java",
"io/flutter/util/PathUtils.java",
"io/flutter/util/Preconditions.java",
"io/flutter/util/Predicate.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public boolean onTouchEvent(@NonNull MotionEvent event) {
* the gesture pointers into screen coordinates.
* @return True if the event was handled.
*/
public boolean onTouchEvent(@NonNull MotionEvent event, Matrix transformMatrix) {
public boolean onTouchEvent(@NonNull MotionEvent event, @NonNull Matrix transformMatrix) {
int pointerCount = event.getPointerCount();

// Prepare a data packet of the appropriate size and order.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,6 @@ navigationBarVisible && guessBottomKeyboardInset(insets) == 0
+ viewportMetrics.viewInsetBottom);

sendViewportMetricsToFlutter();

return newInsets;
}

Expand Down Expand Up @@ -867,21 +866,6 @@ public InputConnection onCreateInputConnection(@NonNull EditorInfo outAttrs) {
return textInputPlugin.createInputConnection(this, keyboardManager, outAttrs);
}

/**
* Allows a {@code View} that is not currently the input connection target to invoke commands on
* the {@link android.view.inputmethod.InputMethodManager}, which is otherwise disallowed.
*
* <p>Returns true to allow non-input-connection-targets to invoke methods on {@code
* InputMethodManager}, or false to exclusively allow the input connection target to invoke such
* methods.
*/
@Override
public boolean checkInputConnectionProxy(View view) {
return flutterEngine != null
? flutterEngine.getPlatformViewsController().checkInputConnectionProxy(view)
: super.checkInputConnectionProxy(view);
}

/**
* Invoked when a hardware key is pressed or released.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import io.flutter.embedding.android.AndroidTouchProcessor;
import io.flutter.util.ViewUtils;

/**
* A view that applies the {@link io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack} to
Expand Down Expand Up @@ -49,31 +49,6 @@ public FlutterMutatorView(@NonNull Context context) {
this(context, 1, /* androidTouchProcessor=*/ null);
}

/**
* Determines if the current view or any descendant view has focus.
*
* @param root The root view.
* @return True if the current view or any descendant view has focus.
*/
@VisibleForTesting
public static boolean childHasFocus(@Nullable View root) {
if (root == null) {
return false;
}
if (root.hasFocus()) {
return true;
}
if (root instanceof ViewGroup) {
final ViewGroup viewGroup = (ViewGroup) root;
for (int idx = 0; idx < viewGroup.getChildCount(); idx++) {
if (childHasFocus(viewGroup.getChildAt(idx))) {
return true;
}
}
}
return false;
}

@Nullable @VisibleForTesting ViewTreeObserver.OnGlobalFocusChangeListener activeFocusListener;

/**
Expand All @@ -95,7 +70,7 @@ public void setOnDescendantFocusChangeListener(@NonNull OnFocusChangeListener us
new ViewTreeObserver.OnGlobalFocusChangeListener() {
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
userFocusListener.onFocusChange(mutatorView, childHasFocus(mutatorView));
userFocusListener.onFocusChange(mutatorView, ViewUtils.childHasFocus(mutatorView));
}
};
observer.addOnGlobalFocusChangeListener(activeFocusListener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -64,6 +65,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
case "resize":
resize(call, result);
break;
case "offset":
offset(call, result);
break;
case "touch":
touch(call, result);
break;
Expand All @@ -82,29 +86,40 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
}

private void create(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Map<String, Object> createArgs = call.arguments();
boolean usesHybridComposition =
final Map<String, Object> createArgs = call.arguments();
// TODO(egarciad): Remove the "hybrid" case.
final boolean usesPlatformViewLayer =
createArgs.containsKey("hybrid") && (boolean) createArgs.get("hybrid");
// In hybrid mode, the size of the view is determined by the size of the Flow layer.
double width = (usesHybridComposition) ? 0 : (double) createArgs.get("width");
double height = (usesHybridComposition) ? 0 : (double) createArgs.get("height");

PlatformViewCreationRequest request =
new PlatformViewCreationRequest(
(int) createArgs.get("id"),
(String) createArgs.get("viewType"),
width,
height,
(int) createArgs.get("direction"),
createArgs.containsKey("params")
? ByteBuffer.wrap((byte[]) createArgs.get("params"))
: null);
final ByteBuffer additionalParams =
createArgs.containsKey("params")
? ByteBuffer.wrap((byte[]) createArgs.get("params"))
: null;
try {
if (usesHybridComposition) {
handler.createAndroidViewForPlatformView(request);
if (usesPlatformViewLayer) {
final PlatformViewCreationRequest request =
new PlatformViewCreationRequest(
(int) createArgs.get("id"),
(String) createArgs.get("viewType"),
0,
0,
0,
0,
(int) createArgs.get("direction"),
additionalParams);
handler.createForPlatformViewLayer(request);
result.success(null);
} else {
long textureId = handler.createVirtualDisplayForPlatformView(request);
final PlatformViewCreationRequest request =
new PlatformViewCreationRequest(
(int) createArgs.get("id"),
(String) createArgs.get("viewType"),
createArgs.containsKey("top") ? (double) createArgs.get("top") : 0.0,
createArgs.containsKey("left") ? (double) createArgs.get("left") : 0.0,
(double) createArgs.get("width"),
(double) createArgs.get("height"),
(int) createArgs.get("direction"),
additionalParams);
long textureId = handler.createForTextureLayer(request);
result.success(textureId);
}
} catch (IllegalStateException exception) {
Expand All @@ -115,15 +130,9 @@ private void create(@NonNull MethodCall call, @NonNull MethodChannel.Result resu
private void dispose(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Map<String, Object> disposeArgs = call.arguments();
int viewId = (int) disposeArgs.get("id");
boolean usesHybridComposition =
disposeArgs.containsKey("hybrid") && (boolean) disposeArgs.get("hybrid");

try {
if (usesHybridComposition) {
handler.disposeAndroidViewForPlatformView(viewId);
} else {
handler.disposeVirtualDisplayForPlatformView(viewId);
}
handler.dispose(viewId);
result.success(null);
} catch (IllegalStateException exception) {
result.error("error", detailedExceptionString(exception), null);
Expand All @@ -138,14 +147,28 @@ private void resize(@NonNull MethodCall call, @NonNull MethodChannel.Result resu
(double) resizeArgs.get("width"),
(double) resizeArgs.get("height"));
try {
handler.resizePlatformView(
resizeRequest,
new Runnable() {
@Override
public void run() {
result.success(null);
}
});
final PlatformViewBufferSize sz = handler.resize(resizeRequest);
if (sz == null) {
result.error("error", "Failed to resize the platform view", null);
} else {
final Map<String, Object> response = new HashMap<>();
response.put("width", (double) sz.width);
response.put("height", (double) sz.height);
result.success(response);
}
} catch (IllegalStateException exception) {
result.error("error", detailedExceptionString(exception), null);
}
}

private void offset(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Map<String, Object> offsetArgs = call.arguments();
try {
handler.offset(
(int) offsetArgs.get("id"),
(double) offsetArgs.get("top"),
(double) offsetArgs.get("left"));
result.success(null);
} catch (IllegalStateException exception) {
result.error("error", detailedExceptionString(exception), null);
}
Expand Down Expand Up @@ -249,36 +272,40 @@ public interface PlatformViewsHandler {
* The Flutter application would like to display a new Android {@code View}, i.e., platform
* view.
*
* <p>The Android {@code View} is added to the view hierarchy.
*/
void createAndroidViewForPlatformView(@NonNull PlatformViewCreationRequest request);

/**
* The Flutter application would like to dispose of an existing Android {@code View} rendered in
* the view hierarchy.
* <p>The Android View is added to the view hierarchy. This view is rendered in the Flutter
* framework by a PlatformViewLayer.
*
* @param request The metadata sent from the framework.
*/
void disposeAndroidViewForPlatformView(int viewId);
void createForPlatformViewLayer(@NonNull PlatformViewCreationRequest request);

/**
* The Flutter application would like to display a new Android {@code View}.
* The Flutter application would like to display a new Android {@code View}, i.e., platform
* view.
*
* <p>{@code View} is added to a {@code VirtualDisplay}. The framework uses id returned by this
* method to lookup the texture in the engine.
* <p>The Android View is added to the view hierarchy. This view is rendered in the Flutter
* framework by a TextureLayer.
*
* @param request The metadata sent from the framework.
* @return The texture ID.
*/
long createVirtualDisplayForPlatformView(@NonNull PlatformViewCreationRequest request);
long createForTextureLayer(@NonNull PlatformViewCreationRequest request);

/** The Flutter application would like to dispose of an existing Android {@code View}. */
void dispose(int viewId);

/**
* The Flutter application would like to dispose of an existing Android {@code View} rendered in
* a virtual display.
* The Flutter application would like to resize an existing Android {@code View}.
*
* @param request The request to resize the platform view.
* @return The buffer size where the platform view pixels are written to.
*/
void disposeVirtualDisplayForPlatformView(int viewId);
PlatformViewBufferSize resize(@NonNull PlatformViewResizeRequest request);

/**
* The Flutter application would like to resize an existing Android {@code View}, i.e., platform
* view.
* The Flutter application would like to change the offset of an existing Android {@code View}.
*/
void resizePlatformView(
@NonNull PlatformViewResizeRequest request, @NonNull Runnable onComplete);
void offset(int viewId, double top, double left);

/**
* The user touched a platform view within Flutter.
Expand Down Expand Up @@ -321,6 +348,12 @@ public static class PlatformViewCreationRequest {
/** The density independent height to display the platform view. */
public final double logicalHeight;

/** The density independent top position to display the platform view. */
public final double logicalTop;

/** The density independent left position to display the platform view. */
public final double logicalLeft;

/**
* The layout direction of the new platform view.
*
Expand All @@ -332,28 +365,28 @@ public static class PlatformViewCreationRequest {
/** Custom parameters that are unique to the desired platform view. */
@Nullable public final ByteBuffer params;

/** Creates a request to construct a platform view that uses a virtual display. */
/** Creates a request to construct a platform view. */
public PlatformViewCreationRequest(
int viewId,
@NonNull String viewType,
double logicalTop,
double logicalLeft,
double logicalWidth,
double logicalHeight,
int direction,
@Nullable ByteBuffer params) {
this.viewId = viewId;
this.viewType = viewType;
this.logicalTop = logicalTop;
this.logicalLeft = logicalLeft;
this.logicalWidth = logicalWidth;
this.logicalHeight = logicalHeight;
this.direction = direction;
this.params = params;
}
}

/**
* Request sent from Flutter to resize a platform view.
*
* <p>This only applies to platform views that use virtual displays.
*/
/** Request sent from Flutter to resize a platform view. */
public static class PlatformViewResizeRequest {
/** The ID of the platform view as seen by the Flutter side. */
public final int viewId;
Expand All @@ -371,6 +404,20 @@ public PlatformViewResizeRequest(int viewId, double newLogicalWidth, double newL
}
}

/** The platform view buffer size. */
public static class PlatformViewBufferSize {
/** The width of the screen buffer. */
public final int width;

/** The height of the screen buffer. */
public final int height;

public PlatformViewBufferSize(int width, int height) {
this.width = width;
this.height = height;
}
}

/** The state of a touch event in Flutter within a platform view. */
public static class PlatformViewTouch {
/** The ID of the platform view as seen by the Flutter side. */
Expand Down
Loading

0 comments on commit 9f54167

Please sign in to comment.