Flutter Engine
The Flutter Engine
PlatformViewWrapper.java
Go to the documentation of this file.
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
5package io.flutter.plugin.platform;
6
7import static io.flutter.Build.API_LEVELS;
8
9import android.annotation.SuppressLint;
10import android.annotation.TargetApi;
11import android.content.Context;
12import android.graphics.Canvas;
13import android.graphics.Color;
14import android.graphics.Matrix;
15import android.graphics.PorterDuff;
16import android.graphics.Rect;
17import android.view.MotionEvent;
18import android.view.Surface;
19import android.view.View;
20import android.view.ViewParent;
21import android.view.ViewTreeObserver;
22import android.view.accessibility.AccessibilityEvent;
23import android.widget.FrameLayout;
24import androidx.annotation.NonNull;
25import androidx.annotation.Nullable;
26import androidx.annotation.VisibleForTesting;
27import io.flutter.Log;
28import io.flutter.embedding.android.AndroidTouchProcessor;
29import io.flutter.embedding.engine.renderer.FlutterRenderer;
30import io.flutter.util.ViewUtils;
31
32/**
33 * Wraps a platform view to intercept gestures and project this view onto a {@link
34 * PlatformViewRenderTarget}.
35 *
36 * <p>An Android platform view is composed by the engine using a {@code TextureLayer}. The view is
37 * embeded to the Android view hierarchy like a normal view, but it's projected onto a {@link
38 * PlatformViewRenderTarget}, so it can be efficiently composed by the engine.
39 *
40 * <p>Since the view is in the Android view hierarchy, keyboard and accessibility interactions
41 * behave normally.
42 */
43@TargetApi(API_LEVELS.API_23)
44public class PlatformViewWrapper extends FrameLayout {
45 private static final String TAG = "PlatformViewWrapper";
46
47 private int prevLeft;
48 private int prevTop;
49 private int left;
50 private int top;
51 private AndroidTouchProcessor touchProcessor;
52 private PlatformViewRenderTarget renderTarget;
53
54 private ViewTreeObserver.OnGlobalFocusChangeListener activeFocusListener;
55
56 public PlatformViewWrapper(@NonNull Context context) {
57 super(context);
58 setWillNotDraw(false);
59 }
60
62 @NonNull Context context, @NonNull PlatformViewRenderTarget renderTarget) {
63 this(context);
64 this.renderTarget = renderTarget;
65
66 Surface surface = renderTarget.getSurface();
68 final Canvas canvas = surface.lockHardwareCanvas();
69 try {
70 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
71 } finally {
72 surface.unlockCanvasAndPost(canvas);
73 }
74 }
75 }
76
77 /**
78 * Sets the touch processor that allows to intercept gestures.
79 *
80 * @param newTouchProcessor The touch processor.
81 */
82 public void setTouchProcessor(@Nullable AndroidTouchProcessor newTouchProcessor) {
83 touchProcessor = newTouchProcessor;
84 }
85
86 /**
87 * Sets the layout parameters for this view.
88 *
89 * @param params The new parameters.
90 */
91 public void setLayoutParams(@NonNull FrameLayout.LayoutParams params) {
92 super.setLayoutParams(params);
93
94 left = params.leftMargin;
95 top = params.topMargin;
96 }
97
98 public void resizeRenderTarget(int width, int height) {
99 if (renderTarget != null) {
100 renderTarget.resize(width, height);
101 }
102 }
103
104 public int getRenderTargetWidth() {
105 if (renderTarget != null) {
106 return renderTarget.getWidth();
107 }
108 return 0;
109 }
110
112 if (renderTarget != null) {
113 return renderTarget.getHeight();
114 }
115 return 0;
116 }
117
118 /** Releases resources. */
119 public void release() {
120 if (renderTarget != null) {
121 renderTarget.release();
122 renderTarget = null;
123 }
124 }
125
126 @Override
127 public boolean onInterceptTouchEvent(@NonNull MotionEvent event) {
128 return true;
129 }
130
131 @Override
132 public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
133 final View embeddedView = getChildAt(0);
134 if (embeddedView != null
135 && embeddedView.getImportantForAccessibility()
136 == View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
137 return false;
138 }
139 // Forward the request only if the embedded view is in the Flutter accessibility tree.
140 // The embedded view may be ignored when the framework doesn't populate a SemanticNode
141 // for the current platform view.
142 // See AccessibilityBridge for more.
143 return super.requestSendAccessibilityEvent(child, event);
144 }
145
146 /** Used on Android O+, {@link invalidateChildInParent} used for previous versions. */
147 @SuppressLint("NewApi")
148 @Override
149 public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
150 super.onDescendantInvalidated(child, target);
151 invalidate();
152 }
153
154 @Override
155 public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
156 invalidate();
157 return super.invalidateChildInParent(location, dirty);
158 }
159
160 @Override
161 @SuppressLint("NewApi")
162 public void draw(Canvas canvas) {
163 if (renderTarget == null) {
164 super.draw(canvas);
165 Log.e(TAG, "Platform view cannot be composed without a RenderTarget.");
166 return;
167 }
168 final Surface targetSurface = renderTarget.getSurface();
169 final Canvas targetCanvas = targetSurface.lockHardwareCanvas();
170 if (targetCanvas == null) {
171 // Cannot render right now.
172 invalidate();
173 return;
174 }
175
176 try {
177 // Fill the render target with transparent pixels. This is needed for platform views that
178 // expect a transparent background.
179 targetCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
180 // Override the canvas that this subtree of views will use to draw.
181 super.draw(targetCanvas);
182 } finally {
183 renderTarget.scheduleFrame();
184 targetSurface.unlockCanvasAndPost(targetCanvas);
185 }
186 }
187
188 @Override
189 @SuppressLint("ClickableViewAccessibility")
190 public boolean onTouchEvent(@NonNull MotionEvent event) {
191 if (touchProcessor == null) {
192 return super.onTouchEvent(event);
193 }
194 final Matrix screenMatrix = new Matrix();
195 switch (event.getAction()) {
196 case MotionEvent.ACTION_DOWN:
197 prevLeft = left;
198 prevTop = top;
199 screenMatrix.postTranslate(left, top);
200 break;
201 case MotionEvent.ACTION_MOVE:
202 // While the view is dragged, use the left and top positions as
203 // they were at the moment the touch event fired.
204 screenMatrix.postTranslate(prevLeft, prevTop);
205 prevLeft = left;
206 prevTop = top;
207 break;
208 case MotionEvent.ACTION_UP:
209 default:
210 screenMatrix.postTranslate(left, top);
211 break;
212 }
213 return touchProcessor.onTouchEvent(event, screenMatrix);
214 }
215
216 @VisibleForTesting
217 public ViewTreeObserver.OnGlobalFocusChangeListener getActiveFocusListener() {
218 return this.activeFocusListener;
219 }
220
221 public void setOnDescendantFocusChangeListener(@NonNull OnFocusChangeListener userFocusListener) {
222 unsetOnDescendantFocusChangeListener();
223 final ViewTreeObserver observer = getViewTreeObserver();
224 if (observer.isAlive() && activeFocusListener == null) {
225 activeFocusListener =
226 new ViewTreeObserver.OnGlobalFocusChangeListener() {
227 @Override
228 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
229 userFocusListener.onFocusChange(
231 }
232 };
233 observer.addOnGlobalFocusChangeListener(activeFocusListener);
234 }
235 }
236
238 final ViewTreeObserver observer = getViewTreeObserver();
239 if (observer.isAlive() && activeFocusListener != null) {
240 final ViewTreeObserver.OnGlobalFocusChangeListener currFocusListener = activeFocusListener;
241 activeFocusListener = null;
242 observer.removeOnGlobalFocusChangeListener(currFocusListener);
243 }
244 }
245}
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
static void e(@NonNull String tag, @NonNull String message)
Definition: Log.java:84
void onDescendantInvalidated(@NonNull View child, @NonNull View target)
void setTouchProcessor(@Nullable AndroidTouchProcessor newTouchProcessor)
void setOnDescendantFocusChangeListener(@NonNull OnFocusChangeListener userFocusListener)
boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event)
ViewParent invalidateChildInParent(int[] location, Rect dirty)
PlatformViewWrapper( @NonNull Context context, @NonNull PlatformViewRenderTarget renderTarget)
boolean onInterceptTouchEvent(@NonNull MotionEvent event)
ViewTreeObserver.OnGlobalFocusChangeListener getActiveFocusListener()
void setLayoutParams(@NonNull FrameLayout.LayoutParams params)
static boolean childHasFocus(@Nullable View root)
Definition: ViewUtils.java:67
const EmbeddedViewParams * params
VkSurfaceKHR surface
Definition: main.cc:49
FlKeyEvent * event
uint32_t * target
SK_API sk_sp< SkShader > Color(SkColor)
TRect< Scalar > Rect
Definition: rect.h:769
SK_API sk_sp< PrecompileColorFilter > Matrix()
#define TAG()
int32_t height
int32_t width