Support flutter run on Android module project (#19600)
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index f6d8cfd..f0a7f28 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -372,7 +372,6 @@ BuildInfo buildInfo = debuggingOptions.buildInfo; if (buildInfo.targetPlatform == null && devicePlatform == TargetPlatform.android_arm64) buildInfo = buildInfo.withTargetPlatform(TargetPlatform.android_arm64); - if (!prebuiltApplication) { printTrace('Building APK'); final FlutterProject project = await FlutterProject.current();
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 8842503..5875731 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -44,7 +44,6 @@ r'|If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to .*)'); - FlutterPluginVersion getFlutterPluginVersion(AndroidProject project) { final File plugin = project.directory.childFile( fs.path.join('buildSrc', 'src', 'main', 'groovy', 'FlutterPlugin.groovy')); @@ -62,6 +61,9 @@ if (line.contains(new RegExp(r'apply from: .*/flutter.gradle'))) { return FlutterPluginVersion.managed; } + if (line.contains("def flutterPluginVersion = 'managed'")) { + return FlutterPluginVersion.managed; + } } } return FlutterPluginVersion.none;
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 41b1592..196924c 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart
@@ -76,7 +76,12 @@ IosProject get ios => new IosProject(directory.childDirectory('ios')); /// The Android sub project of this project. - AndroidProject get android => new AndroidProject(directory.childDirectory('android')); + AndroidProject get android { + if (manifest.isModule) { + return new AndroidProject(directory.childDirectory('.android')); + } + return new AndroidProject(directory.childDirectory('android')); + } /// The generated AndroidModule sub project of this module project. AndroidModuleProject get androidModule => new AndroidModuleProject(directory.childDirectory('.android'));
diff --git a/packages/flutter_tools/templates/module/android/Flutter.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/module/android/Flutter.tmpl/build.gradle.tmpl index 0db90f2..7d97120 100644 --- a/packages/flutter_tools/templates/module/android/Flutter.tmpl/build.gradle.tmpl +++ b/packages/flutter_tools/templates/module/android/Flutter.tmpl/build.gradle.tmpl
@@ -23,6 +23,8 @@ flutterVersionName = '1.0' } +buildDir = new File(rootProject.projectDir, "../build/flutter") + apply plugin: 'com.android.library' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/module/android/app.tmpl/build.gradle.tmpl new file mode 100644 index 0000000..3eb860c --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/build.gradle.tmpl
@@ -0,0 +1,35 @@ +def flutterPluginVersion = 'managed' + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "{{androidIdentifier}}.host" + minSdkVersion 16 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + +} +buildDir = new File(rootProject.projectDir, "../build/host") +dependencies { + implementation project(':flutter') + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'com.android.support:design:27.1.1' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +}
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/AndroidManifest.xml.tmpl new file mode 100644 index 0000000..e9f4d8e --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/AndroidManifest.xml.tmpl
@@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="{{androidIdentifier}}.host"> + + <!-- The INTERNET permission is required for development. Specifically, + flutter needs it to communicate with the running application + to allow setting breakpoints, to provide hot reload, etc. + --> + <uses-permission android:name="android.permission.INTERNET"/> + + <!-- io.flutter.app.FlutterApplication is an android.app.Application that + calls FlutterMain.startInitialization(this); in its onCreate method. + In most cases you can leave this as-is, but you if you want to provide + additional functionality it is fine to subclass or reimplement + FlutterApplication and put your custom class here. --> + <application + android:name="io.flutter.app.FlutterApplication" + android:icon="@mipmap/ic_launcher"> + <activity + android:name=".MainActivity" + android:launchMode="singleTop" + android:theme="@style/LaunchTheme" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" + android:hardwareAccelerated="true" + android:windowSoftInputMode="adjustResize"> + <!-- This keeps the window background of the activity showing + until Flutter renders its first frame. It can be removed if + there is no splash screen (such as the default splash screen + defined in @style/LaunchTheme). --> + <meta-data + android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" + android:value="true" /> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> +</manifest>
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl new file mode 100644 index 0000000..3441c3a --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl
@@ -0,0 +1,13 @@ +package {{androidIdentifier}}.host; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +}
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/drawable/launch_background.xml b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Modify this file to customize your launch splash screen --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@android:color/white" /> + + <!-- You can insert your own image assets here --> + <!-- <item> + <bitmap + android:gravity="center" + android:src="@mipmap/launch_image" /> + </item> --> +</layer-list>
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differ
diff --git a/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/values/styles.xml b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/values/styles.xml new file mode 100644 index 0000000..00fa441 --- /dev/null +++ b/packages/flutter_tools/templates/module/android/app.tmpl/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> + <!-- Show a splash screen on the activity. Automatically removed when + Flutter draws its first frame --> + <item name="android:windowBackground">@drawable/launch_background</item> + </style> +</resources>
diff --git a/packages/flutter_tools/templates/module/android/settings.gradle.tmpl b/packages/flutter_tools/templates/module/android/settings.gradle.tmpl index 49eb997..0605586 100644 --- a/packages/flutter_tools/templates/module/android/settings.gradle.tmpl +++ b/packages/flutter_tools/templates/module/android/settings.gradle.tmpl
@@ -1,4 +1,5 @@ // Generated file. Do not edit. +include ':app' rootProject.name = 'android_generated' setBinding(new Binding([gradle: this]))
diff --git a/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl b/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl index 1b325f6..ce3660d 100644 --- a/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl +++ b/packages/flutter_tools/templates/module/common/lib/main.dart.tmpl
@@ -1,38 +1,180 @@ -import 'dart:async'; -import 'dart:ui'; - import 'package:flutter/material.dart'; -import 'package:package_info/package_info.dart'; +{{#withDriverTest}} +import 'package:flutter_driver/driver_extension.dart'; +{{/withDriverTest}} +{{#withPluginHook}} +import 'dart:async'; -Future<void> main() async { - final PackageInfo packageInfo = await PackageInfo.fromPlatform(); - runApp(Directionality(textDirection: TextDirection.ltr, child: Router(packageInfo))); +import 'package:flutter/services.dart'; +import 'package:{{pluginProjectName}}/{{pluginProjectName}}.dart'; +{{/withPluginHook}} + +{{^withDriverTest}} +void main() => runApp(new MyApp()); +{{/withDriverTest}} +{{#withDriverTest}} +void main() { + // Enable integration testing with the Flutter Driver extension. + // See https://flutter.io/testing/ for more info. + enableFlutterDriverExtension(); + runApp(new MyApp()); +} +{{/withDriverTest}} + +{{^withPluginHook}} +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Demo', + theme: new ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or press Run > Flutter Hot Reload in IntelliJ). Notice that the + // counter didn't reset back to zero; the application is not restarted. + primarySwatch: Colors.blue, + ), + home: new MyHomePage(title: 'Flutter Demo Home Page'), + ); + } } -class Router extends StatelessWidget { - Router(this.packageInfo); +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); - final PackageInfo packageInfo; + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => new _MyHomePageState(); +} + +class _MyHomePageState extends State<MyHomePage> { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } @override Widget build(BuildContext context) { - final String route = window.defaultRouteName; - switch (route) { - case 'route1': - return Container( - child: Center(child: Text('Route 1\n${packageInfo.appName}')), - color: Colors.green, - ); - case 'route2': - return Container( - child: Center(child: Text('Route 2\n${packageInfo.packageName}')), - color: Colors.blue, - ); - default: - return Container( - child: Center(child: Text('Unknown route: $route')), - color: Colors.red, - ); - } + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return new Scaffold( + appBar: new AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: new Text(widget.title), + ), + body: new Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: new Column( + // Column is also layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug paint" (press "p" in the console where you ran + // "flutter run", or select "Toggle Debug Paint" from the Flutter tool + // window in IntelliJ) to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + new Text( + 'You have pushed the button this many times:', + ), + new Text( + '$_counter', + style: Theme.of(context).textTheme.display1, + ), + ], + ), + ), + floatingActionButton: new FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: new Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); } } +{{/withPluginHook}} +{{#withPluginHook}} +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => new _MyAppState(); +} + +class _MyAppState extends State<MyApp> { + String _platformVersion = 'Unknown'; + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future<void> initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + try { + platformVersion = await {{pluginDartClass}}.platformVersion; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return new MaterialApp( + home: new Scaffold( + appBar: new AppBar( + title: const Text('Plugin example app'), + ), + body: new Center( + child: new Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} +{{/withPluginHook}}
diff --git a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl index dc6bb57..7a8edcf 100644 --- a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl +++ b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl
@@ -3,7 +3,6 @@ version: 1.0.0+1 dependencies: - package_info: flutter: sdk: flutter