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

Commit 4c4c0f8

Browse files
Add plugin shim to facilitate old plugins in new embedding (#33478). (#9120)
1 parent e8c2b17 commit 4c4c0f8

File tree

6 files changed

+323
-3
lines changed

6 files changed

+323
-3
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin
533533
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java
534534
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java
535535
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java
536+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java
537+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
536538
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
537539
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java
538540
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java

shell/platform/android/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ action("flutter_shell_java") {
149149
"io/flutter/embedding/engine/plugins/service/ServiceAware.java",
150150
"io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java",
151151
"io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java",
152+
"io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java",
153+
"io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java",
152154
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
153155
"io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java",
154156
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public Intent build(@NonNull Context context) {
178178
}
179179

180180
@Override
181-
public void onCreate(Bundle savedInstanceState) {
181+
protected void onCreate(Bundle savedInstanceState) {
182182
Log.d(TAG, "onCreate()");
183183
super.onCreate(savedInstanceState);
184184
configureWindowForTransparency();

shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import android.content.Context;
1010
import android.support.annotation.NonNull;
1111

12+
import java.util.HashSet;
13+
import java.util.Set;
14+
1215
import io.flutter.embedding.engine.dart.DartExecutor;
1316
import io.flutter.embedding.engine.plugins.PluginRegistry;
1417
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
@@ -84,11 +87,13 @@ public class FlutterEngine implements LifecycleOwner {
8487
@NonNull
8588
private final TextInputChannel textInputChannel;
8689

90+
private final Set<EngineLifecycleListener> engineLifecycleListeners = new HashSet<>();
8791
private final EngineLifecycleListener engineLifecycleListener = new EngineLifecycleListener() {
8892
@SuppressWarnings("unused")
8993
public void onPreEngineRestart() {
90-
// TODO(mattcarroll): work into plugin API. should probably loop through each plugin.
91-
// pluginRegistry.onPreEngineRestart();
94+
for (EngineLifecycleListener lifecycleListener : engineLifecycleListeners) {
95+
lifecycleListener.onPreEngineRestart();
96+
}
9297
}
9398
};
9499

@@ -165,6 +170,22 @@ public void destroy() {
165170
flutterJNI.detachFromNativeAndReleaseResources();
166171
}
167172

173+
/**
174+
* Adds a {@code listener} to be notified of Flutter engine lifecycle events, e.g.,
175+
* {@code onPreEngineStart()}.
176+
*/
177+
public void addEngineLifecycleListener(@NonNull EngineLifecycleListener listener) {
178+
engineLifecycleListeners.add(listener);
179+
}
180+
181+
/**
182+
* Removes a {@code listener} that was previously added with
183+
* {@link #addEngineLifecycleListener(EngineLifecycleListener)}.
184+
*/
185+
public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener listener) {
186+
engineLifecycleListeners.remove(listener);
187+
}
188+
168189
/**
169190
* The Dart execution context associated with this {@code FlutterEngine}.
170191
*
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.engine.plugins.shim;
6+
7+
import android.app.Activity;
8+
import android.support.annotation.NonNull;
9+
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
import io.flutter.embedding.engine.FlutterEngine;
14+
import io.flutter.plugin.common.PluginRegistry;
15+
import io.flutter.plugin.platform.PlatformViewsController;
16+
import io.flutter.view.FlutterView;
17+
18+
/**
19+
* A {@link PluginRegistry} that is shimmed to use the new Android embedding and plugin API behind
20+
* the scenes.
21+
* <p>
22+
* The following is an example usage of {@code ShimPluginRegistry} within a {@code FlutterActivity}:
23+
* {@code
24+
* // Create the FlutterEngine that will back the Flutter UI.
25+
* FlutterEngine flutterEngine = new FlutterEngine(context);
26+
*
27+
* // Create a ShimPluginRegistry and wrap the FlutterEngine with the shim.
28+
* ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine, platformViewsController);
29+
*
30+
* // Use the GeneratedPluginRegistrant to add every plugin that's in the pubspec.
31+
* GeneratedPluginRegistrant.registerWith(shimPluginRegistry);
32+
* }
33+
*/
34+
public class ShimPluginRegistry implements PluginRegistry {
35+
private final FlutterEngine flutterEngine;
36+
private final PlatformViewsController platformViewsController;
37+
private final Map<String, Object> pluginMap = new HashMap<>();
38+
private final FlutterEngine.EngineLifecycleListener engineLifecycleListener = new FlutterEngine.EngineLifecycleListener() {
39+
@Override
40+
public void onPreEngineRestart() {
41+
ShimPluginRegistry.this.onPreEngineRestart();
42+
}
43+
};
44+
45+
public ShimPluginRegistry(
46+
@NonNull FlutterEngine flutterEngine,
47+
@NonNull PlatformViewsController platformViewsController
48+
) {
49+
this.flutterEngine = flutterEngine;
50+
this.flutterEngine.addEngineLifecycleListener(engineLifecycleListener);
51+
this.platformViewsController = platformViewsController;
52+
}
53+
54+
@Override
55+
public Registrar registrarFor(String pluginKey) {
56+
if (pluginMap.containsKey(pluginKey)) {
57+
throw new IllegalStateException("Plugin key " + pluginKey + " is already in use");
58+
}
59+
pluginMap.put(pluginKey, null);
60+
ShimRegistrar registrar = new ShimRegistrar(pluginKey, pluginMap);
61+
flutterEngine.getPlugins().add(registrar);
62+
return registrar;
63+
}
64+
65+
@Override
66+
public boolean hasPlugin(String pluginKey) {
67+
return pluginMap.containsKey(pluginKey);
68+
}
69+
70+
@Override
71+
public <T> T valuePublishedByPlugin(String pluginKey) {
72+
return (T) pluginMap.get(pluginKey);
73+
}
74+
75+
//----- From FlutterPluginRegistry that aren't in the PluginRegistry interface ----//
76+
public void attach(FlutterView flutterView, Activity activity) {
77+
platformViewsController.attach(activity, flutterEngine.getRenderer(), flutterEngine.getDartExecutor());
78+
}
79+
80+
public void detach() {
81+
platformViewsController.detach();
82+
platformViewsController.onFlutterViewDestroyed();
83+
}
84+
85+
private void onPreEngineRestart() {
86+
platformViewsController.onPreEngineRestart();
87+
}
88+
89+
public PlatformViewsController getPlatformViewsController() {
90+
return platformViewsController;
91+
}
92+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
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.engine.plugins.shim;
6+
7+
import android.app.Activity;
8+
import android.content.Context;
9+
import android.support.annotation.NonNull;
10+
11+
import java.util.HashSet;
12+
import java.util.Map;
13+
import java.util.Set;
14+
15+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
16+
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
17+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
18+
import io.flutter.plugin.common.BinaryMessenger;
19+
import io.flutter.plugin.common.PluginRegistry;
20+
import io.flutter.plugin.platform.PlatformViewRegistry;
21+
import io.flutter.view.FlutterMain;
22+
import io.flutter.view.FlutterView;
23+
import io.flutter.view.TextureRegistry;
24+
25+
/**
26+
* A {@link PluginRegistry.Registrar} that is shimmed to use the new Android embedding and plugin
27+
* API behind the scenes.
28+
* <p>
29+
* Instances of {@code ShimRegistrar}s are vended internally by a {@link ShimPluginRegistry}.
30+
*/
31+
class ShimRegistrar implements PluginRegistry.Registrar, FlutterPlugin, ActivityAware {
32+
private static final String TAG = "ShimRegistrar";
33+
34+
private final Map<String, Object> globalRegistrarMap;
35+
private final String pluginId;
36+
private final Set<PluginRegistry.ViewDestroyListener> viewDestroyListeners = new HashSet<>();
37+
private final Set<PluginRegistry.RequestPermissionsResultListener> requestPermissionsResultListeners = new HashSet<>();
38+
private final Set<PluginRegistry.ActivityResultListener> activityResultListeners = new HashSet<>();
39+
private final Set<PluginRegistry.NewIntentListener> newIntentListeners = new HashSet<>();
40+
private final Set<PluginRegistry.UserLeaveHintListener> userLeaveHintListeners = new HashSet<>();
41+
private FlutterPlugin.FlutterPluginBinding pluginBinding;
42+
private ActivityPluginBinding activityPluginBinding;
43+
44+
public ShimRegistrar(@NonNull String pluginId, @NonNull Map<String, Object> globalRegistrarMap) {
45+
this.pluginId = pluginId;
46+
this.globalRegistrarMap = globalRegistrarMap;
47+
}
48+
49+
@Override
50+
public Activity activity() {
51+
return activityPluginBinding != null ? activityPluginBinding.getActivity() : null;
52+
}
53+
54+
@Override
55+
public Context context() {
56+
return pluginBinding != null ? pluginBinding.getApplicationContext() : null;
57+
}
58+
59+
@Override
60+
public Context activeContext() {
61+
return activityPluginBinding == null ? context() : activity();
62+
}
63+
64+
@Override
65+
public BinaryMessenger messenger() {
66+
return pluginBinding != null ? pluginBinding.getFlutterEngine().getDartExecutor() : null;
67+
}
68+
69+
@Override
70+
public TextureRegistry textures() {
71+
return pluginBinding != null ? pluginBinding.getFlutterEngine().getRenderer() : null;
72+
}
73+
74+
@Override
75+
public PlatformViewRegistry platformViewRegistry() {
76+
return null;
77+
}
78+
79+
@Override
80+
public FlutterView view() {
81+
throw new UnsupportedOperationException("The new embedding does not support the old FlutterView.");
82+
}
83+
84+
@Override
85+
public String lookupKeyForAsset(String asset) {
86+
return FlutterMain.getLookupKeyForAsset(asset);
87+
}
88+
89+
@Override
90+
public String lookupKeyForAsset(String asset, String packageName) {
91+
return FlutterMain.getLookupKeyForAsset(asset, packageName);
92+
}
93+
94+
@Override
95+
public PluginRegistry.Registrar publish(Object value) {
96+
globalRegistrarMap.put(pluginId, value);
97+
return this;
98+
}
99+
100+
@Override
101+
public PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener listener) {
102+
requestPermissionsResultListeners.add(listener);
103+
104+
if (activityPluginBinding != null) {
105+
activityPluginBinding.addRequestPermissionsResultListener(listener);
106+
}
107+
108+
return this;
109+
}
110+
111+
@Override
112+
public PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener listener) {
113+
activityResultListeners.add(listener);
114+
115+
if (activityPluginBinding != null) {
116+
activityPluginBinding.addActivityResultListener(listener);
117+
}
118+
119+
return this;
120+
}
121+
122+
@Override
123+
public PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener listener) {
124+
newIntentListeners.add(listener);
125+
126+
if (activityPluginBinding != null) {
127+
activityPluginBinding.addOnNewIntentListener(listener);
128+
}
129+
130+
return this;
131+
}
132+
133+
@Override
134+
public PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener listener) {
135+
userLeaveHintListeners.add(listener);
136+
137+
if (activityPluginBinding != null) {
138+
activityPluginBinding.addOnUserLeaveHintListener(listener);
139+
}
140+
141+
return this;
142+
}
143+
144+
@Override
145+
@NonNull
146+
public PluginRegistry.Registrar addViewDestroyListener(@NonNull PluginRegistry.ViewDestroyListener listener) {
147+
viewDestroyListeners.add(listener);
148+
return this;
149+
}
150+
151+
@Override
152+
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
153+
pluginBinding = binding;
154+
}
155+
156+
@Override
157+
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
158+
for (PluginRegistry.ViewDestroyListener listener : viewDestroyListeners) {
159+
// The following invocation might produce unexpected behavior in old plugins because
160+
// we have no FlutterNativeView to pass to onViewDestroy(). This is a limitation of this shim.
161+
listener.onViewDestroy(null);
162+
}
163+
164+
pluginBinding = null;
165+
}
166+
167+
@Override
168+
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
169+
activityPluginBinding = binding;
170+
addExistingListenersToActivityPluginBinding();
171+
}
172+
173+
@Override
174+
public void onDetachedFromActivityForConfigChanges() {
175+
activityPluginBinding = null;
176+
}
177+
178+
@Override
179+
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
180+
activityPluginBinding = binding;
181+
addExistingListenersToActivityPluginBinding();
182+
}
183+
184+
@Override
185+
public void onDetachedFromActivity() {
186+
activityPluginBinding = null;
187+
}
188+
189+
private void addExistingListenersToActivityPluginBinding() {
190+
for (PluginRegistry.RequestPermissionsResultListener listener : requestPermissionsResultListeners) {
191+
activityPluginBinding.addRequestPermissionsResultListener(listener);
192+
}
193+
for (PluginRegistry.ActivityResultListener listener : activityResultListeners) {
194+
activityPluginBinding.addActivityResultListener(listener);
195+
}
196+
for (PluginRegistry.NewIntentListener listener : newIntentListeners) {
197+
activityPluginBinding.addOnNewIntentListener(listener);
198+
}
199+
for (PluginRegistry.UserLeaveHintListener listener : userLeaveHintListeners) {
200+
activityPluginBinding.addOnUserLeaveHintListener(listener);
201+
}
202+
}
203+
}

0 commit comments

Comments
 (0)