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';
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 @@ abstract class 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.
/// 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 @@ class AndroidApk extends ApplicationPackage {
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 @@ class IOSApp extends ApplicationPackage {
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 @@ class ApplicationPackageStore {
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:
......
......@@ -74,13 +74,7 @@ class Artifact {
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
......
......@@ -242,7 +242,7 @@ Future<_ApkComponents> _findApkComponents(
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 @@ Future<_ApkComponents> _findApkComponents(
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 @@ Future<int> build(
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(
......
......@@ -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(
Device device,
ApplicationPackageStore applicationPackages,
......@@ -157,7 +169,11 @@ Future<int> startApp(
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;
}
......
......@@ -16,7 +16,11 @@ import 'package:mockito/mockito.dart';
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'
......
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