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
settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties);
writeFlutterPluginsList();
injectPlugins();
final String gradle = ensureGradle();
......
......@@ -15,6 +15,7 @@ import '../doctor.dart';
import '../flx.dart' as flx;
import '../globals.dart';
import '../ios/xcodeproj.dart';
import '../plugins.dart';
import '../runner/flutter_command.dart';
import '../template.dart';
......@@ -141,8 +142,10 @@ class CreateCommand extends FlutterCommand {
updateXcodeGeneratedProperties(appPath, BuildMode.debug, flx.defaultMainPath);
if (argResults['pub'])
if (argResults['pub']) {
await pubGet(directory: appPath);
injectPlugins(directory: appPath);
}
printStatus('');
......
......@@ -152,7 +152,7 @@ Future<XcodeBuildResult> buildXcodeProject({
// copied over to a location that is suitable for Xcodebuild to find them.
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);
writeFlutterPluginsList();
injectPlugins();
await _runPodInstall(appDirectory, flutterFrameworkDir(mode));
......
......@@ -2,46 +2,69 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:mustache/mustache.dart' as mustache;
import 'package:yaml/yaml.dart';
import 'base/file_system.dart';
import 'dart/package_map.dart';
import 'globals.dart';
dynamic _loadYamlFile(String path) {
if (!fs.isFileSync(path))
class Plugin {
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;
final String manifestString = fs.file(path).readAsStringSync();
return loadYaml(manifestString);
final dynamic pubspec = loadYaml(fs.file(pubspecPath).readAsStringSync());
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;
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) {
printTrace('Invalid .packages file: $e');
return '';
return plugins;
}
final List<String> plugins = <String>[];
packages.forEach((String name, Uri uri) {
final Uri packageRoot = uri.resolve('..');
final dynamic packageConfig = _loadYamlFile(packageRoot.resolve('pubspec.yaml').path);
if (packageConfig != null) {
final dynamic flutterConfig = packageConfig['flutter'];
if (flutterConfig != null && flutterConfig.containsKey('plugin')) {
printTrace('Found plugin $name at ${packageRoot.path}');
plugins.add('$name=${packageRoot.path}');
}
}
final Plugin plugin = _pluginFromPubspec(name, packageRoot);
if (plugin != null)
plugins.add(plugin);
});
return plugins.join('\n');
return plugins;
}
void writeFlutterPluginsList() {
final File pluginsProperties = fs.file('.flutter-plugins');
final String pluginManifest = _generatePluginManifest();
void _writeFlutterPluginsList(String directory, List<Plugin> plugins) {
final File pluginsProperties = fs.file(fs.path.join(directory, '.flutter-plugins'));
final String pluginManifest =
plugins.map((Plugin p) => '${p.name}=${p.path}').join('\n');
if (pluginManifest.isNotEmpty) {
pluginsProperties.writeAsStringSync('$pluginManifest\n');
} else {
......@@ -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}};
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
{{#withPluginHook}}
import {{androidPluginIdentifier}}.{{pluginClass}};
{{/withPluginHook}}
import io.flutter.plugins.PluginRegistry;
public class MainActivity extends FlutterActivity {
PluginRegistry pluginRegistry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
{{#withPluginHook}}
{{pluginClass}}.register(this);
{{/withPluginHook}}
pluginRegistry = new PluginRegistry();
pluginRegistry.registerAll(this);
}
}
......@@ -7,6 +7,7 @@
objects = {
/* 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 */; };
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, ); }; };
......@@ -39,6 +40,8 @@
/* End PBXCopyFilesBuildPhase 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>"; };
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; };
......@@ -121,6 +124,8 @@
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* PluginRegistry.h */,
1498D2331E8E89220040F4C2 /* PluginRegistry.m */,
);
path = Runner;
sourceTree = "<group>";
......@@ -299,6 +304,7 @@
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* PluginRegistry.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
#include "AppDelegate.h"
{{#withPluginHook}}
#include "{{pluginClass}}.h"
#include "PluginRegistry.h"
@implementation AppDelegate {
{{pluginClass}} *_{{pluginProjectName}};
PluginRegistry *plugins;
}
{{/withPluginHook}}
{{^withPluginHook}}
@implementation AppDelegate
{{/withPluginHook}}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
{{#withPluginHook}}
// Override point for customization after application launch.
FlutterViewController *flutterController =
(FlutterViewController *)self.window.rootViewController;
_{{pluginProjectName}} = [[{{pluginClass}} alloc] initWithController:flutterController];
{{/withPluginHook}}
return YES;
plugins = [[PluginRegistry alloc] initWithController:flutterController];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
......
......@@ -12,8 +12,8 @@ import io.flutter.plugin.common.MethodCall;
public class {{pluginClass}} implements MethodCallHandler {
private FlutterActivity activity;
public static void register(FlutterActivity activity) {
new {{pluginClass}}(activity);
public static {{pluginClass}} register(FlutterActivity activity) {
return new {{pluginClass}}(activity);
}
private {{pluginClass}}(FlutterActivity activity) {
......
......@@ -3,6 +3,8 @@ description: {{description}}
flutter:
plugin:
androidPackage: {{androidIdentifier}}
pluginClass: {{pluginClass}}
dependencies:
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