nativescript
Version:
Command-line interface for building NativeScript projects
226 lines • 11 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AndroidApplicationManager = void 0;
const os_1 = require("os");
const application_manager_base_1 = require("../application-manager-base");
const constants_1 = require("../../constants");
const helpers_1 = require("../../helpers");
const decorators_1 = require("../../decorators");
const path_1 = require("path");
const _ = require("lodash");
const constants_2 = require("../../../constants");
class AndroidApplicationManager extends application_manager_base_1.ApplicationManagerBase {
constructor(adb, identifier, $androidBundleToolService, $fs, $options, $logcatHelper, $androidProcessService, $httpClient, $deviceLogProvider, $errors, $logger, $hooksService) {
super($logger, $hooksService, $deviceLogProvider);
this.adb = adb;
this.identifier = identifier;
this.$androidBundleToolService = $androidBundleToolService;
this.$fs = $fs;
this.$options = $options;
this.$logcatHelper = $logcatHelper;
this.$androidProcessService = $androidProcessService;
this.$httpClient = $httpClient;
this.$deviceLogProvider = $deviceLogProvider;
this.$errors = $errors;
this.PID_CHECK_INTERVAL = 100;
this.PID_CHECK_TIMEOUT = 10000; // 10 secs
}
async getInstalledApplications() {
const result = (await this.adb.executeShellCommand(["pm", "list", "packages"])) || "";
const regex = /package:(.+)/;
return result
.split(os_1.EOL)
.map((packageString) => {
const match = packageString.match(regex);
return match ? match[1] : null;
})
.filter((parsedPackage) => parsedPackage !== null);
}
async installApplication(packageFilePath, appIdentifier, buildData) {
if (appIdentifier) {
const deviceRootPath = `${constants_1.LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appIdentifier}`;
await this.adb.executeShellCommand(["rm", "-rf", deviceRootPath]);
}
const { dir, name, ext } = (0, path_1.parse)(packageFilePath);
if (ext === constants_2.AAB_EXTENSION_NAME) {
const apksOutputPath = (0, path_1.join)(dir, name) + constants_2.APKS_EXTENSION_NAME;
if (!this.hasValidApksFile(packageFilePath, apksOutputPath)) {
await this.$androidBundleToolService.buildApks({
aabFilePath: packageFilePath,
apksOutputPath,
signingData: buildData,
});
}
await this.$androidBundleToolService.installApks({
apksFilePath: apksOutputPath,
deviceId: this.identifier,
});
}
else {
return this.adb.executeCommand(["install", "-r", `${packageFilePath}`]);
}
}
uninstallApplication(appIdentifier) {
// Need to set the treatErrorsAsWarnings to true because when using tns run command if the application is not installed on the device it will throw error
return this.adb.executeShellCommand(["pm", "uninstall", `${appIdentifier}`], { treatErrorsAsWarnings: true });
}
async startApplication(appData) {
if (appData.waitForDebugger) {
await this.adb.executeShellCommand([
`cat /dev/null > ${constants_1.LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appData.appId}-debugbreak`,
]);
}
// If the app is debuggable, the Runtime will update the file when its ready for debugging
// and we will be able to take decisions and synchronize the debug experience based on the content
await this.adb.executeShellCommand([
`cat /dev/null > ${constants_1.LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appData.appId}-debugger-started`,
]);
/*
Example "pm dump <app_identifier> | grep -A 1 MAIN" output"
android.intent.action.MAIN:
3b2df03 org.nativescript.cliapp/com.tns.NativeScriptActivity filter 50dd82e
Action: "android.intent.action.MAIN"
Category: "android.intent.category.LAUNCHER"
--
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.nativescript.cliapp/com.tns.NativeScriptActivity}
realActivity=org.nativescript.cliapp/com.tns.NativeScriptActivity
--
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.nativescript.cliapp/com.tns.NativeScriptActivity }
frontOfTask=true task=TaskRecord{fe592ac #449 A=org.nativescript.cliapp U=0 StackId=1 sz=1}
*/
const appIdentifier = appData.appId;
const pmDumpOutput = await this.adb.executeShellCommand([
"pm",
"dump",
appIdentifier,
"|",
"grep",
"-A",
"1",
"MAIN",
]);
const activityMatch = this.getFullyQualifiedActivityRegex(appIdentifier);
const match = activityMatch.exec(pmDumpOutput);
const possibleIdentifier = match && match[0];
if (possibleIdentifier) {
await this.adb.executeShellCommand([
"am",
"start",
"-n",
possibleIdentifier,
]);
}
else {
this.$logger.trace(`Tried starting activity for: ${appIdentifier}, using activity manager but failed.`);
await this.adb.executeShellCommand([
"monkey",
"-p",
appIdentifier,
"-c",
"android.intent.category.LAUNCHER",
"1",
]);
}
await this.onAppLaunch(appData);
}
async onAppLaunch(appData) {
const appIdentifier = appData.appId;
if (!this.$options.justlaunch && !appData.justLaunch) {
const deviceIdentifier = this.identifier;
const processIdentifier = await this.getAppProcessId(deviceIdentifier, appIdentifier);
if (processIdentifier) {
this.$deviceLogProvider.setApplicationPidForDevice(deviceIdentifier, processIdentifier);
this.$deviceLogProvider.setApplicationIdForDevice(deviceIdentifier, appIdentifier);
this.$deviceLogProvider.setProjectDirForDevice(deviceIdentifier, appData.projectDir);
await this.$logcatHelper.start({
deviceIdentifier: this.identifier,
pid: processIdentifier,
appId: appIdentifier,
onAppRestarted: () => {
// If the app restarts, we update the PID and
// restart log helper.
this.onAppLaunch(appData);
},
});
}
else {
await this.$logcatHelper.dump(this.identifier);
this.$errors.fail(`Unable to find running "${appIdentifier}" application on device "${deviceIdentifier}".`);
}
}
}
async getAppProcessId(deviceIdentifier, appIdentifier) {
const appIdCheckStartTime = new Date().getTime();
let processIdentifier = "";
let hasTimedOut = false;
while (!processIdentifier && !hasTimedOut) {
processIdentifier = await this.$androidProcessService.getAppProcessId(deviceIdentifier, appIdentifier);
if (!processIdentifier) {
this.$logger.trace(`Wasn't able to get pid of the app. Sleeping for "${this.PID_CHECK_INTERVAL}ms".`);
await (0, helpers_1.sleep)(this.PID_CHECK_INTERVAL);
hasTimedOut =
new Date().getTime() - appIdCheckStartTime > this.PID_CHECK_TIMEOUT;
}
}
return processIdentifier;
}
stopApplication(appData) {
this.$logcatHelper.stop(this.identifier);
this.$deviceLogProvider.setApplicationPidForDevice(this.identifier, null);
this.$deviceLogProvider.setProjectDirForDevice(this.identifier, null);
return this.adb.executeShellCommand([
"am",
"force-stop",
`${appData.appId}`,
]);
}
getDebuggableApps() {
return this.$androidProcessService.getDebuggableApps(this.identifier);
}
async getDebuggableAppViews(appIdentifiers) {
const mappedAppIdentifierPorts = await this.$androidProcessService.getMappedAbstractToTcpPorts(this.identifier, appIdentifiers, constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova), applicationViews = {};
await Promise.all(_.map(mappedAppIdentifierPorts, async (port, appIdentifier) => {
applicationViews[appIdentifier] = [];
const localAddress = `http://127.0.0.1:${port}/json`;
try {
if (port) {
const apps = (await this.$httpClient.httpRequest(localAddress))
.body;
applicationViews[appIdentifier] = JSON.parse(apps);
}
}
catch (err) {
this.$logger.trace(`Error while checking ${localAddress}. Error is: ${err.message}`);
}
}));
return applicationViews;
}
getFullyQualifiedActivityRegex(appIdentifier) {
const packageActivitySeparator = "\\/";
const fullJavaClassName = "([a-zA-Z_0-9]*\\.)*[A-Z_$]($[A-Z_$]|[$_\\w_])*";
return new RegExp(`${(0, helpers_1.regExpEscape)(appIdentifier)}${packageActivitySeparator}${fullJavaClassName}`, `m`);
}
hasValidApksFile(aabFilaPath, apksFilePath) {
let isValid = false;
if (this.$fs.exists(apksFilePath)) {
const lastUpdatedApks = this.$fs.getFsStats(apksFilePath).ctime.getTime();
const lastUpdatedAab = this.$fs.getFsStats(aabFilaPath).ctime.getTime();
isValid = lastUpdatedApks >= lastUpdatedAab;
}
return isValid;
}
}
exports.AndroidApplicationManager = AndroidApplicationManager;
__decorate([
(0, helpers_1.hook)("install")
], AndroidApplicationManager.prototype, "installApplication", null);
__decorate([
(0, decorators_1.cache)()
], AndroidApplicationManager.prototype, "getFullyQualifiedActivityRegex", null);
//# sourceMappingURL=android-application-manager.js.map
;