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.view; 6 7 import android.app.Activity; 8 import android.content.Context; 9 import android.support.annotation.NonNull; 10 import android.support.annotation.UiThread; 11 import android.util.Log; 12 import io.flutter.app.FlutterPluginRegistry; 13 import io.flutter.embedding.engine.FlutterJNI; 14 import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; 15 import io.flutter.embedding.engine.dart.DartExecutor; 16 import io.flutter.embedding.engine.renderer.FlutterRenderer; 17 import io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface; 18 import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; 19 import io.flutter.plugin.common.*; 20 import java.nio.ByteBuffer; 21 import java.util.concurrent.atomic.AtomicBoolean; 22 import java.util.HashMap; 23 import java.util.Map; 24 25 import io.flutter.embedding.engine.dart.PlatformMessageHandler; 26 27 public class FlutterNativeView implements BinaryMessenger { 28 private static final String TAG = "FlutterNativeView"; 29 30 private final FlutterPluginRegistry mPluginRegistry; 31 private final DartExecutor dartExecutor; 32 private FlutterView mFlutterView; 33 private final FlutterJNI mFlutterJNI; 34 private final Context mContext; 35 private boolean applicationIsRunning; 36 FlutterNativeView(@onNull Context context)37 public FlutterNativeView(@NonNull Context context) { 38 this(context, false); 39 } 40 FlutterNativeView(@onNull Context context, boolean isBackgroundView)41 public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) { 42 mContext = context; 43 mPluginRegistry = new FlutterPluginRegistry(this, context); 44 mFlutterJNI = new FlutterJNI(); 45 mFlutterJNI.setRenderSurface(new RenderSurfaceImpl()); 46 this.dartExecutor = new DartExecutor(mFlutterJNI, context.getAssets()); 47 mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl()); 48 attach(this, isBackgroundView); 49 assertAttached(); 50 } 51 detachFromFlutterView()52 public void detachFromFlutterView() { 53 mPluginRegistry.detach(); 54 mFlutterView = null; 55 } 56 destroy()57 public void destroy() { 58 mPluginRegistry.destroy(); 59 dartExecutor.onDetachedFromJNI(); 60 mFlutterView = null; 61 mFlutterJNI.detachFromNativeAndReleaseResources(); 62 applicationIsRunning = false; 63 } 64 65 @NonNull getDartExecutor()66 public DartExecutor getDartExecutor() { 67 return dartExecutor; 68 } 69 70 @NonNull getPluginRegistry()71 public FlutterPluginRegistry getPluginRegistry() { 72 return mPluginRegistry; 73 } 74 attachViewAndActivity(FlutterView flutterView, Activity activity)75 public void attachViewAndActivity(FlutterView flutterView, Activity activity) { 76 mFlutterView = flutterView; 77 mPluginRegistry.attach(flutterView, activity); 78 } 79 isAttached()80 public boolean isAttached() { 81 return mFlutterJNI.isAttached(); 82 } 83 assertAttached()84 public void assertAttached() { 85 if (!isAttached()) throw new AssertionError("Platform view is not attached"); 86 } 87 runFromBundle(FlutterRunArguments args)88 public void runFromBundle(FlutterRunArguments args) { 89 if (args.entrypoint == null) { 90 throw new AssertionError("An entrypoint must be specified"); 91 } 92 assertAttached(); 93 if (applicationIsRunning) 94 throw new AssertionError( 95 "This Flutter engine instance is already running an application"); 96 mFlutterJNI.runBundleAndSnapshotFromLibrary( 97 args.bundlePath, 98 args.entrypoint, 99 args.libraryPath, 100 mContext.getResources().getAssets() 101 ); 102 103 applicationIsRunning = true; 104 } 105 isApplicationRunning()106 public boolean isApplicationRunning() { 107 return applicationIsRunning; 108 } 109 getObservatoryUri()110 public static String getObservatoryUri() { 111 return FlutterJNI.getObservatoryUri(); 112 } 113 114 @Override 115 @UiThread send(String channel, ByteBuffer message)116 public void send(String channel, ByteBuffer message) { 117 dartExecutor.send(channel, message); 118 } 119 120 @Override 121 @UiThread send(String channel, ByteBuffer message, BinaryReply callback)122 public void send(String channel, ByteBuffer message, BinaryReply callback) { 123 if (!isAttached()) { 124 Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); 125 return; 126 } 127 128 dartExecutor.send(channel, message, callback); 129 } 130 131 @Override 132 @UiThread setMessageHandler(String channel, BinaryMessageHandler handler)133 public void setMessageHandler(String channel, BinaryMessageHandler handler) { 134 dartExecutor.setMessageHandler(channel, handler); 135 } 136 getFlutterJNI()137 /*package*/ FlutterJNI getFlutterJNI() { 138 return mFlutterJNI; 139 } 140 attach(FlutterNativeView view, boolean isBackgroundView)141 private void attach(FlutterNativeView view, boolean isBackgroundView) { 142 mFlutterJNI.attachToNative(isBackgroundView); 143 dartExecutor.onAttachedToJNI(); 144 } 145 146 private final class RenderSurfaceImpl implements RenderSurface { 147 @Override attachToRenderer(@onNull FlutterRenderer renderer)148 public void attachToRenderer(@NonNull FlutterRenderer renderer) { 149 // Not relevant for v1 embedding. 150 } 151 152 @Override detachFromRenderer()153 public void detachFromRenderer() { 154 // Not relevant for v1 embedding. 155 } 156 157 // Called by native to update the semantics/accessibility tree. updateSemantics(ByteBuffer buffer, String[] strings)158 public void updateSemantics(ByteBuffer buffer, String[] strings) { 159 if (mFlutterView == null) { 160 return; 161 } 162 mFlutterView.updateSemantics(buffer, strings); 163 } 164 165 // Called by native to update the custom accessibility actions. updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings)166 public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { 167 if (mFlutterView == null) { 168 return; 169 } 170 mFlutterView.updateCustomAccessibilityActions(buffer, strings); 171 } 172 173 // Called by native to notify first Flutter frame rendered. onFirstFrameRendered()174 public void onFirstFrameRendered() { 175 if (mFlutterView == null) { 176 return; 177 } 178 mFlutterView.onFirstFrame(); 179 } 180 181 @Override addOnFirstFrameRenderedListener(@onNull OnFirstFrameRenderedListener listener)182 public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {} 183 184 @Override removeOnFirstFrameRenderedListener(@onNull OnFirstFrameRenderedListener listener)185 public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {} 186 } 187 188 private final class EngineLifecycleListenerImpl implements EngineLifecycleListener { 189 // Called by native to notify when the engine is restarted (cold reload). 190 @SuppressWarnings("unused") onPreEngineRestart()191 public void onPreEngineRestart() { 192 if (mFlutterView != null) { 193 mFlutterView.resetAccessibilityTree(); 194 } 195 if (mPluginRegistry == null) { 196 return; 197 } 198 mPluginRegistry.onPreEngineRestart(); 199 } 200 } 201 } 202