add: build apk from js file
This commit is contained in:
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "com.stardust.scriptdroid"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 23
|
||||
versionCode 210
|
||||
versionName "3.0.0 Alpha11"
|
||||
versionCode 211
|
||||
versionName "3.0.0 Alpha12"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
ndk {
|
||||
@@ -121,6 +121,8 @@ dependencies {
|
||||
compile 'com.wang.avi:library:2.1.3'
|
||||
compile 'org.apache.commons:commons-lang3:3.5'
|
||||
compile 'com.github.hyb1996:FloatingCircularActionMenu:0.0.2'
|
||||
compile(name: 'apkbuilder-release', ext: 'aar')
|
||||
|
||||
// RxJava
|
||||
compile "io.reactivex.rxjava2:rxjava:2.1.0"
|
||||
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.stardust.scriptdroid.autojs.build;
|
||||
|
||||
import com.stardust.autojs.apkbuilder.ApkBuilder;
|
||||
import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.ui.build.BuildActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/10/24.
|
||||
*/
|
||||
|
||||
public class AutoJsApkBuilder extends ApkBuilder {
|
||||
|
||||
|
||||
public interface ProgressCallback {
|
||||
void onPrepare(AutoJsApkBuilder builder);
|
||||
|
||||
void onBuild(AutoJsApkBuilder builder);
|
||||
|
||||
void onSign(AutoJsApkBuilder builder);
|
||||
|
||||
void onClean(AutoJsApkBuilder builder);
|
||||
|
||||
}
|
||||
|
||||
public static class AppConfig {
|
||||
String appName;
|
||||
String versionName;
|
||||
int versionCode;
|
||||
String jsPath;
|
||||
|
||||
public AppConfig(String appName, String versionName, int versionCode, String jsPath) {
|
||||
this.appName = appName;
|
||||
this.versionName = versionName;
|
||||
this.versionCode = versionCode;
|
||||
this.jsPath = jsPath;
|
||||
}
|
||||
}
|
||||
|
||||
private ProgressCallback mProgressCallback;
|
||||
|
||||
public AutoJsApkBuilder(InputStream apkInputStream, File outApkFile, String workspacePath) {
|
||||
super(apkInputStream, outApkFile, workspacePath);
|
||||
}
|
||||
|
||||
public AutoJsApkBuilder(File inFile, File outFile, String workspacePath) throws FileNotFoundException {
|
||||
super(inFile, outFile, workspacePath);
|
||||
}
|
||||
|
||||
public AutoJsApkBuilder setProgressCallback(ProgressCallback callback) {
|
||||
mProgressCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoJsApkBuilder prepare() throws IOException {
|
||||
if (mProgressCallback != null) {
|
||||
App.getApp().getUiHandler().post(() -> mProgressCallback.onPrepare(AutoJsApkBuilder.this));
|
||||
}
|
||||
return (AutoJsApkBuilder) super.prepare();
|
||||
}
|
||||
|
||||
public AutoJsApkBuilder setScriptFile(String path) throws IOException {
|
||||
replaceFile("assets/script.js", path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AutoJsApkBuilder withConfig(AppConfig config) throws IOException {
|
||||
editManifest().setAppName(config.appName)
|
||||
.setVersionName(config.versionName)
|
||||
.setVersionCode(config.versionCode)
|
||||
.commit();
|
||||
setScriptFile(config.jsPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoJsApkBuilder build() throws IOException {
|
||||
if (mProgressCallback != null) {
|
||||
App.getApp().getUiHandler().post(() -> mProgressCallback.onBuild(AutoJsApkBuilder.this));
|
||||
}
|
||||
return (AutoJsApkBuilder) super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoJsApkBuilder sign() throws Exception {
|
||||
if (mProgressCallback != null) {
|
||||
App.getApp().getUiHandler().post(() -> mProgressCallback.onSign(AutoJsApkBuilder.this));
|
||||
}
|
||||
return (AutoJsApkBuilder) super.sign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoJsApkBuilder cleanWorkspace() {
|
||||
if (mProgressCallback != null) {
|
||||
App.getApp().getUiHandler().post(() -> mProgressCallback.onClean(AutoJsApkBuilder.this));
|
||||
}
|
||||
return (AutoJsApkBuilder) super.cleanWorkspace();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,46 @@
|
||||
package com.stardust.scriptdroid.ui.build;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.stardust.autojs.apkbuilder.ApkBuilder;
|
||||
import com.stardust.pio.PFiles;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.autojs.build.AutoJsApkBuilder;
|
||||
import com.stardust.scriptdroid.io.StorageFileProvider;
|
||||
import com.stardust.scriptdroid.model.script.ScriptFile;
|
||||
import com.stardust.scriptdroid.ui.BaseActivity;
|
||||
import com.stardust.scriptdroid.ui.filechooser.FileChooserDialogBuilder;
|
||||
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
|
||||
import com.stardust.util.IntentUtil;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Click;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import butterknife.OnClick;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/10/22.
|
||||
*/
|
||||
@EActivity(R.layout.activity_build)
|
||||
public class BuildActivity extends BaseActivity {
|
||||
public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.ProgressCallback {
|
||||
|
||||
public static final String EXTRA_SOURCE_FILE = BuildActivity.class.getName() + ".extra_source_file";
|
||||
|
||||
private static final String LOG_TAG = "BuildActivity";
|
||||
|
||||
@ViewById(R.id.source_path)
|
||||
EditText mSourcePath;
|
||||
|
||||
@@ -36,6 +58,7 @@ public class BuildActivity extends BaseActivity {
|
||||
|
||||
@ViewById(R.id.version_code)
|
||||
EditText mVersionCode;
|
||||
private MaterialDialog mProgressDialog;
|
||||
|
||||
@AfterViews
|
||||
void setupViews() {
|
||||
@@ -47,11 +70,110 @@ public class BuildActivity extends BaseActivity {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void setupWithSourceFile(ScriptFile file) {
|
||||
mSourcePath.setText(file.getPath());
|
||||
mOutputPath.setText(file.getParent());
|
||||
mAppName.setText(file.getSimplifiedName());
|
||||
mPackageName.setText("org.autojs.example_" + file.getSimplifiedName());
|
||||
}
|
||||
|
||||
@Click(R.id.select_source)
|
||||
void selectSourceFilePath() {
|
||||
String initialDir = new File(mSourcePath.getText().toString()).getParent();
|
||||
new FileChooserDialogBuilder(this)
|
||||
.title(R.string.text_source_file_path)
|
||||
.dir(initialDir == null ? StorageFileProvider.DEFAULT_DIRECTORY_PATH : initialDir)
|
||||
.justScriptFile()
|
||||
.singleChoice(file -> mSourcePath.setText(file.getPath()))
|
||||
.show();
|
||||
}
|
||||
|
||||
|
||||
@Click(R.id.select_output)
|
||||
void selectOutputDirPath() {
|
||||
String initialDir = new File(mOutputPath.getText().toString()).exists() ?
|
||||
mOutputPath.getText().toString() : StorageFileProvider.DEFAULT_DIRECTORY_PATH;
|
||||
new FileChooserDialogBuilder(this)
|
||||
.title(R.string.text_source_file_path)
|
||||
.dir(initialDir)
|
||||
.chooseDir()
|
||||
.singleChoice(dir -> mOutputPath.setText(dir.getPath()))
|
||||
.show();
|
||||
}
|
||||
|
||||
@Click(R.id.fab)
|
||||
void buildApk() {
|
||||
String jsPath = mSourcePath.getText().toString();
|
||||
String versionName = mVersionName.getText().toString();
|
||||
int versionCode = Integer.parseInt(mVersionCode.getText().toString());
|
||||
String appName = mAppName.getText().toString();
|
||||
File tmpDir = new File(getCacheDir(), "build/");
|
||||
File outApk = new File(mOutputPath.getText().toString(),
|
||||
String.format("%s_v%s.apk", appName, versionName));
|
||||
showProgressDialog();
|
||||
Observable.fromCallable(() ->
|
||||
new AutoJsApkBuilder(getAssets().open("template.apk"), outApk, tmpDir.getPath())
|
||||
.setProgressCallback(BuildActivity.this)
|
||||
.prepare()
|
||||
.withConfig(new AutoJsApkBuilder.AppConfig(appName, versionName, versionCode, jsPath))
|
||||
.build()
|
||||
.sign()
|
||||
.cleanWorkspace()
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(apkBuilder -> onBuildSuccessful(outApk),
|
||||
this::onBuildFailed);
|
||||
|
||||
}
|
||||
|
||||
private void showProgressDialog() {
|
||||
mProgressDialog = new MaterialDialog.Builder(this)
|
||||
.progress(true, 100)
|
||||
.content(R.string.text_on_progress)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void onBuildFailed(Throwable error) {
|
||||
mProgressDialog.dismiss();
|
||||
mProgressDialog = null;
|
||||
Toast.makeText(this, getString(R.string.text_build_failed) + error.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
Log.e(LOG_TAG, "Build failed", error);
|
||||
}
|
||||
|
||||
private void onBuildSuccessful(File outApk) {
|
||||
mProgressDialog.dismiss();
|
||||
mProgressDialog = null;
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.text_build_successfully)
|
||||
.content(getString(R.string.format_build_successfully, outApk.getPath()))
|
||||
.positiveText(R.string.text_install)
|
||||
.negativeText(R.string.cancel)
|
||||
.onPositive((dialog, which) ->
|
||||
IntentUtil.installApk(BuildActivity.this, outApk.getPath())
|
||||
)
|
||||
.show();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepare(AutoJsApkBuilder builder) {
|
||||
mProgressDialog.setContent(R.string.apk_builder_prepare);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuild(AutoJsApkBuilder builder) {
|
||||
mProgressDialog.setContent(R.string.apk_builder_build);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSign(AutoJsApkBuilder builder) {
|
||||
mProgressDialog.setContent(R.string.apk_builder_package);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClean(AutoJsApkBuilder builder) {
|
||||
mProgressDialog.setContent(R.string.apk_builder_clean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="文件"
|
||||
android:text="@string/text_file"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?android:textColorSecondary"/>
|
||||
|
||||
@@ -69,8 +69,7 @@
|
||||
android:id="@+id/source_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="脚本文件路径"
|
||||
android:maxLines="1"/>
|
||||
android:hint="@string/text_source_file_path"/>
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
@@ -99,8 +98,7 @@
|
||||
android:id="@+id/output_path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/text_output_apk_path"
|
||||
android:maxLines="1"/>
|
||||
android:hint="@string/text_output_apk_path"/>
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
@@ -185,6 +183,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/text_version_code"
|
||||
android:inputType="number"
|
||||
android:text="1"/>
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -247,12 +247,21 @@
|
||||
<string name="text_select_file_to_import">选择要导入的文件</string>
|
||||
<string name="text_build_apk">打包应用</string>
|
||||
<string name="text_select">选择</string>
|
||||
<string name="text_output_apk_path">打包应用保存路径</string>
|
||||
<string name="text_output_apk_path">打包应用保存位置</string>
|
||||
<string name="text_config">配置</string>
|
||||
<string name="text_app_name">应用名称</string>
|
||||
<string name="text_package_name">包名</string>
|
||||
<string name="text_version_name">版本名称</string>
|
||||
<string name="text_version_code">版本号</string>
|
||||
<string name="text_build_successfully">打包成功</string>
|
||||
<string name="text_install">安装</string>
|
||||
<string name="text_build_failed">打包失败,错误信息:</string>
|
||||
<string name="format_build_successfully" formatted="true">apk文件已保存到 %s</string>
|
||||
<string name="apk_builder_prepare">准备文件</string>
|
||||
<string name="apk_builder_build">构建中</string>
|
||||
<string name="apk_builder_package">打包中</string>
|
||||
<string name="apk_builder_clean">清理临时文件</string>
|
||||
<string name="text_source_file_path">脚本文件路径</string>
|
||||
|
||||
|
||||
<string-array name="ad_showing_mode_keys">
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.stardust.autojs.runtime.api.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@@ -11,10 +12,19 @@ import com.stardust.autojs.runtime.api.ui.widget.JsFrameLayout;
|
||||
import com.stardust.autojs.runtime.api.ui.xml.XmlConverter;
|
||||
import com.stardust.util.MapEntries;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/14.
|
||||
*/
|
||||
@@ -33,7 +43,7 @@ public class ConvertLayoutInflater implements JsLayoutInflater {
|
||||
.entry("password", InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT)
|
||||
.entry("email", InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_CLASS_TEXT)
|
||||
.entry("date", InputType.TYPE_DATETIME_VARIATION_DATE | InputType.TYPE_CLASS_DATETIME)
|
||||
.entry("time", InputType.TYPE_DATETIME_VARIATION_TIME| InputType.TYPE_CLASS_DATETIME)
|
||||
.entry("time", InputType.TYPE_DATETIME_VARIATION_TIME | InputType.TYPE_CLASS_DATETIME)
|
||||
.map();
|
||||
|
||||
@Override
|
||||
@@ -56,6 +66,7 @@ public class ConvertLayoutInflater implements JsLayoutInflater {
|
||||
|
||||
@Override
|
||||
public View inflate(Context context, String xml) {
|
||||
|
||||
try {
|
||||
String androidLayoutXml = XmlConverter.convertToAndroidLayout(xml);
|
||||
JsFrameLayout root = new JsFrameLayout(context);
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
#Mon Jan 23 10:16:46 CST 2017
|
||||
systemProp.https.proxyPort=1080
|
||||
systemProp.http.proxyHost=127.0.0.1
|
||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xms8192m -Xmx8192m -Dfile.encoding=UTF-8
|
||||
systemProp.https.proxyHost=127.0.0.1
|
||||
systemProp.http.proxyPort=1080
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.stardust.auojs.inrt">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:label="inrt"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
tools:replace="android:label">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.stardust.auojs.inrt;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -14,6 +16,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
checkPermissions();
|
||||
try {
|
||||
InputStream is = getAssets().open("script.js");
|
||||
byte[] data = new byte[is.available()];
|
||||
@@ -25,4 +28,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 11124);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user