#!/usr/bin/php
<?
set_time_limit(false);
$script_dir = dirname(__FILE__);
$keystore_dir = $script_dir . '/keystores/';
if(!is_dir($keystore_dir)) {
mkdir($keystore_dir);
}
function getSourceDirs($dir, &$results = []) {
$files = scandir($dir);
foreach($files as $key => $value){
$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
if(!is_dir($path)) {
$valid_path = str_replace(realpath('.') . '/', '', dirname($path) . '/*.java');
if(preg_match('/\.java$/', $path) && !in_array($valid_path, $results)) {
$results[] = $valid_path;
}
} else if($value != "." && $value != "..") {
getSourceDirs($path, $results);
}
}
return $results;
}
switch(@$argv[1]) {
case 'create':
$name = basename(trim(readline('Project name: ')));
$package = trim(readline('Package name: '));
if(empty($name) || empty($package)) {
die("Project name or package is empty!!!\n");
}
if(is_dir($name)) {
die("Directory '{$name}' is exists!\n");
}
$src_dir = $name . '/src/' . str_replace('.', '/', $package);
mkdir($name . '/bin', 0777, true);
mkdir($name . '/obj');
mkdir($name . '/res/values', 0777, true);
mkdir($name . '/res/layout');
mkdir($name . '/res/drawable');
mkdir($src_dir, 0777, true);
file_put_contents($name . '/AndroidManifest.xml', str_replace('__PACKAGE__', $package, file_get_contents($script_dir . '/res/AndroidManifest.xml')));
file_put_contents($name . '/res/values/strings.xml', str_replace('__LABEL__', $name, file_get_contents($script_dir . '/res/strings.xml')));
file_put_contents($src_dir . '/MainActivity.java', str_replace('__PACKAGE__', $package, file_get_contents($script_dir . '/res/MainActivity.java')));
copy($script_dir . '/res/activity_main.xml', $name . '/res/layout/activity_main.xml');
echo "Project '{$name}' successfully created! :-)\n";
break;
case 'build':
if(!isset($_SERVER['ANDROID_HOME'])) {
die("ANDROID_HOME must be inicialized!\n");
}
if(!file_exists('AndroidManifest.xml')) {
die("AndroidManifest.xml not found!\n");
}
$sdk_home = $_SERVER['ANDROID_HOME'];
$build_tools = $sdk_home . '/build-tools/29.0.1';
$platform_home = $sdk_home . '/platforms/android-28';
if(!preg_match('/package="(.*?)"/', file_get_contents('AndroidManifest.xml'), $package)) {
die("Package name is required in AndroidManifest.xml!\n");
}
$package = $package[1];
echo "Package = '{$package}'\n";
$keystore = basename(trim(readline('Keystore = ')));
$kst = scandir($keystore_dir);
foreach($kst as $files) {
if(preg_match("/{$keystore}_([A-z0-9]+)\.keystore/", $files, $matches)) {
$alias = $matches[1];
}
}
if(!isset($alias)) {
die("Keystore not found!\n");
} else {
echo "Keystore alias = {$alias}\n";
}
$storepass = readline('Store password [android] = ');
if(empty($storepass)) {
$storepass = 'android';
}
$keypass = readline('Key password [android] = ');
if(empty($keypass)) {
$keypass = 'android';
}
echo "Generating 'R.java'... ";
passthru("{$build_tools}/aapt package -f -m -S res -J src -M AndroidManifest.xml -I {$platform_home}/android.jar", $code);
if($code == 0) {
echo "Success!\n";
} else {
exit;
}
$source_dirs = implode(' ', getSourceDirs('src'));
if(empty($source_dirs)) {
die("Java sources not found!\n");
}
echo "Compiling java sources... ";
exec('rm -rf obj/*');
passthru("javac -source 7 -target 7 -Xlint:-options -d obj -cp {$platform_home}/android.jar -sourcepath src {$source_dirs}", $code);
if($code == 0) {
echo "Success!\n";
} else {
exit;
}
echo "Converting classes to dex... ";
if(file_exists('bin/classes.dex')) {
unlink('bin/classes.dex');
}
passthru("{$build_tools}/dx --dex --output=bin/classes.dex obj/", $code);
if($code == 0) {
echo "Success!\n";
} else {
exit;
}
echo "Building APK... ";
if(file_exists("bin/{$package}_unsigned.apk")) {
unlink("bin/{$package}_unsigned.apk");
}
if(file_exists("bin/{$package}_signed.apk")) {
unlink("bin/{$package}_signed.apk");
}
passthru("{$build_tools}/aapt package -f -M AndroidManifest.xml -S res -I {$platform_home}/android.jar -F bin/{$package}_unsigned.apk bin", $code);
if($code == 0) {
echo "Success!\n";
} else {
exit;
}
echo "Signing APK... ";
ob_start();
passthru("jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore '{$keystore_dir}/{$keystore}_{$alias}.keystore' -storepass {$storepass} -keypass {$keypass} -signedjar bin/{$package}_signed.apk bin/{$package}_unsigned.apk {$alias}");
$jarsigner_result = explode("\n", ob_get_contents())[0];
ob_end_clean();
$jarsigner_result = str_replace('jar', 'apk', $jarsigner_result);
echo $jarsigner_result . "\n";
break;
case 'createKeystore':
$keystore = basename(trim(readline('Keystore name = ')));
if(empty($keystore)) {
die("Keystore name is empty!\n");
}
$alias = trim(readline('Alias [androiddebugkey] = '));
if(empty($alias)) {
$alias = 'androiddebugkey';
}
$storepass = readline('Store password [android] = ');
if(empty($storepass)) {
$storepass = 'android';
}
$keypass = readline('Key password [android] = ');
if(empty($keypass)) {
$keypass = 'android';
}
$validity = intval(readline('Valid days [10000] = '));
if($validity == 0) {
$validity = 10000;
}
$keysize = intval(readline('Key size [2048] = '));
if($keysize == 0) {
$keysize = 2048;
}
passthru("keytool -genkey -validity {$validity} -keystore {$keystore_dir}/{$keystore}_{$alias}.keystore -storepass {$storepass} -keypass {$keypass} -alias {$alias} -keyalg RSA -v -keysize {$keysize}");
break;
case 'listKeystores':
echo "All founded keystores:\n";
$keystores = scandir($keystore_dir);
$founded = [];
foreach($keystores as $keystore) {
if(preg_match('/\.keystore$/', $keystore)) {
$founded[] = preg_replace('/_[A-z0-9]+\.keystore/', '', $keystore);
}
}
if(count($founded) === 0) {
echo "\t Not found keystores!\n";
} else {
foreach($founded as $pos => $ks) {
echo "\t" . ($pos + 1) . ". {$ks}\n";
}
}
break;
case '--help':
default:
echo "Usage: androed [COMMAND]\n";
echo "All commands:\n";
echo "\tcreate = create project\n";
echo "\tbuild = build project\n";
echo "\tcreateKeystore = create new keystore\n";
echo "\tlistKeystores = list all keystores\n";
echo "\t--help = show this message\n";
break;
}