Stop using a prebuilt APK Instead, require an AndroidManifest.xml and always build an APK. Fixes #2517
diff --git a/dev/manual_tests/android/AndroidManifest.xml b/dev/manual_tests/android/AndroidManifest.xml new file mode 100644 index 0000000..98a4eaf --- /dev/null +++ b/dev/manual_tests/android/AndroidManifest.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2015 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.tests.ManualTests" android:versionCode="1" android:versionName="0.0.1"> + + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> + <uses-permission android:name="android.permission.INTERNET"/> + + <application android:label="Flutter Manual Tests" android:name="org.domokit.sky.shell.SkyApplication"> + <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:name="org.domokit.sky.shell.SkyActivity" android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + </manifest>
diff --git a/examples/hello_world/android/AndroidManifest.xml b/examples/hello_world/android/AndroidManifest.xml new file mode 100644 index 0000000..6d96b4e --- /dev/null +++ b/examples/hello_world/android/AndroidManifest.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2015 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.examples.HelloWorld" android:versionCode="1" android:versionName="0.0.1"> + + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> + <uses-permission android:name="android.permission.INTERNET"/> + + <application android:label="Flutter Hello" android:name="org.domokit.sky.shell.SkyApplication"> + <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:name="org.domokit.sky.shell.SkyActivity" android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + </manifest>
diff --git a/examples/layers/android/AndroidManifest.xml b/examples/layers/android/AndroidManifest.xml new file mode 100644 index 0000000..e81d686 --- /dev/null +++ b/examples/layers/android/AndroidManifest.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2015 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.examples.Layers" android:versionCode="1" android:versionName="0.0.1"> + + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> + <uses-permission android:name="android.permission.INTERNET"/> + + <application android:label="Flutter Layers" android:name="org.domokit.sky.shell.SkyApplication"> + <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:name="org.domokit.sky.shell.SkyActivity" android:theme="@android:style/Theme.Black.NoTitleBar"> + <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/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index b5a5bac..1b233b6 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -8,7 +8,6 @@ import 'package:path/path.dart' as path; import 'package:xml/xml.dart' as xml; -import 'artifacts.dart'; import 'build_configuration.dart'; import 'ios/plist_utils.dart'; @@ -36,32 +35,23 @@ } class AndroidApk extends ApplicationPackage { - static const String _defaultName = 'SkyShell.apk'; - static const String _defaultId = 'org.domokit.sky.shell'; - static const String _defaultLaunchActivity = '$_defaultId/$_defaultId.SkyActivity'; - static const String _defaultManifestPath = 'android/AndroidManifest.xml'; - static const String _defaultOutputPath = 'build/app.apk'; - /// The path to the activity that should be launched. - /// Defaults to 'org.domokit.sky.shell/org.domokit.sky.shell.SkyActivity' final String launchActivity; AndroidApk({ String localPath, - String id: _defaultId, - this.launchActivity: _defaultLaunchActivity + String id, + this.launchActivity }) : super(localPath: localPath, id: id) { assert(launchActivity != null); } /// Creates a new AndroidApk based on the information in the Android manifest. - static AndroidApk getCustomApk({ - String localPath: _defaultOutputPath, - String manifest: _defaultManifestPath - }) { - if (!FileSystemEntity.isFileSync(manifest)) + factory AndroidApk.fromBuildConfiguration(BuildConfiguration config) { + String manifestPath = path.join('android', 'AndroidManifest.xml'); + if (!FileSystemEntity.isFileSync(manifestPath)) return null; - String manifestString = new File(manifest).readAsStringSync(); + String manifestString = new File(manifestPath).readAsStringSync(); xml.XmlDocument document = xml.parse(manifestString); Iterable<xml.XmlElement> manifests = document.findElements('manifest'); @@ -81,6 +71,7 @@ if (id == null || launchActivity == null) return null; + String localPath = path.join('build', 'app.apk'); return new AndroidApk(localPath: localPath, id: id, launchActivity: launchActivity); } } @@ -95,12 +86,12 @@ if (getCurrentHostPlatform() != HostPlatform.mac) return null; - String plistPath = path.join("ios", "Info.plist"); + String plistPath = path.join('ios', 'Info.plist'); String value = getValueFromFile(plistPath, kCFBundleIdentifierKey); if (value == null) return null; - String projectDir = path.join("ios", ".generated"); + String projectDir = path.join('ios', '.generated'); return new IOSApp(iosProjectDir: projectDir, iosProjectBundleId: value); } @@ -134,20 +125,7 @@ switch (config.targetPlatform) { case TargetPlatform.android_arm: assert(android == null); - android = AndroidApk.getCustomApk(); - // Fall back to the prebuilt or engine-provided apk if we can't build - // a custom one. - // TODO(mpcomplete): we should remove both these fallbacks. - if (android != null) { - break; - } else if (config.type != BuildType.prebuilt) { - String localPath = path.join(config.buildDir, 'apks', AndroidApk._defaultName); - android = new AndroidApk(localPath: localPath); - } else { - Artifact artifact = ArtifactStore.getArtifact( - type: ArtifactType.shell, targetPlatform: TargetPlatform.android_arm); - android = new AndroidApk(localPath: await ArtifactStore.getPath(artifact)); - } + android = new AndroidApk.fromBuildConfiguration(config); break; case TargetPlatform.ios_arm:
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 19680d4..db27a19 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -74,13 +74,7 @@ class ArtifactStore { static const List<Artifact> knownArtifacts = const <Artifact>[ const Artifact._( - name: 'Sky Shell', - fileName: 'SkyShell.apk', - type: ArtifactType.shell, - targetPlatform: TargetPlatform.android_arm - ), - const Artifact._( - name: 'Sky Shell', + name: 'Flutter Tester', fileName: 'sky_shell', type: ArtifactType.shell, targetPlatform: TargetPlatform.linux_x64
diff --git a/packages/flutter_tools/lib/src/commands/apk.dart b/packages/flutter_tools/lib/src/commands/apk.dart index 43114e8..d6dc01b 100644 --- a/packages/flutter_tools/lib/src/commands/apk.dart +++ b/packages/flutter_tools/lib/src/commands/apk.dart
@@ -242,7 +242,7 @@ if (!components.resources.existsSync()) { // TODO(eseidel): This level should be higher when path is manually set. - printStatus('Can not locate Resources: ${components.resources}, ignoring.'); + printStatus('Cannot locate Resources: ${components.resources}, ignoring.'); components.resources = null; } @@ -250,7 +250,7 @@ components.manifest, components.icuData, components.libSkyShell, components.debugKeystore ]..addAll(components.jars)) { if (!f.existsSync()) { - printError('Can not locate file: ${f.path}'); + printError('Cannot locate file: ${f.path}'); return null; } } @@ -453,8 +453,8 @@ String target: '' }) async { if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) { - printStatus('Using pre-built SkyShell.apk.'); - return 0; + printError('Cannot build APK. Missing $_kDefaultAndroidManifestPath.'); + return 1; } int result = await buildAndroid(
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 26c7dfc..09f627e 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -129,6 +129,18 @@ } } +String _getMissingPackageHintForPlatform(TargetPlatform platform) { + switch (platform) { + case TargetPlatform.android_arm: + return 'Is your project missing an android/AndroidManifest.xml?'; + case TargetPlatform.ios_arm: + case TargetPlatform.ios_x64: + return 'Is your project missing an ios/Info.plist?'; + default: + return null; + } +} + Future<int> startApp( Device device, ApplicationPackageStore applicationPackages, @@ -157,7 +169,11 @@ ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); if (package == null) { - printError('No application found for ${device.platform}.'); + String message = 'No application found for ${device.platform}.'; + String hint = _getMissingPackageHintForPlatform(device.platform); + if (hint != null) + message += '\n$hint'; + printError(message); return 1; }
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index 62d6c36..52db59e 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart
@@ -16,7 +16,11 @@ class MockApplicationPackageStore extends ApplicationPackageStore { MockApplicationPackageStore() : super( - android: new AndroidApk(localPath: '/mock/path/to/android/SkyShell.apk'), + android: new AndroidApk( + localPath: '/mock/path/to/android/SkyShell.apk', + id: 'io.flutter.android.mock', + launchActivity: 'io.flutter.android.mock.MockActivity' + ), iOS: new IOSApp( iosProjectDir: '/mock/path/to/iOS/SkyShell.app', iosProjectBundleId: 'io.flutter.ios.mock'