Commit 7ffa82aa authored by Jakob Andersen's avatar Jakob Andersen Committed by GitHub

Inject plugin registration. (#9216)

Added a PluginRegistry to the new project template. The registry files will be automatically updated at build time to register the native plugins.

Fixes #7814.
parent 4f258357
...@@ -156,7 +156,7 @@ Future<Null> buildGradleProject(BuildMode buildMode, String target, String kerne ...@@ -156,7 +156,7 @@ Future<Null> buildGradleProject(BuildMode buildMode, String target, String kerne
settings.values['flutter.buildMode'] = buildModeName; settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties); settings.writeContents(localProperties);
writeFlutterPluginsList(); injectPlugins();
final String gradle = ensureGradle(); final String gradle = ensureGradle();
......
...@@ -15,6 +15,7 @@ import '../doctor.dart'; ...@@ -15,6 +15,7 @@ import '../doctor.dart';
import '../flx.dart' as flx; import '../flx.dart' as flx;
import '../globals.dart'; import '../globals.dart';
import '../ios/xcodeproj.dart'; import '../ios/xcodeproj.dart';
import '../plugins.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../template.dart'; import '../template.dart';
...@@ -141,8 +142,10 @@ class CreateCommand extends FlutterCommand { ...@@ -141,8 +142,10 @@ class CreateCommand extends FlutterCommand {
updateXcodeGeneratedProperties(appPath, BuildMode.debug, flx.defaultMainPath); updateXcodeGeneratedProperties(appPath, BuildMode.debug, flx.defaultMainPath);
if (argResults['pub']) if (argResults['pub']) {
await pubGet(directory: appPath); await pubGet(directory: appPath);
injectPlugins(directory: appPath);
}
printStatus(''); printStatus('');
......
...@@ -152,7 +152,7 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -152,7 +152,7 @@ Future<XcodeBuildResult> buildXcodeProject({
// copied over to a location that is suitable for Xcodebuild to find them. // copied over to a location that is suitable for Xcodebuild to find them.
final Directory appDirectory = fs.directory(app.appDirectory); final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory); await _addServicesToBundle(appDirectory);
writeFlutterPluginsList(); injectPlugins();
await _runPodInstall(appDirectory, flutterFrameworkDir(mode)); await _runPodInstall(appDirectory, flutterFrameworkDir(mode));
......
...@@ -2,46 +2,69 @@ ...@@ -2,46 +2,69 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:mustache/mustache.dart' as mustache;
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'dart/package_map.dart'; import 'dart/package_map.dart';
import 'globals.dart'; import 'globals.dart';
dynamic _loadYamlFile(String path) { class Plugin {
if (!fs.isFileSync(path)) final String name;
final String path;
final String pluginClass;
final String androidPackage;
Plugin(this.name, this.path, this.pluginClass, this.androidPackage);
factory Plugin.fromYaml(String name, String path, dynamic pluginYaml) {
String androidPackage;
String pluginClass;
if (pluginYaml != null) {
androidPackage = pluginYaml['androidPackage'];
pluginClass = pluginYaml['pluginClass'];
}
return new Plugin(name, path, pluginClass, androidPackage);
}
}
Plugin _pluginFromPubspec(String name, Uri packageRoot) {
final String pubspecPath = packageRoot.resolve('pubspec.yaml').path;
if (!fs.isFileSync(pubspecPath))
return null; return null;
final String manifestString = fs.file(path).readAsStringSync(); final dynamic pubspec = loadYaml(fs.file(pubspecPath).readAsStringSync());
return loadYaml(manifestString); if (pubspec == null)
return null;
final dynamic flutterConfig = pubspec['flutter'];
if (flutterConfig == null || !flutterConfig.containsKey('plugin'))
return null;
printTrace('Found plugin $name at ${packageRoot.path}');
return new Plugin.fromYaml(name, packageRoot.path, flutterConfig['plugin']);
} }
String _generatePluginManifest() { List<Plugin> _findPlugins(String directory) {
final List<Plugin> plugins = <Plugin>[];
Map<String, Uri> packages; Map<String, Uri> packages;
try { try {
packages = new PackageMap(PackageMap.globalPackagesPath).map; final String packagesFile = fs.path.join(directory, PackageMap.globalPackagesPath);
packages = new PackageMap(packagesFile).map;
} on FormatException catch(e) { } on FormatException catch(e) {
printTrace('Invalid .packages file: $e'); printTrace('Invalid .packages file: $e');
return ''; return plugins;
} }
final List<String> plugins = <String>[];
packages.forEach((String name, Uri uri) { packages.forEach((String name, Uri uri) {
final Uri packageRoot = uri.resolve('..'); final Uri packageRoot = uri.resolve('..');
final dynamic packageConfig = _loadYamlFile(packageRoot.resolve('pubspec.yaml').path); final Plugin plugin = _pluginFromPubspec(name, packageRoot);
if (packageConfig != null) { if (plugin != null)
final dynamic flutterConfig = packageConfig['flutter']; plugins.add(plugin);
if (flutterConfig != null && flutterConfig.containsKey('plugin')) {
printTrace('Found plugin $name at ${packageRoot.path}');
plugins.add('$name=${packageRoot.path}');
}
}
}); });
return plugins.join('\n'); return plugins;
} }
void writeFlutterPluginsList() { void _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
final File pluginsProperties = fs.file('.flutter-plugins'); final File pluginsProperties = fs.file(fs.path.join(directory, '.flutter-plugins'));
final String pluginManifest =
final String pluginManifest = _generatePluginManifest(); plugins.map((Plugin p) => '${p.name}=${p.path}').join('\n');
if (pluginManifest.isNotEmpty) { if (pluginManifest.isNotEmpty) {
pluginsProperties.writeAsStringSync('$pluginManifest\n'); pluginsProperties.writeAsStringSync('$pluginManifest\n');
} else { } else {
...@@ -50,3 +73,132 @@ void writeFlutterPluginsList() { ...@@ -50,3 +73,132 @@ void writeFlutterPluginsList() {
} }
} }
} }
const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
import io.flutter.app.FlutterActivity;
{{#plugins}}
import {{package}}.{{class}};
{{/plugins}}
/**
* Generated file. Do not edit.
*/
public class PluginRegistry {
{{#plugins}}
public {{class}} {{name}};
{{/plugins}}
public void registerAll(FlutterActivity activity) {
{{#plugins}}
{{name}} = {{class}}.register(activity);
{{/plugins}}
}
}
''';
void _writeAndroidPluginRegistry(String directory, List<Plugin> plugins) {
final List<Map<String, dynamic>> androidPlugins = plugins
.where((Plugin p) => p.androidPackage != null && p.pluginClass != null)
.map((Plugin p) => <String, dynamic>{
'name': p.name,
'package': p.androidPackage,
'class': p.pluginClass,
})
.toList();
final Map<String, dynamic> context = <String, dynamic>{
'plugins': androidPlugins,
};
final String pluginRegistry =
new mustache.Template(_androidPluginRegistryTemplate).renderString(context);
final String javaSourcePath = fs.path.join(directory, 'android', 'app', 'src', 'main', 'java');
final Directory registryDirectory =
fs.directory(fs.path.join(javaSourcePath, 'io', 'flutter', 'plugins'));
registryDirectory.createSync(recursive: true);
final File registryFile = registryDirectory.childFile('PluginRegistry.java');
registryFile.writeAsStringSync(pluginRegistry);
}
const String _iosPluginRegistryHeaderTemplate = '''//
// Generated file. Do not edit.
//
#ifndef PluginRegistry_h
#define PluginRegistry_h
#import <Flutter/Flutter.h>
{{#plugins}}
#import "{{class}}.h"
{{/plugins}}
@interface PluginRegistry : NSObject
{{#plugins}}
@property (readonly, nonatomic) {{class}} *{{name}};
{{/plugins}}
- (instancetype)initWithController:(FlutterViewController *)controller;
@end
#endif /* PluginRegistry_h */
''';
const String _iosPluginRegistryImplementationTemplate = '''//
// Generated file. Do not edit.
//
#import "PluginRegistry.h"
@implementation PluginRegistry
- (instancetype)initWithController:(FlutterViewController *)controller {
if (self = [super init]) {
{{#plugins}}
_{{name}} = [[{{class}} alloc] initWithController:controller];
{{/plugins}}
}
return self;
}
@end
''';
void _writeIOSPluginRegistry(String directory, List<Plugin> plugins) {
final List<Map<String, dynamic>> iosPlugins = plugins
.where((Plugin p) => p.pluginClass != null)
.map((Plugin p) => <String, dynamic>{
'name': p.name,
'class': p.pluginClass,
}).
toList();
final Map<String, dynamic> context = <String, dynamic>{
'plugins': iosPlugins,
};
final String pluginRegistryHeader =
new mustache.Template(_iosPluginRegistryHeaderTemplate).renderString(context);
final String pluginRegistryImplementation =
new mustache.Template(_iosPluginRegistryImplementationTemplate).renderString(context);
final Directory registryDirectory = fs.directory(fs.path.join(directory, 'ios', 'Runner'));
registryDirectory.createSync(recursive: true);
final File registryHeaderFile = registryDirectory.childFile('PluginRegistry.h');
registryHeaderFile.writeAsStringSync(pluginRegistryHeader);
final File registryImplementationFile = registryDirectory.childFile('PluginRegistry.m');
registryImplementationFile.writeAsStringSync(pluginRegistryImplementation);
}
void injectPlugins({String directory}) {
directory ??= fs.currentDirectory.path;
final List<Plugin> plugins = _findPlugins(directory);
_writeFlutterPluginsList(directory, plugins);
if (fs.isDirectorySync(fs.path.join(directory, 'android')))
_writeAndroidPluginRegistry(directory, plugins);
if (fs.isDirectorySync(fs.path.join(directory, 'ios')))
_writeIOSPluginRegistry(directory, plugins);
}
...@@ -2,16 +2,15 @@ package {{androidIdentifier}}; ...@@ -2,16 +2,15 @@ package {{androidIdentifier}};
import android.os.Bundle; import android.os.Bundle;
import io.flutter.app.FlutterActivity; import io.flutter.app.FlutterActivity;
{{#withPluginHook}} import io.flutter.plugins.PluginRegistry;
import {{androidPluginIdentifier}}.{{pluginClass}};
{{/withPluginHook}}
public class MainActivity extends FlutterActivity { public class MainActivity extends FlutterActivity {
PluginRegistry pluginRegistry;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
{{#withPluginHook}} pluginRegistry = new PluginRegistry();
{{pluginClass}}.register(this); pluginRegistry.registerAll(this);
{{/withPluginHook}}
} }
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* PluginRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* PluginRegistry.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
...@@ -39,6 +40,8 @@ ...@@ -39,6 +40,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* PluginRegistry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PluginRegistry.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* PluginRegistry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PluginRegistry.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
4D558BB7489B1C82B42A9097 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4D558BB7489B1C82B42A9097 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
...@@ -121,6 +124,8 @@ ...@@ -121,6 +124,8 @@
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */, 97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */, 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* PluginRegistry.h */,
1498D2331E8E89220040F4C2 /* PluginRegistry.m */,
); );
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -299,6 +304,7 @@ ...@@ -299,6 +304,7 @@
files = ( files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */, 97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* PluginRegistry.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
......
#include "AppDelegate.h" #include "AppDelegate.h"
{{#withPluginHook}} #include "PluginRegistry.h"
#include "{{pluginClass}}.h"
@implementation AppDelegate { @implementation AppDelegate {
{{pluginClass}} *_{{pluginProjectName}}; PluginRegistry *plugins;
} }
{{/withPluginHook}}
{{^withPluginHook}}
@implementation AppDelegate
{{/withPluginHook}}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch. // Override point for customization after application launch.
{{#withPluginHook}}
FlutterViewController *flutterController = FlutterViewController *flutterController =
(FlutterViewController *)self.window.rootViewController; (FlutterViewController *)self.window.rootViewController;
_{{pluginProjectName}} = [[{{pluginClass}} alloc] initWithController:flutterController]; plugins = [[PluginRegistry alloc] initWithController:flutterController];
{{/withPluginHook}} return YES;
return YES;
} }
- (void)applicationWillResignActive:(UIApplication *)application { - (void)applicationWillResignActive:(UIApplication *)application {
......
...@@ -12,8 +12,8 @@ import io.flutter.plugin.common.MethodCall; ...@@ -12,8 +12,8 @@ import io.flutter.plugin.common.MethodCall;
public class {{pluginClass}} implements MethodCallHandler { public class {{pluginClass}} implements MethodCallHandler {
private FlutterActivity activity; private FlutterActivity activity;
public static void register(FlutterActivity activity) { public static {{pluginClass}} register(FlutterActivity activity) {
new {{pluginClass}}(activity); return new {{pluginClass}}(activity);
} }
private {{pluginClass}}(FlutterActivity activity) { private {{pluginClass}}(FlutterActivity activity) {
......
...@@ -3,6 +3,8 @@ description: {{description}} ...@@ -3,6 +3,8 @@ description: {{description}}
flutter: flutter:
plugin: plugin:
androidPackage: {{androidIdentifier}}
pluginClass: {{pluginClass}}
dependencies: dependencies:
flutter: flutter:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment