Skip to content
This repository has been archived by the owner on Apr 24, 2021. It is now read-only.

Build APK backend #3498

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2a94ecc
added some handling of the Build button click event
albualexandru Sep 17, 2014
acb2a0b
added the arhiving capability to the build command
albualexandru Sep 18, 2014
9b3c425
some minor changes
albualexandru Sep 18, 2014
19e79bc
Merge remote-tracking branch 'upstream/master' into build-backend
albualexandru Sep 18, 2014
9626845
some minor changes
albualexandru Sep 18, 2014
7e218b9
some minor changes
albualexandru Sep 18, 2014
92276a3
Merge remote-tracking branch 'upstream/master' into build-backend
albualexandru Sep 19, 2014
32e997f
added the app info required for deployment into the manifest.json
albualexandru Sep 22, 2014
7cb6fbd
refactored the backend of the build apk process
albualexandru Sep 23, 2014
5bd8764
style changes
albualexandru Sep 23, 2014
f8b5ca9
Merge remote-tracking branch 'upstream/master' into build-backend
albualexandru Sep 23, 2014
1ea8d8a
encode manifest with indentation
albualexandru Sep 23, 2014
3055e23
refactoring
albualexandru Sep 23, 2014
dc363f9
Merge remote-tracking branch 'upstream/master' into build-backend
albualexandru Sep 23, 2014
a8c8b05
Merge remote-tracking branch 'upstream/master' into build-backend
albualexandru Sep 24, 2014
1a7f254
changed to send the zip file
albualexandru Sep 29, 2014
9a45004
changes
albualexandru Oct 1, 2014
2aa5daf
integration with CADT
albualexandru Oct 2, 2014
11bc0d1
cleaned the code
albualexandru Oct 2, 2014
2966e88
cleaned the code
albualexandru Oct 2, 2014
fd8fd04
review changes
albualexandru Oct 3, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion ide/web/lib/mobile/deploy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'dart:convert';
import 'package:chrome/chrome_app.dart' as chrome;
import 'package:chrome_net/tcp.dart';
import 'package:logging/logging.dart';
import 'package:crypto/crypto.dart' as crypto;

