Stop using a prebuilt APK
Instead, require an AndroidManifest.xml and always build an APK. Fixes #2517
This commit is contained in:
parent
cb62071882
commit
e2744e9a30
19
dev/manual_tests/android/AndroidManifest.xml
Normal file
19
dev/manual_tests/android/AndroidManifest.xml
Normal file
@ -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>
|
19
examples/hello_world/android/AndroidManifest.xml
Normal file
19
examples/hello_world/android/AndroidManifest.xml
Normal file
@ -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>
|
19
examples/layers/android/AndroidManifest.xml
Normal file
19
examples/layers/android/AndroidManifest.xml
Normal file
@ -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>
|
@ -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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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(
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
|||||||
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';
|
|||||||
|
|
||||||
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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user