Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterEngineProvider.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ android_java_sources = [
"io/flutter/embedding/android/FlutterEngineProvider.java",
"io/flutter/embedding/android/FlutterFragment.java",
"io/flutter/embedding/android/FlutterFragmentActivity.java",
"io/flutter/embedding/android/FlutterImageView.java",
"io/flutter/embedding/android/FlutterSplashView.java",
"io/flutter/embedding/android/FlutterSurfaceView.java",
"io/flutter/embedding/android/FlutterTextureView.java",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.embedding.android;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorSpace;
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
* Paints a Flutter UI provided by an {@link android.media.ImageReader} onto a {@link
* android.graphics.Canvas}.
*
* <p>A {@code FlutterImageView} is intended for situations where a developer needs to render a
* Flutter UI, but also needs to render an interactive {@link
* io.flutter.plugin.platform.PlatformView}.
*
* <p>This {@code View} takes an {@link android.media.ImageReader} that provides the Flutter UI in
* an {@link android.media.Image} and renders it to the {@link android.graphics.Canvas} in {@code
* onDraw}.
*/
@SuppressLint("ViewConstructor")
@TargetApi(19)
public class FlutterImageView extends View {
private final ImageReader imageReader;
@Nullable private Image nextImage;
@Nullable private Image currentImage;

/**
* Constructs a {@code FlutterImageView} with an {@link android.media.ImageReader} that provides
* the Flutter UI.
*/
public FlutterImageView(@NonNull Context context, @NonNull ImageReader imageReader) {
super(context, null);
this.imageReader = imageReader;
}

/** Acquires the next image to be drawn to the {@link android.graphics.Canvas}. */
@TargetApi(19)
public void acquireLatestImage() {
nextImage = imageReader.acquireLatestImage();
invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (nextImage == null) {
return;
}

if (currentImage != null) {
currentImage.close();
}
currentImage = nextImage;
nextImage = null;

if (android.os.Build.VERSION.SDK_INT >= 29) {
drawImageBuffer(canvas);
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else: throw unsupported Android version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since, I moved the @RequiresApi(api = Build.VERSION_CODES.KITKAT) to the top of the class due to your other comment. I don't think it needs that anymore, right?


drawImagePlane(canvas);
}

@TargetApi(29)
private void drawImageBuffer(@NonNull Canvas canvas) {
final HardwareBuffer buffer = currentImage.getHardwareBuffer();

final Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
canvas.drawBitmap(bitmap, 0, 0, null);
}

private void drawImagePlane(@NonNull Canvas canvas) {
if (currentImage == null) {
return;
}

final Plane[] imagePlanes = currentImage.getPlanes();
if (imagePlanes.length != 1) {
return;
}

final Plane imagePlane = imagePlanes[0];
final int desiredWidth = imagePlane.getRowStride() / imagePlane.getPixelStride();
final int desiredHeight = currentImage.getHeight();

final Bitmap bitmap =
android.graphics.Bitmap.createBitmap(
desiredWidth, desiredHeight, android.graphics.Bitmap.Config.ARGB_8888);

bitmap.copyPixelsFromBuffer(imagePlane.getBuffer());
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC
// Internal view hierarchy references.
@Nullable private FlutterSurfaceView flutterSurfaceView;
@Nullable private FlutterTextureView flutterTextureView;
@Nullable private FlutterImageView flutterImageView;
@Nullable private RenderSurface renderSurface;
private final Set<FlutterUiDisplayListener> flutterUiDisplayListeners = new HashSet<>();
private boolean isFlutterUiDisplayed;
Expand Down Expand Up @@ -155,7 +156,8 @@ public FlutterView(@NonNull Context context) {

/**
* Deprecated - use {@link #FlutterView(Context, FlutterSurfaceView)} or {@link
* #FlutterView(Context, FlutterTextureView)} instead.
* #FlutterView(Context, FlutterTextureView)} or {@link #FlutterView(Context, FlutterImageView)}
* instead.
*/
@Deprecated
public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
Expand All @@ -164,9 +166,12 @@ public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
if (renderMode == RenderMode.surface) {
flutterSurfaceView = new FlutterSurfaceView(context);
renderSurface = flutterSurfaceView;
} else {
} else if (renderMode == RenderMode.texture) {
flutterTextureView = new FlutterTextureView(context);
renderSurface = flutterTextureView;
} else {
throw new IllegalArgumentException(
String.format("RenderMode not supported with this constructor: ", renderMode));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the missing format arguments break Google test (here and below line 268). If we have not yet, can we fix this?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

init();
Expand Down Expand Up @@ -216,6 +221,18 @@ public FlutterView(@NonNull Context context, @NonNull FlutterTextureView flutter
this(context, null, flutterTextureView);
}

/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes, uses the given
* {@link FlutterImageView} to render the Flutter UI.
*
* <p>{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be
* compatible with {@link PlatformViewsController}.
*/
@TargetApi(19)
public FlutterView(@NonNull Context context, @NonNull FlutterImageView flutterImageView) {
this(context, null, flutterImageView);
}

/**
* Constructs a {@code FlutterView} in an XML-inflation-compliant manner.
*
Expand Down Expand Up @@ -243,9 +260,12 @@ public FlutterView(
flutterSurfaceView =
new FlutterSurfaceView(context, transparencyMode == TransparencyMode.transparent);
renderSurface = flutterSurfaceView;
} else {
} else if (renderMode == RenderMode.texture) {
flutterTextureView = new FlutterTextureView(context);
renderSurface = flutterTextureView;
} else {
throw new IllegalArgumentException(
String.format("RenderMode not supported with this constructor: ", renderMode));
}

init();
Expand Down Expand Up @@ -275,15 +295,30 @@ private FlutterView(
init();
}

@TargetApi(19)
private FlutterView(
@NonNull Context context,
@Nullable AttributeSet attrs,
@NonNull FlutterImageView flutterImageView) {
super(context, attrs);

this.flutterImageView = flutterImageView;

init();
}

private void init() {
Log.v(TAG, "Initializing FlutterView");

if (flutterSurfaceView != null) {
Log.v(TAG, "Internally using a FlutterSurfaceView.");
addView(flutterSurfaceView);
} else {
} else if (flutterTextureView != null) {
Log.v(TAG, "Internally using a FlutterTextureView.");
addView(flutterTextureView);
} else {
Log.v(TAG, "Internally using a FlutterImageView.");
addView(flutterImageView);
}

// FlutterView needs to be focusable so that the InputMethodManager can interact with it.
Expand Down Expand Up @@ -1018,7 +1053,16 @@ public enum RenderMode {
* android.graphics.SurfaceTexture} are required, developers should strongly prefer the {@link
* RenderMode#surface} render mode.
*/
texture
texture,
/**
* {@code RenderMode}, which paints Paints a Flutter UI provided by an {@link
* android.media.ImageReader} onto a {@link android.graphics.Canvas}. This mode is not as
* performant as {@link RenderMode#surface}, but a {@code FlutterView} in this mode can handle
* full interactivity with a {@link io.flutter.plugin.platform.PlatformView}. Unless {@link
* io.flutter.plugin.platform.PlatformView}s are required developers should strongly prefer the
* {@link RenderMode#surface} render mode.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is awesome!

*/
image
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,14 @@ public enum RenderMode {
* Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are
* required, developers should strongly prefer the {@link #surface} render mode.
*/
texture
texture,
/**
* {@code RenderMode}, which paints Paints a Flutter UI provided by an {@link
* android.media.ImageReader} onto a {@link android.graphics.Canvas}. This mode is not as
* performant as {@link RenderMode#surface}, but a {@code FlutterView} in this mode can handle
* full interactivity with a {@link io.flutter.plugin.platform.PlatformView}. Unless {@link
* io.flutter.plugin.platform.PlatformView}s are required developers should strongly prefer the
* {@link RenderMode#surface} render mode.
*/
image
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.media.ImageReader;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
Expand Down Expand Up @@ -370,6 +371,17 @@ public void systemInsetHandlesFullscreenNavbarLeft() {
assertEquals(100, viewportMetricsCaptor.getValue().paddingRight);
}

@Test
public void flutterImageView_acquiresImageAndInvalidates() {
final ImageReader mockReader = mock(ImageReader.class);
final FlutterImageView imageView =
spy(new FlutterImageView(RuntimeEnvironment.application, mockReader));

imageView.acquireLatestImage();
verify(mockReader, times(1)).acquireLatestImage();
verify(imageView, times(1)).invalidate();
}

/*
* A custom shadow that reports fullscreen flag for system UI visibility
*/
Expand Down