Skip to content

Commit

Permalink
Android Embedding PR15: Add Viewport Metrics to FlutterView (#8029)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-carroll authored Mar 5, 2019
1 parent 36ca574 commit fb3e35d
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public void surfaceCreated(SurfaceHolder holder) {

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "SurfaceHolder.Callback.surfaceChanged()");
if (isAttachedToFlutterRenderer) {
changeSurfaceSize(width, height);
}
Expand Down Expand Up @@ -97,13 +98,17 @@ private void init() {
* Invoked by the owner of this {@code FlutterSurfaceView} when it wants to begin rendering
* a Flutter UI to this {@code FlutterSurfaceView}.
*
* If an Android {@link android.view.Surface} is available, this method will begin rendering
* {@link FlutterRenderer}'s Flutter UI to this {@code FlutterSurfaceView}.
* If an Android {@link android.view.Surface} is available, this method will give that
* {@link android.view.Surface} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterSurfaceView}.
*
* If no Android {@link android.view.Surface} is available yet, this {@code FlutterSurfaceView}
* will wait until a {@link android.view.Surface} becomes available and then begin rendering.
* will wait until a {@link android.view.Surface} becomes available and then give that
* {@link android.view.Surface} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterSurfaceView}.
*/
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
Log.d(TAG, "attachToRenderer");
if (this.flutterRenderer != null) {
this.flutterRenderer.detachFromRenderSurface();
}
Expand All @@ -114,6 +119,7 @@ public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
// If we're already attached to an Android window then we're now attached to both a renderer
// and the Android window. We can begin rendering now.
if (isSurfaceAvailableForRendering) {
Log.d(TAG, "Surface is available for rendering. Connecting.");
connectSurfaceToRenderer();
}
}
Expand Down Expand Up @@ -179,5 +185,6 @@ public void updateSemantics(ByteBuffer buffer, String[] strings) {
@Override
public void onFirstFrameRendered() {
// TODO(mattcarroll): decide where this method should live and what it needs to do.
Log.d(TAG, "onFirstFrameRendered()");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,14 @@ private void init() {
* Invoked by the owner of this {@code FlutterTextureView} when it wants to begin rendering
* a Flutter UI to this {@code FlutterTextureView}.
*
* If an Android {@link SurfaceTexture} is available, this method will begin rendering
* {@link FlutterRenderer}'s Flutter UI to this {@code FlutterTextureView}.
* If an Android {@link SurfaceTexture} is available, this method will give that
* {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterTextureView}.
*
* If no Android {@link SurfaceTexture} is available yet, this {@code FlutterSurfaceView}
* will wait until a {@link SurfaceTexture} becomes available and then begin rendering.
* If no Android {@link SurfaceTexture} is available yet, this {@code FlutterTextureView}
* will wait until a {@link SurfaceTexture} becomes available and then give that
* {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering
* Flutter's UI to this {@code FlutterTextureView}.
*/
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
if (this.flutterRenderer != null) {
Expand Down Expand Up @@ -193,5 +196,6 @@ public void updateSemantics(ByteBuffer buffer, String[] strings) {
@Override
public void onFirstFrameRendered() {
// TODO(mattcarroll): decide where this method should live and what it needs to do.
Log.d(TAG, "onFirstFrameRendered()");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;

import java.util.ArrayList;
Expand All @@ -30,6 +29,7 @@
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.view.VsyncWaiter;

/**
* Displays a Flutter UI on an Android device.
Expand Down Expand Up @@ -77,6 +77,9 @@ public class FlutterView extends FrameLayout {
@Nullable
private AndroidKeyProcessor androidKeyProcessor;

// Directly implemented View behavior that communicates with Flutter.
private final FlutterRenderer.ViewportMetrics viewportMetrics = new FlutterRenderer.ViewportMetrics();

/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes.
*
Expand Down Expand Up @@ -158,8 +161,10 @@ protected void onConfigurationChanged(Configuration newConfig) {
*/
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
// TODO(mattcarroll): hookup to viewport metrics.
super.onSizeChanged(width, height, oldWidth, oldHeight);
viewportMetrics.width = width;
viewportMetrics.height = height;
sendViewportMetricsToFlutter();
}

/**
Expand All @@ -174,8 +179,22 @@ protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
*/
@Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
// TODO(mattcarroll): hookup to Flutter metrics.
return insets;
WindowInsets newInsets = super.onApplyWindowInsets(insets);

// Status bar (top) and left/right system insets should partially obscure the content (padding).
viewportMetrics.paddingTop = insets.getSystemWindowInsetTop();
viewportMetrics.paddingRight = insets.getSystemWindowInsetRight();
viewportMetrics.paddingBottom = 0;
viewportMetrics.paddingLeft = insets.getSystemWindowInsetLeft();

// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
viewportMetrics.viewInsetTop = 0;
viewportMetrics.viewInsetRight = 0;
viewportMetrics.viewInsetBottom = insets.getSystemWindowInsetBottom();
viewportMetrics.viewInsetLeft = 0;
sendViewportMetricsToFlutter();

return newInsets;
}

/**
Expand All @@ -188,8 +207,23 @@ public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
@Override
@SuppressWarnings("deprecation")
protected boolean fitSystemWindows(Rect insets) {
// TODO(mattcarroll): hookup to Flutter metrics.
return super.fitSystemWindows(insets);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
// Status bar, left/right system insets partially obscure content (padding).
viewportMetrics.paddingTop = insets.top;
viewportMetrics.paddingRight = insets.right;
viewportMetrics.paddingBottom = 0;
viewportMetrics.paddingLeft = insets.left;

// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
viewportMetrics.viewInsetTop = 0;
viewportMetrics.viewInsetRight = 0;
viewportMetrics.viewInsetBottom = insets.bottom;
viewportMetrics.viewInsetLeft = 0;
sendViewportMetricsToFlutter();
return true;
} else {
return super.fitSystemWindows(insets);
}
}
//------- End: Process View configuration that Flutter cares about. --------

Expand Down Expand Up @@ -312,13 +346,16 @@ public boolean onHoverEvent(MotionEvent event) {
* {@link FlutterEngine}.
*/
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
Log.d(TAG, "attachToFlutterEngine()");
if (isAttachedToFlutterEngine()) {
if (flutterEngine == this.flutterEngine) {
// We are already attached to this FlutterEngine
Log.d(TAG, "Already attached to this engine. Doing nothing.");
return;
}

// Detach from a previous FlutterEngine so we can attach to this new one.
Log.d(TAG, "Currently attached to a different engine. Detaching.");
detachFromFlutterEngine();
}

Expand Down Expand Up @@ -346,6 +383,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
// Push View and Context related information from Android to Flutter.
sendUserSettingsToFlutter();
sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter();
}

/**
Expand All @@ -359,7 +397,9 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
* {@link FlutterEngine}.
*/
public void detachFromFlutterEngine() {
Log.d(TAG, "detachFromFlutterEngine()");
if (!isAttachedToFlutterEngine()) {
Log.d(TAG, "Not attached to an engine. Doing nothing.");
return;
}
Log.d(TAG, "Detaching from Flutter Engine");
Expand Down Expand Up @@ -422,6 +462,18 @@ private void sendUserSettingsToFlutter() {
.send();
}

// TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI
private void sendViewportMetricsToFlutter() {
Log.d(TAG, "sendViewportMetricsToFlutter()");
if (!isAttachedToFlutterEngine()) {
Log.w(TAG, "Tried to send viewport metrics from Android to Flutter but this FlutterView was not attached to a FlutterEngine.");
return;
}

viewportMetrics.devicePixelRatio = getResources().getDisplayMetrics().density;
flutterEngine.getRenderer().setViewportMetrics(viewportMetrics);
}

/**
* Render modes for a {@link FlutterView}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out
* certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy.
*
* {@link FlutterView} is an implementation of a {@link RenderSurface}.
* {@link io.flutter.embedding.engine.android.FlutterView} is an implementation of a {@link RenderSurface}.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class FlutterRenderer implements TextureRegistry {
Expand All @@ -49,14 +49,16 @@ public void attachToRenderSurface(@NonNull RenderSurface renderSurface) {
}

this.renderSurface = renderSurface;
this.renderSurface.attachToRenderer(this);
this.flutterJNI.setRenderSurface(renderSurface);
}

public void detachFromRenderSurface() {
// TODO(mattcarroll): determine desired behavior if we're asked to detach without first being attached
if (this.renderSurface != null) {
surfaceDestroyed();
this.renderSurface.detachFromRenderer();
this.renderSurface = null;
surfaceDestroyed();
this.flutterJNI.setRenderSurface(null);
}
}
Expand Down Expand Up @@ -157,29 +159,19 @@ public void surfaceDestroyed() {
}

// TODO(mattcarroll): describe the native behavior that this invokes
public void setViewportMetrics(float devicePixelRatio,
int physicalWidth,
int physicalHeight,
int physicalPaddingTop,
int physicalPaddingRight,
int physicalPaddingBottom,
int physicalPaddingLeft,
int physicalViewInsetTop,
int physicalViewInsetRight,
int physicalViewInsetBottom,
int physicalViewInsetLeft) {
public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) {
flutterJNI.setViewportMetrics(
devicePixelRatio,
physicalWidth,
physicalHeight,
physicalPaddingTop,
physicalPaddingRight,
physicalPaddingBottom,
physicalPaddingLeft,
physicalViewInsetTop,
physicalViewInsetRight,
physicalViewInsetBottom,
physicalViewInsetLeft
viewportMetrics.devicePixelRatio,
viewportMetrics.width,
viewportMetrics.height,
viewportMetrics.paddingTop,
viewportMetrics.paddingRight,
viewportMetrics.paddingBottom,
viewportMetrics.paddingLeft,
viewportMetrics.viewInsetTop,
viewportMetrics.viewInsetRight,
viewportMetrics.viewInsetBottom,
viewportMetrics.viewInsetLeft
);
}

Expand Down Expand Up @@ -281,4 +273,24 @@ public interface RenderSurface {
*/
void onFirstFrameRendered();
}

/**
* Mutable data structure that holds all viewport metrics properties that Flutter cares about.
*
* All distance measurements, e.g., width, height, padding, viewInsets, are measured in device
* pixels, not logical pixels.
*/
public static final class ViewportMetrics {
public float devicePixelRatio = 1.0f;
public int width = 0;
public int height = 0;
public int paddingTop = 0;
public int paddingRight = 0;
public int paddingBottom = 0;
public int paddingLeft = 0;
public int viewInsetTop = 0;
public int viewInsetRight = 0;
public int viewInsetBottom = 0;
public int viewInsetLeft = 0;
}
}

0 comments on commit fb3e35d

Please sign in to comment.