diff --git a/examples/hello_android/README.md b/examples/hello_android/README.md new file mode 100644 index 00000000000..c7d64c4fac2 --- /dev/null +++ b/examples/hello_android/README.md @@ -0,0 +1,16 @@ +# Example of building a Flutter app for Android using Gradle + +This project demonstrates how to embed Flutter within an Android application +and build the Android and Flutter components with Gradle. + +To build this project: + +* Create a `local.properties` file with these entries: + * `sdk.dir=[path to the Android SDK]` + * `flutter.sdk=[path to the Flutter SDK]` + * `flutter.jar=[path to the flutter.jar file in your build of the Flutter engine]` + +Then run: + +* `gradle wrapper` +* `./gradlew build` diff --git a/examples/hello_android/app/build.gradle b/examples/hello_android/app/build.gradle new file mode 100644 index 00000000000..8ece56f40bf --- /dev/null +++ b/examples/hello_android/app/build.gradle @@ -0,0 +1,15 @@ +apply plugin: 'com.android.application' +apply plugin: 'flutter' + +android { + compileSdkVersion 22 + buildToolsVersion '22.0.1' + + lintOptions { + disable 'InvalidPackage' + } +} + +flutter { + source 'src/flutter' +} diff --git a/examples/hello_android/app/src/flutter/lib/main.dart b/examples/hello_android/app/src/flutter/lib/main.dart new file mode 100644 index 00000000000..151b1da2b81 --- /dev/null +++ b/examples/hello_android/app/src/flutter/lib/main.dart @@ -0,0 +1,7 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/widgets.dart'; + +void main() => runApp(new Center(child: new Text('Hello from Flutter!'))); diff --git a/examples/hello_android/app/src/flutter/pubspec.yaml b/examples/hello_android/app/src/flutter/pubspec.yaml new file mode 100644 index 00000000000..56699090ba1 --- /dev/null +++ b/examples/hello_android/app/src/flutter/pubspec.yaml @@ -0,0 +1,4 @@ +name: gradle +dependencies: + flutter: + path: ../../../../../packages/flutter diff --git a/examples/hello_android/app/src/main/AndroidManifest.xml b/examples/hello_android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a272e1429ac --- /dev/null +++ b/examples/hello_android/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/examples/hello_android/app/src/main/java/com/example/flutter/FlutterActivity.java b/examples/hello_android/app/src/main/java/com/example/flutter/FlutterActivity.java new file mode 100644 index 00000000000..7323d6feb29 --- /dev/null +++ b/examples/hello_android/app/src/main/java/com/example/flutter/FlutterActivity.java @@ -0,0 +1,39 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.example.flutter; + +import android.app.Activity; +import android.os.Bundle; + +import org.chromium.base.PathUtils; +import org.domokit.sky.shell.SkyApplication; +import org.domokit.sky.shell.SkyMain; +import org.domokit.sky.shell.PlatformViewAndroid; + +import java.io.File; + +public class FlutterActivity extends Activity { + private PlatformViewAndroid flutterView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + SkyMain.ensureInitialized(getApplicationContext(), null); + setContentView(R.layout.flutter_layout); + + flutterView = (PlatformViewAndroid) findViewById(R.id.flutter_view); + File appBundle = new File(PathUtils.getDataDirectory(this), SkyApplication.APP_BUNDLE); + flutterView.runFromBundle(appBundle.getPath(), null); + } + + @Override + protected void onDestroy() { + if (flutterView != null) { + flutterView.destroy(); + } + super.onDestroy(); + } +} diff --git a/examples/hello_android/app/src/main/res/layout/flutter_layout.xml b/examples/hello_android/app/src/main/res/layout/flutter_layout.xml new file mode 100644 index 00000000000..bc8c11d2394 --- /dev/null +++ b/examples/hello_android/app/src/main/res/layout/flutter_layout.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/examples/hello_android/app/src/main/res/values/strings.xml b/examples/hello_android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000000..670cfaef817 --- /dev/null +++ b/examples/hello_android/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Flutter App + Flutter Application + diff --git a/examples/hello_android/build.gradle b/examples/hello_android/build.gradle new file mode 100644 index 00000000000..ba2176743a5 --- /dev/null +++ b/examples/hello_android/build.gradle @@ -0,0 +1,17 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.5.0' + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.8' +} diff --git a/examples/hello_android/buildSrc/build.gradle b/examples/hello_android/buildSrc/build.gradle new file mode 100644 index 00000000000..b62a213c72f --- /dev/null +++ b/examples/hello_android/buildSrc/build.gradle @@ -0,0 +1,7 @@ +repositories { + jcenter() +} + +dependencies { + compile "com.android.tools.build:gradle:1.5.0" +} diff --git a/examples/hello_android/buildSrc/src/main/groovy/FlutterPlugin.groovy b/examples/hello_android/buildSrc/src/main/groovy/FlutterPlugin.groovy new file mode 100644 index 00000000000..bdd0c2ec2a5 --- /dev/null +++ b/examples/hello_android/buildSrc/src/main/groovy/FlutterPlugin.groovy @@ -0,0 +1,107 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.domokit.sky.gradle + +import com.android.builder.model.AndroidProject +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.Plugin +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +class FlutterPlugin implements Plugin { + private File sdkDir + + @Override + void apply(Project project) { + Properties properties = new Properties() + properties.load(project.rootProject.file("local.properties").newDataInputStream()) + + String enginePath = properties.getProperty("flutter.jar") + if (enginePath == null) { + throw new GradleException("flutter.jar must be defined in local.properties") + } + FileCollection flutterEngine = project.files(enginePath) + if (!flutterEngine.singleFile.isFile()) { + throw new GradleException("flutter.jar must point to a Flutter engine JAR") + } + + String sdkPath = properties.getProperty("flutter.sdk") + if (sdkPath == null) { + throw new GradleException("flutter.sdk must be defined in local.properties") + } + sdkDir = project.file(sdkPath) + if (!sdkDir.isDirectory()) { + throw new GradleException("flutter.sdk must point to the Flutter SDK directory") + } + + project.extensions.create("flutter", FlutterExtension) + project.dependencies.add("compile", flutterEngine) + project.afterEvaluate this.&addFlutterTask + } + + private void addFlutterTask(Project project) { + if (project.flutter.source == null) { + throw new GradleException("Must provide Flutter source directory") + } + + FlutterTask flutterTask = project.tasks.create("flutterBuild", FlutterTask) { + sdkDir this.sdkDir + sourceDir project.file(project.flutter.source) + intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter") + } + + project.android.applicationVariants.all { variant -> + Task copyFlxTask = project.tasks.create(name: "copyFlx${variant.name.capitalize()}", type: Copy) { + dependsOn flutterTask + dependsOn variant.mergeAssets + from flutterTask.flxPath + into variant.mergeAssets.outputDir + } + variant.outputs[0].processResources.dependsOn(copyFlxTask) + } + } +} + +class FlutterExtension { + String source +} + +class FlutterTask extends DefaultTask { + File sdkDir + + @InputDirectory + File sourceDir + + @OutputDirectory + File intermediateDir + + String getFlxPath() { + return "${intermediateDir}/app.flx" + } + + @TaskAction + void build() { + if (!sourceDir.isDirectory()) { + throw new GradleException("Invalid Flutter source directory: ${sourceDir}") + } + + intermediateDir.mkdirs() + project.exec { + executable "${sdkDir}/bin/flutter" + workingDir sourceDir + args "build" + args "-o", flxPath + args "--snapshot", "${intermediateDir}/snapshot_blob.bin" + args "--depfile", "${intermediateDir}/snapshot_blob.bin.d" + args "--working-dir", "${intermediateDir}/flx" + } + } +} diff --git a/examples/hello_android/buildSrc/src/main/resources/META-INF/gradle-plugins/flutter.properties b/examples/hello_android/buildSrc/src/main/resources/META-INF/gradle-plugins/flutter.properties new file mode 100644 index 00000000000..f8219bee105 --- /dev/null +++ b/examples/hello_android/buildSrc/src/main/resources/META-INF/gradle-plugins/flutter.properties @@ -0,0 +1 @@ +implementation-class=org.domokit.sky.gradle.FlutterPlugin diff --git a/examples/hello_android/settings.gradle b/examples/hello_android/settings.gradle new file mode 100644 index 00000000000..e7b4def49cb --- /dev/null +++ b/examples/hello_android/settings.gradle @@ -0,0 +1 @@ +include ':app'