import 'adb.dart';
import 'adb_client_tcp.dart';
Expand Down Expand Up @@ -49,8 +50,9 @@ class MobileDeploy {
final PreferenceStore _prefs;

List<DeviceInfo> _knownDevices = [];
MobileBuildInfo _appInfo;

MobileDeploy(this.appContainer, this._prefs) {
MobileDeploy(this.appContainer, this._prefs, [this._appInfo]) {
if (appContainer == null) {
throw new ArgumentError('must provide an app to push');
}
Expand Down Expand Up @@ -103,6 +105,36 @@ class MobileDeploy {
});
}

Future buildWithHost(String target, ProgressMonitor monitor) {
monitor.start('Building…', maxWork: 10);

_logger.info('building application to ip host');
HttpDeployer dep = new HttpDeployer(appContainer, _prefs, target);
return dep.build(monitor, _appInfo);
}


Future buildWithAdb(ProgressMonitor monitor) {
monitor.start('Building…', maxWork: 10);

// Try to find a local ADB server. If we fail, try to use USB.
return AdbClientTcp.createClient().then((AdbClientTcp client) {
_logger.info('building application via adb server');
return client.forwardTcp(DEPLOY_PORT, DEPLOY_PORT).then((_) {
// Push the app binary on DEPLOY_PORT.
ADBDeployer dep = new ADBDeployer(appContainer, _prefs);
return dep.build(monitor, _appInfo);
});
}, onError: (_) {
_logger.info('building application via ADB over USB');
USBDeployer dep = new USBDeployer(appContainer, _prefs);
return dep.init().then((_) {
return dep.build(monitor, _appInfo);
});
});

}

Future _pushToAdbServer(AdbClientTcp client, ProgressMonitor monitor) {
// Start ADT on the device.
// TODO: a SocketException, code == -100 here often means that the App Dev
Expand All @@ -127,6 +159,7 @@ abstract class AbstractDeployer {
static const int DEPLOY_PORT = 2424;
static Duration REGULAR_REQUEST_TIMEOUT = new Duration(seconds: 2);
static Duration PUSH_REQUEST_TIMEOUT = new Duration(seconds: 60);
static Duration BUILD_REQUEST_TIMEOUT = new Duration(seconds: 120);

final Container appContainer;
final PreferenceStore _prefs;
Expand Down Expand Up @@ -182,6 +215,12 @@ abstract class AbstractDeployer {
payload: archivedData);
}

List<int> _makeBuildRequest(String target, List<int> archivedData) {
return _buildHttpRequest("POST" ,target,
"buildapk?appId=${appContainer.project.name}&appType=chrome",
payload: archivedData);
}

List<int> _buildDeleteRequest(String target, List<int> archivedData) {
return _buildHttpRequest("POST" ,target,
"deletefiles?appId=${appContainer.project.name}",
Expand Down Expand Up @@ -310,6 +349,49 @@ abstract class AbstractDeployer {
/// that the used resources are disposed
void _doWhenComplete();

Future build(ProgressMonitor monitor, MobileBuildInfo appInfo) {
List<int> httpRequest;
Map<String, String> signInfo = new Map<String, String>();

httpRequest = _buildAssetManifestRequest(_getTarget());
return _setTimeout(_pushRequestToDevice(httpRequest, REGULAR_REQUEST_TIMEOUT))
.then((msg) {
return _expectHttpOkResponse(msg);
}).then((String result) {
return _deleteObsoleteFiles(result);
}).then((msg) {
if (msg != null) {
return _expectHttpOkResponse(msg);
}
}).then((_) {
return archiveModifiedFilesInContainer(appContainer, true, [])
.then((List<int> archivedData) {
httpRequest = _buildPushRequest(_getTarget(), archivedData);
return _setTimeout(_pushRequestToDevice(httpRequest, PUSH_REQUEST_TIMEOUT));
}).then((msg) {
return _expectHttpOkResponse(msg);
}).then((String response) {
_updateContainerEtag(response);
return appInfo.publicKey.readBytes();
}).then((chrome.ArrayBuffer data) {
signInfo['publicKeyData'] = crypto.CryptoUtils.bytesToBase64(data.getBytes());
return appInfo.privateKey.readBytes();
}).then((chrome.ArrayBuffer data) {
signInfo['privateKeyData'] = crypto.CryptoUtils.bytesToBase64(data.getBytes());
signInfo['keyPassword'] = appInfo.keyPassword;

httpRequest = _makeBuildRequest(_getTarget(), JSON.encode(signInfo).codeUnits);
return _setTimeout(_pushRequestToDevice(httpRequest, BUILD_REQUEST_TIMEOUT));
}).then((msg) {
return _expectHttpOkResponse(msg);
}).then((String response) {
return new Future.error("Not implemented");
});
}).whenComplete(() {
_doWhenComplete();
});
}

/// Implements the deployement flow
Future deploy(ProgressMonitor monitor) {
List<int> httpRequest;
Expand Down Expand Up @@ -474,3 +556,9 @@ class LiveDeployManager {
});
}
}

class MobileBuildInfo {
chrome.ChromeFileEntry publicKey;
chrome.ChromeFileEntry privateKey;
String keyPassword;
}
111 changes: 107 additions & 4 deletions ide/web/spark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
library spark;

import 'dart:async';
import 'dart:convert' show JSON;
import 'dart:convert';
import 'dart:html' hide File;

import 'package:chrome/chrome_app.dart' as chrome;
Expand Down Expand Up @@ -2166,9 +2166,16 @@ class FocusMainMenuAction extends SparkAction {
}
}

class BuildApkAction extends SparkActionWithDialog {
class BuildApkAction extends SparkActionWithProgressDialog {
BuildApkAction(Spark spark, Element dialog)
: super(spark, "application-build", "Build APK…", dialog) {
_appNameElement = getElement('#appName');
_packageNameElement = getElement('#packageName');
_versionNameElement = getElement('#versionName');
_privateKeyElement = getElement('#privateKey');
_publicKeyElement = getElement('#publicKey');
_privatePasswordElement = getElement('#privateKeyPassword');
_appInfo = new MobileBuildInfo();
getElement('#choosePrivateKey').onClick.listen((_) {
_selectKey('privateKey');
});
Expand All @@ -2179,19 +2186,115 @@ class BuildApkAction extends SparkActionWithDialog {

void _selectKey(String outputField) {
spark.chooseFileEntry().then((ChromeFileEntry entry) {
if (outputField == 'publicKey') {
_appInfo.publicKey = entry;
} else {
_appInfo.privateKey = entry;
}
filesystem.fileSystemAccess.getDisplayPath(entry).then((String path) {
getElement('#$outputField').text = path;
});
});
}

InputElement _pushUrlElement;
ProgressMonitor _monitor;
ws.Container deployContainer;
ws.Resource _resource;
MobileBuildInfo _appInfo;
InputElement _appNameElement;
InputElement _packageNameElement;
InputElement _versionNameElement;
InputElement _privatePasswordElement;
SpanElement _publicKeyElement;
SpanElement _privateKeyElement;


void _invoke([context]) {
_show();
if (context == null) {
_resource = spark.focusManager.currentResource;
} else {
_resource = context.first;
}

deployContainer = getAppContainerFor(_resource);

if (deployContainer == null) {
spark.showErrorMessage(
'Unable to Build the APK',
message: 'Unable to build the current selection; '
'please select a Chrome App to build.');
} else if (!MobileDeploy.isAvailable()) {
spark.showErrorMessage(
'Unable to Build', message: 'No USB devices available.');
} else {
_pushUrlElement = getElement('#buildTargetUrl');
_toggleProgressVisible(false);
_updateViewFromManifest(deployContainer);
_show();
}
}

SparkDialogButton get _buildButton => getElement("#buildButton");

void _updateViewFromManifest(Container container) {
chrome.ChromeFileEntry manifest = container.getChild('manifest.json').entry;
manifest.readText().then((String manifestJson) {
Map<String, dynamic> map = JSON.decode(manifestJson);
_appNameElement.value = map["name"];
_packageNameElement.value = map["short_name"];
_versionNameElement.value = map["version"];
});
}

void _updateManifestFromView(Container container) {
chrome.ChromeFileEntry manifest = container.getChild('manifest.json').entry;
manifest.readText().then((String manifestJson) {
Map<String, dynamic> map = JSON.decode(manifestJson);
map["name"] = _appNameElement.value;
map["short_name"] = _packageNameElement.value;
map["version"] = _versionNameElement.value;
var encoder = new JsonEncoder.withIndent(" ");
manifest.writeText(encoder.convert(map));
});
}

void _commit() {
super._commit();
//TODO(albualexandru): add here the binding to the build class

_setProgressMessage("Building…");
_toggleProgressVisible(true);
_buildButton.disabled = true;
_buildButton.deliverChanges();

_appInfo.keyPassword = _privatePasswordElement.value;

_monitor = new ProgressMonitorImpl(this);

String type = getElement('input[name="mobileBuildType"]:checked').id;
bool useAdb = type == 'buildTargetADB';
String url = _pushUrlElement.value;

MobileDeploy deployer = new MobileDeploy(deployContainer,
spark.localPrefs, _appInfo);
_updateManifestFromView(deployContainer);

Future f = new Future(() {
return useAdb ?
deployer.buildWithAdb(_monitor) : deployer.pushToHost(url, _monitor);
});
_monitor.runCancellableFuture(f).then((_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

_hide();
spark.showSuccessMessage('Successfully built');
}).catchError((e) {
if (e is! UserCancelledException) {
spark.showMessage('Build Failure', e.toString());
}
}).whenComplete(() {
_buildButton.disabled = false;
_buildButton.deliverChanges();
_monitor = null;
});
}
}

Expand Down
8 changes: 7 additions & 1 deletion ide/web/spark_polymer_ui.html
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@
placeholder="1.0"
spellcheck="false" autocomplete="on" focused>
</div>
<div class="input-label">
<label for="privateKeyPassword">Private Password:</label>
<input id="privateKeyPassword" type="text" class="form-control"
placeholder="android"
spellcheck="false" autosave focused>
</div>
</div>
<div class="form-group">
<spark-toolbar class="settings-block"
Expand Down Expand Up @@ -433,7 +439,7 @@
</div>

<spark-dialog-button cancel>Cancel</spark-dialog-button>
<spark-dialog-button submit>Build</spark-dialog-button>
<spark-dialog-button submit id="buildButton">Build</spark-dialog-button>
</spark-dialog>


Expand Down