Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1913040

Browse files
Creates a new RenderMode for FlutterView (#19143)
1 parent a188779 commit 1913040

6 files changed

Lines changed: 179 additions & 6 deletions

File tree

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt
689689
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterEngineProvider.java
690690
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
691691
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java
692+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java
692693
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
693694
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
694695
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ android_java_sources = [
135135
"io/flutter/embedding/android/FlutterEngineProvider.java",
136136
"io/flutter/embedding/android/FlutterFragment.java",
137137
"io/flutter/embedding/android/FlutterFragmentActivity.java",
138+
"io/flutter/embedding/android/FlutterImageView.java",
138139
"io/flutter/embedding/android/FlutterSplashView.java",
139140
"io/flutter/embedding/android/FlutterSurfaceView.java",
140141
"io/flutter/embedding/android/FlutterTextureView.java",
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.embedding.android;
6+
7+
import android.annotation.SuppressLint;
8+
import android.annotation.TargetApi;
9+
import android.content.Context;
10+
import android.graphics.Bitmap;
11+
import android.graphics.Canvas;
12+
import android.graphics.ColorSpace;
13+
import android.hardware.HardwareBuffer;
14+
import android.media.Image;
15+
import android.media.Image.Plane;
16+
import android.media.ImageReader;
17+
import android.view.View;
18+
import androidx.annotation.NonNull;
19+
import androidx.annotation.Nullable;
20+
21+
/**
22+
* Paints a Flutter UI provided by an {@link android.media.ImageReader} onto a {@link
23+
* android.graphics.Canvas}.
24+
*
25+
* <p>A {@code FlutterImageView} is intended for situations where a developer needs to render a
26+
* Flutter UI, but also needs to render an interactive {@link
27+
* io.flutter.plugin.platform.PlatformView}.
28+
*
29+
* <p>This {@code View} takes an {@link android.media.ImageReader} that provides the Flutter UI in
30+
* an {@link android.media.Image} and renders it to the {@link android.graphics.Canvas} in {@code
31+
* onDraw}.
32+
*/
33+
@SuppressLint("ViewConstructor")
34+
@TargetApi(19)
35+
public class FlutterImageView extends View {
36+
private final ImageReader imageReader;
37+
@Nullable private Image nextImage;
38+
@Nullable private Image currentImage;
39+
40+
/**
41+
* Constructs a {@code FlutterImageView} with an {@link android.media.ImageReader} that provides
42+
* the Flutter UI.
43+
*/
44+
public FlutterImageView(@NonNull Context context, @NonNull ImageReader imageReader) {
45+
super(context, null);
46+
this.imageReader = imageReader;
47+
}
48+
49+
/** Acquires the next image to be drawn to the {@link android.graphics.Canvas}. */
50+
@TargetApi(19)
51+
public void acquireLatestImage() {
52+
nextImage = imageReader.acquireLatestImage();
53+
invalidate();
54+
}
55+
56+
@Override
57+
protected void onDraw(Canvas canvas) {
58+
super.onDraw(canvas);
59+
if (nextImage == null) {
60+
return;
61+
}
62+
63+
if (currentImage != null) {
64+
currentImage.close();
65+
}
66+
currentImage = nextImage;
67+
nextImage = null;
68+
69+
if (android.os.Build.VERSION.SDK_INT >= 29) {
70+
drawImageBuffer(canvas);
71+
return;
72+
}
73+
74+
drawImagePlane(canvas);
75+
}
76+
77+
@TargetApi(29)
78+
private void drawImageBuffer(@NonNull Canvas canvas) {
79+
final HardwareBuffer buffer = currentImage.getHardwareBuffer();
80+
81+
final Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
82+
canvas.drawBitmap(bitmap, 0, 0, null);
83+
}
84+
85+
private void drawImagePlane(@NonNull Canvas canvas) {
86+
if (currentImage == null) {
87+
return;
88+
}
89+
90+
final Plane[] imagePlanes = currentImage.getPlanes();
91+
if (imagePlanes.length != 1) {
92+
return;
93+
}
94+
95+
final Plane imagePlane = imagePlanes[0];
96+
final int desiredWidth = imagePlane.getRowStride() / imagePlane.getPixelStride();
97+
final int desiredHeight = currentImage.getHeight();
98+
99+
final Bitmap bitmap =
100+
android.graphics.Bitmap.createBitmap(
101+
desiredWidth, desiredHeight, android.graphics.Bitmap.Config.ARGB_8888);
102+
103+
bitmap.copyPixelsFromBuffer(imagePlane.getBuffer());
104+
canvas.drawBitmap(bitmap, 0, 0, null);
105+
}
106+
}

shell/platform/android/io/flutter/embedding/android/FlutterView.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC
7979
// Internal view hierarchy references.
8080
@Nullable private FlutterSurfaceView flutterSurfaceView;
8181
@Nullable private FlutterTextureView flutterTextureView;
82+
@Nullable private FlutterImageView flutterImageView;
8283
@Nullable private RenderSurface renderSurface;
8384
private final Set<FlutterUiDisplayListener> flutterUiDisplayListeners = new HashSet<>();
8485
private boolean isFlutterUiDisplayed;
@@ -155,7 +156,8 @@ public FlutterView(@NonNull Context context) {
155156

156157
/**
157158
* Deprecated - use {@link #FlutterView(Context, FlutterSurfaceView)} or {@link
158-
* #FlutterView(Context, FlutterTextureView)} instead.
159+
* #FlutterView(Context, FlutterTextureView)} or {@link #FlutterView(Context, FlutterImageView)}
160+
* instead.
159161
*/
160162
@Deprecated
161163
public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
@@ -164,9 +166,12 @@ public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
164166
if (renderMode == RenderMode.surface) {
165167
flutterSurfaceView = new FlutterSurfaceView(context);
166168
renderSurface = flutterSurfaceView;
167-
} else {
169+
} else if (renderMode == RenderMode.texture) {
168170
flutterTextureView = new FlutterTextureView(context);
169171
renderSurface = flutterTextureView;
172+
} else {
173+
throw new IllegalArgumentException(
174+
String.format("RenderMode not supported with this constructor: ", renderMode));
170175
}
171176

172177
init();
@@ -216,6 +221,18 @@ public FlutterView(@NonNull Context context, @NonNull FlutterTextureView flutter
216221
this(context, null, flutterTextureView);
217222
}
218223

224+
/**
225+
* Constructs a {@code FlutterView} programmatically, without any XML attributes, uses the given
226+
* {@link FlutterImageView} to render the Flutter UI.
227+
*
228+
* <p>{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be
229+
* compatible with {@link PlatformViewsController}.
230+
*/
231+
@TargetApi(19)
232+
public FlutterView(@NonNull Context context, @NonNull FlutterImageView flutterImageView) {
233+
this(context, null, flutterImageView);
234+
}
235+
219236
/**
220237
* Constructs a {@code FlutterView} in an XML-inflation-compliant manner.
221238
*
@@ -243,9 +260,12 @@ public FlutterView(
243260
flutterSurfaceView =
244261
new FlutterSurfaceView(context, transparencyMode == TransparencyMode.transparent);
245262
renderSurface = flutterSurfaceView;
246-
} else {
263+
} else if (renderMode == RenderMode.texture) {
247264
flutterTextureView = new FlutterTextureView(context);
248265
renderSurface = flutterTextureView;
266+
} else {
267+
throw new IllegalArgumentException(
268+
String.format("RenderMode not supported with this constructor: ", renderMode));
249269
}
250270

251271
init();
@@ -275,15 +295,30 @@ private FlutterView(
275295
init();
276296
}
277297

298+
@TargetApi(19)
299+
private FlutterView(
300+
@NonNull Context context,
301+
@Nullable AttributeSet attrs,
302+
@NonNull FlutterImageView flutterImageView) {
303+
super(context, attrs);
304+
305+
this.flutterImageView = flutterImageView;
306+
307+
init();
308+
}
309+
278310
private void init() {
279311
Log.v(TAG, "Initializing FlutterView");
280312

281313
if (flutterSurfaceView != null) {
282314
Log.v(TAG, "Internally using a FlutterSurfaceView.");
283315
addView(flutterSurfaceView);
284-
} else {
316+
} else if (flutterTextureView != null) {
285317
Log.v(TAG, "Internally using a FlutterTextureView.");
286318
addView(flutterTextureView);
319+
} else {
320+
Log.v(TAG, "Internally using a FlutterImageView.");
321+
addView(flutterImageView);
287322
}
288323

289324
// FlutterView needs to be focusable so that the InputMethodManager can interact with it.
@@ -1018,7 +1053,16 @@ public enum RenderMode {
10181053
* android.graphics.SurfaceTexture} are required, developers should strongly prefer the {@link
10191054
* RenderMode#surface} render mode.
10201055
*/
1021-
texture
1056+
texture,
1057+
/**
1058+
* {@code RenderMode}, which paints Paints a Flutter UI provided by an {@link
1059+
* android.media.ImageReader} onto a {@link android.graphics.Canvas}. This mode is not as
1060+
* performant as {@link RenderMode#surface}, but a {@code FlutterView} in this mode can handle
1061+
* full interactivity with a {@link io.flutter.plugin.platform.PlatformView}. Unless {@link
1062+
* io.flutter.plugin.platform.PlatformView}s are required developers should strongly prefer the
1063+
* {@link RenderMode#surface} render mode.
1064+
*/
1065+
image
10221066
}
10231067

10241068
/**

shell/platform/android/io/flutter/embedding/android/RenderMode.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,14 @@ public enum RenderMode {
2121
* Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are
2222
* required, developers should strongly prefer the {@link #surface} render mode.
2323
*/
24-
texture
24+
texture,
25+
/**
26+
* {@code RenderMode}, which paints Paints a Flutter UI provided by an {@link
27+
* android.media.ImageReader} onto a {@link android.graphics.Canvas}. This mode is not as
28+
* performant as {@link RenderMode#surface}, but a {@code FlutterView} in this mode can handle
29+
* full interactivity with a {@link io.flutter.plugin.platform.PlatformView}. Unless {@link
30+
* io.flutter.plugin.platform.PlatformView}s are required developers should strongly prefer the
31+
* {@link RenderMode#surface} render mode.
32+
*/
33+
image
2534
}

shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import android.content.Context;
1313
import android.content.res.Configuration;
1414
import android.content.res.Resources;
15+
import android.media.ImageReader;
1516
import android.view.View;
1617
import android.view.ViewGroup;
1718
import android.view.WindowInsets;
@@ -370,6 +371,17 @@ public void systemInsetHandlesFullscreenNavbarLeft() {
370371
assertEquals(100, viewportMetricsCaptor.getValue().paddingRight);
371372
}
372373

374+
@Test
375+
public void flutterImageView_acquiresImageAndInvalidates() {
376+
final ImageReader mockReader = mock(ImageReader.class);
377+
final FlutterImageView imageView =
378+
spy(new FlutterImageView(RuntimeEnvironment.application, mockReader));
379+
380+
imageView.acquireLatestImage();
381+
verify(mockReader, times(1)).acquireLatestImage();
382+
verify(imageView, times(1)).invalidate();
383+
}
384+
373385
/*
374386
* A custom shadow that reports fullscreen flag for system UI visibility
375387
*/

0 commit comments

Comments
 (0)