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.support.annotation.NonNull; 8 9 import java.util.HashMap; 10 import java.util.HashSet; 11 import java.util.Map; 12 import java.util.Set; 13 14 import io.flutter.Log; 15 import io.flutter.embedding.engine.FlutterEngine; 16 import io.flutter.embedding.engine.plugins.FlutterPlugin; 17 import io.flutter.embedding.engine.plugins.activity.ActivityAware; 18 import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; 19 import io.flutter.plugin.common.PluginRegistry; 20 21 /** 22 * A {@link PluginRegistry} that is shimmed to use the new Android embedding and plugin API behind 23 * the scenes. 24 * <p> 25 * The following is an example usage of {@code ShimPluginRegistry} within a {@code FlutterActivity}: 26 * {@code 27 * // Create the FlutterEngine that will back the Flutter UI. 28 * FlutterEngine flutterEngine = new FlutterEngine(context); 29 * 30 * // Create a ShimPluginRegistry and wrap the FlutterEngine with the shim. 31 * ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine, platformViewsController); 32 * 33 * // Use the GeneratedPluginRegistrant to add every plugin that's in the pubspec. 34 * GeneratedPluginRegistrant.registerWith(shimPluginRegistry); 35 * } 36 */ 37 public class ShimPluginRegistry implements PluginRegistry { 38 private static final String TAG = "ShimPluginRegistry"; 39 40 private final FlutterEngine flutterEngine; 41 private final Map<String, Object> pluginMap = new HashMap<>(); 42 private final ShimRegistrarAggregate shimRegistrarAggregate; 43 ShimPluginRegistry(@onNull FlutterEngine flutterEngine)44 public ShimPluginRegistry(@NonNull FlutterEngine flutterEngine) { 45 this.flutterEngine = flutterEngine; 46 this.shimRegistrarAggregate = new ShimRegistrarAggregate(); 47 this.flutterEngine.getPlugins().add(shimRegistrarAggregate); 48 } 49 50 @Override registrarFor(String pluginKey)51 public Registrar registrarFor(String pluginKey) { 52 Log.v(TAG, "Creating plugin Registrar for '" + pluginKey + "'"); 53 if (pluginMap.containsKey(pluginKey)) { 54 throw new IllegalStateException("Plugin key " + pluginKey + " is already in use"); 55 } 56 pluginMap.put(pluginKey, null); 57 ShimRegistrar registrar = new ShimRegistrar(pluginKey, pluginMap); 58 shimRegistrarAggregate.addPlugin(registrar); 59 return registrar; 60 } 61 62 @Override hasPlugin(String pluginKey)63 public boolean hasPlugin(String pluginKey) { 64 return pluginMap.containsKey(pluginKey); 65 } 66 67 @Override 68 @SuppressWarnings("unchecked") valuePublishedByPlugin(String pluginKey)69 public <T> T valuePublishedByPlugin(String pluginKey) { 70 return (T) pluginMap.get(pluginKey); 71 } 72 73 /** 74 * Aggregates all {@link ShimRegistrar}s within one single {@link FlutterPlugin}. 75 * <p> 76 * The reason we need this aggregate is because the new embedding uniquely identifies 77 * plugins by their plugin class, but the plugin shim system represents every plugin 78 * with a {@link ShimRegistrar}. Therefore, every plugin we would register after the first 79 * plugin, would overwrite the previous plugin, because they're all {@link ShimRegistrar} 80 * instances. 81 * <p> 82 * {@code ShimRegistrarAggregate} multiplexes {@link FlutterPlugin} and {@link ActivityAware} 83 * calls so that we can register just one {@code ShimRegistrarAggregate} with a 84 * {@link FlutterEngine}, while forwarding the relevant plugin resources to any number 85 * of {@link ShimRegistrar}s within this {@code ShimRegistrarAggregate}. 86 */ 87 private static class ShimRegistrarAggregate implements FlutterPlugin, ActivityAware { 88 private final Set<ShimRegistrar> shimRegistrars = new HashSet<>(); 89 private FlutterPluginBinding flutterPluginBinding; 90 private ActivityPluginBinding activityPluginBinding; 91 addPlugin(@onNull ShimRegistrar shimRegistrar)92 public void addPlugin(@NonNull ShimRegistrar shimRegistrar) { 93 shimRegistrars.add(shimRegistrar); 94 95 if (flutterPluginBinding != null) { 96 shimRegistrar.onAttachedToEngine(flutterPluginBinding); 97 } 98 if (activityPluginBinding != null) { 99 shimRegistrar.onAttachedToActivity(activityPluginBinding); 100 } 101 } 102 103 @Override onAttachedToEngine(@onNull FlutterPluginBinding binding)104 public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { 105 flutterPluginBinding = binding; 106 for (ShimRegistrar shimRegistrar : shimRegistrars) { 107 shimRegistrar.onAttachedToEngine(binding); 108 } 109 } 110 111 @Override onDetachedFromEngine(@onNull FlutterPluginBinding binding)112 public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 113 for (ShimRegistrar shimRegistrar : shimRegistrars) { 114 shimRegistrar.onDetachedFromEngine(binding); 115 } 116 flutterPluginBinding = null; 117 } 118 119 @Override onAttachedToActivity(@onNull ActivityPluginBinding binding)120 public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { 121 activityPluginBinding = binding; 122 for (ShimRegistrar shimRegistrar : shimRegistrars) { 123 shimRegistrar.onAttachedToActivity(binding); 124 } 125 } 126 127 @Override onDetachedFromActivityForConfigChanges()128 public void onDetachedFromActivityForConfigChanges() { 129 for (ShimRegistrar shimRegistrar : shimRegistrars) { 130 shimRegistrar.onDetachedFromActivity(); 131 } 132 activityPluginBinding = null; 133 } 134 135 @Override onReattachedToActivityForConfigChanges(@onNull ActivityPluginBinding binding)136 public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { 137 for (ShimRegistrar shimRegistrar : shimRegistrars) { 138 shimRegistrar.onReattachedToActivityForConfigChanges(binding); 139 } 140 } 141 142 @Override onDetachedFromActivity()143 public void onDetachedFromActivity() { 144 for (ShimRegistrar shimRegistrar : shimRegistrars) { 145 shimRegistrar.onDetachedFromActivity(); 146 } 147 } 148 } 149 } 150