Commit e2744e9a authored by Adam Barth's avatar Adam Barth

Stop using a prebuilt APK

Instead, require an AndroidManifest.xml and always build an APK.

Fixes #2517
parent cb620718
<?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>
<?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>
<?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>
...@@ -8,7 +8,6 @@ import 'dart:io'; ...@@ -8,7 +8,6 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:xml/xml.dart' as xml; import 'package:xml/xml.dart' as xml;
import 'artifacts.dart';
import 'build_configuration.dart'; import 'build_configuration.dart';
import 'ios/plist_utils.dart'; import 'ios/plist_utils.dart';
...@@ -36,32 +35,23 @@ abstract class ApplicationPackage { ...@@ -36,32 +35,23 @@ abstract class ApplicationPackage {
} }
class AndroidApk extends ApplicationPackage { 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. /// The path to the activity that should be launched.
/// Defaults to 'org.domokit.sky.shell/org.domokit.sky.shell.SkyActivity'
final String launchActivity; final String launchActivity;
AndroidApk({ AndroidApk({
String localPath, String localPath,
String id: _defaultId, String id,
this.launchActivity: _defaultLaunchActivity this.launchActivity
}) : super(localPath: localPath, id: id) { }) : super(localPath: localPath, id: id) {
assert(launchActivity != null); assert(launchActivity != null);
} }
/// Creates a new AndroidApk based on the information in the Android manifest. /// Creates a new AndroidApk based on the information in the Android manifest.
static AndroidApk getCustomApk({ factory AndroidApk.fromBuildConfiguration(BuildConfiguration config) {
String localPath: _defaultOutputPath, String manifestPath = path.join('android', 'AndroidManifest.xml');
String manifest: _defaultManifestPath if (!FileSystemEntity.isFileSync(manifestPath))
}) {
if (!FileSystemEntity.isFileSync(manifest))
return null; return null;
String manifestString = new File(manifest).readAsStringSync(); String manifestString = new File(manifestPath).readAsStringSync();
xml.XmlDocument document = xml.parse(manifestString); xml.XmlDocument document = xml.parse(manifestString);
Iterable<xml.XmlElement> manifests = document.findElements('manifest'); Iterable<xml.XmlElement> manifests = document.findElements('manifest');
...@@ -81,6 +71,7 @@ class AndroidApk extends ApplicationPackage { ...@@ -81,6 +71,7 @@ class AndroidApk extends ApplicationPackage {
if (id == null || launchActivity == null) if (id == null || launchActivity == null)
return null; return null;
String localPath = path.join('build', 'app.apk');
return new AndroidApk(localPath: localPath, id: id, launchActivity: launchActivity); return new AndroidApk(localPath: localPath, id: id, launchActivity: launchActivity);
} }
} }
...@@ -95,12 +86,12 @@ class IOSApp extends ApplicationPackage { ...@@ -95,12 +86,12 @@ class IOSApp extends ApplicationPackage {
if (getCurrentHostPlatform() != HostPlatform.mac) if (getCurrentHostPlatform() != HostPlatform.mac)
return null; return null;
String plistPath = path.join("ios", "Info.plist"); String plistPath = path.join('ios', 'Info.plist');
String value = getValueFromFile(plistPath, kCFBundleIdentifierKey); String value = getValueFromFile(plistPath, kCFBundleIdentifierKey);
if (value == null) if (value == null)
return null; return null;
String projectDir = path.join("ios", ".generated"); String projectDir = path.join('ios', '.generated');
return new IOSApp(iosProjectDir: projectDir, iosProjectBundleId: value); return new IOSApp(iosProjectDir: projectDir, iosProjectBundleId: value);
} }
...@@ -134,20 +125,7 @@ class ApplicationPackageStore { ...@@ -134,20 +125,7 @@ class ApplicationPackageStore {
switch (config.targetPlatform) { switch (config.targetPlatform) {
case TargetPlatform.android_arm: case TargetPlatform.android_arm:
assert(android == null); assert(android == null);
android = AndroidApk.getCustomApk(); android = new AndroidApk.fromBuildConfiguration(config);
// 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));
}
break; break;
case TargetPlatform.ios_arm: case TargetPlatform.ios_arm:
......
...@@ -74,13 +74,7 @@ class Artifact { ...@@ -74,13 +74,7 @@ class Artifact {
class ArtifactStore { class ArtifactStore {
static const List<Artifact> knownArtifacts = const <Artifact>[ static const List<Artifact> knownArtifacts = const <Artifact>[
const Artifact._( const Artifact._(
name: 'Sky Shell', name: 'Flutter Tester',
fileName: 'SkyShell.apk',
type: ArtifactType.shell,
targetPlatform: TargetPlatform.android_arm
),
const Artifact._(
name: 'Sky Shell',
fileName: 'sky_shell', fileName: 'sky_shell',
type: ArtifactType.shell, type: ArtifactType.shell,
targetPlatform: TargetPlatform.linux_x64 targetPlatform: TargetPlatform.linux_x64
......
...@@ -242,7 +242,7 @@ Future<_ApkComponents> _findApkComponents( ...@@ -242,7 +242,7 @@ Future<_ApkComponents> _findApkComponents(
if (!components.resources.existsSync()) { if (!components.resources.existsSync()) {
// TODO(eseidel): This level should be higher when path is manually set. // 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; components.resources = null;
} }
...@@ -250,7 +250,7 @@ Future<_ApkComponents> _findApkComponents( ...@@ -250,7 +250,7 @@ Future<_ApkComponents> _findApkComponents(
components.manifest, components.icuData, components.libSkyShell, components.debugKeystore components.manifest, components.icuData, components.libSkyShell, components.debugKeystore
]..addAll(components.jars)) { ]..addAll(components.jars)) {
if (!f.existsSync()) { if (!f.existsSync()) {
printError('Can not locate file: ${f.path}'); printError('Cannot locate file: ${f.path}');
return null; return null;
} }
} }
...@@ -453,8 +453,8 @@ Future<int> build( ...@@ -453,8 +453,8 @@ Future<int> build(
String target: '' String target: ''
}) async { }) async {
if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) { if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) {
printStatus('Using pre-built SkyShell.apk.'); printError('Cannot build APK. Missing $_kDefaultAndroidManifestPath.');
return 0; return 1;
} }
int result = await buildAndroid( int result = await buildAndroid(
......
...@@ -129,6 +129,18 @@ class RunCommand extends RunCommandBase { ...@@ -129,6 +129,18 @@ class RunCommand extends RunCommandBase {
} }
} }
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( Future<int> startApp(
Device device, Device device,
ApplicationPackageStore applicationPackages, ApplicationPackageStore applicationPackages,
...@@ -157,7 +169,11 @@ Future<int> startApp( ...@@ -157,7 +169,11 @@ Future<int> startApp(
ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform); ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
if (package == null) { 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; return 1;
} }
......
...@@ -16,7 +16,11 @@ import 'package:mockito/mockito.dart'; ...@@ -16,7 +16,11 @@ import 'package:mockito/mockito.dart';
class MockApplicationPackageStore extends ApplicationPackageStore { class MockApplicationPackageStore extends ApplicationPackageStore {
MockApplicationPackageStore() : super( 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( iOS: new IOSApp(
iosProjectDir: '/mock/path/to/iOS/SkyShell.app', iosProjectDir: '/mock/path/to/iOS/SkyShell.app',
iosProjectBundleId: 'io.flutter.ios.mock' iosProjectBundleId: 'io.flutter.ios.mock'
......
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