add: build apk from js file

This commit is contained in:
hyb1996
2017-10-24 15:49:45 +08:00
parent 448d39b7e8
commit 9077e4928d
9 changed files with 275 additions and 16 deletions

View File

@@ -10,8 +10,8 @@ android {
applicationId "com.stardust.scriptdroid" applicationId "com.stardust.scriptdroid"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 23 targetSdkVersion 23
versionCode 210 versionCode 211
versionName "3.0.0 Alpha11" versionName "3.0.0 Alpha12"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
ndk { ndk {
@@ -121,6 +121,8 @@ dependencies {
compile 'com.wang.avi:library:2.1.3' compile 'com.wang.avi:library:2.1.3'
compile 'org.apache.commons:commons-lang3:3.5' compile 'org.apache.commons:commons-lang3:3.5'
compile 'com.github.hyb1996:FloatingCircularActionMenu:0.0.2' compile 'com.github.hyb1996:FloatingCircularActionMenu:0.0.2'
compile(name: 'apkbuilder-release', ext: 'aar')
// RxJava // RxJava
compile "io.reactivex.rxjava2:rxjava:2.1.0" compile "io.reactivex.rxjava2:rxjava:2.1.0"
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

View File

@@ -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();
}
}

View File

@@ -1,24 +1,46 @@
package com.stardust.scriptdroid.ui.build; package com.stardust.scriptdroid.ui.build;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.util.Log;
import android.widget.EditText; 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.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.model.script.ScriptFile;
import com.stardust.scriptdroid.ui.BaseActivity; 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.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById; 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. * Created by Stardust on 2017/10/22.
*/ */
@EActivity(R.layout.activity_build) @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"; 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) @ViewById(R.id.source_path)
EditText mSourcePath; EditText mSourcePath;
@@ -36,6 +58,7 @@ public class BuildActivity extends BaseActivity {
@ViewById(R.id.version_code) @ViewById(R.id.version_code)
EditText mVersionCode; EditText mVersionCode;
private MaterialDialog mProgressDialog;
@AfterViews @AfterViews
void setupViews() { void setupViews() {
@@ -47,11 +70,110 @@ public class BuildActivity extends BaseActivity {
} }
@SuppressLint("SetTextI18n")
private void setupWithSourceFile(ScriptFile file) { private void setupWithSourceFile(ScriptFile file) {
mSourcePath.setText(file.getPath()); mSourcePath.setText(file.getPath());
mOutputPath.setText(file.getParent()); mOutputPath.setText(file.getParent());
mAppName.setText(file.getSimplifiedName()); 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);
} }
} }

View File

@@ -51,7 +51,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
android:text="文件" android:text="@string/text_file"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="?android:textColorSecondary"/> android:textColor="?android:textColorSecondary"/>
@@ -69,8 +69,7 @@
android:id="@+id/source_path" android:id="@+id/source_path"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="脚本文件路径" android:hint="@string/text_source_file_path"/>
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@@ -99,8 +98,7 @@
android:id="@+id/output_path" android:id="@+id/output_path"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/text_output_apk_path" android:hint="@string/text_output_apk_path"/>
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@@ -185,6 +183,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/text_version_code" android:hint="@string/text_version_code"
android:inputType="number"
android:text="1"/> android:text="1"/>
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
</LinearLayout> </LinearLayout>

View File

@@ -247,12 +247,21 @@
<string name="text_select_file_to_import">选择要导入的文件</string> <string name="text_select_file_to_import">选择要导入的文件</string>
<string name="text_build_apk">打包应用</string> <string name="text_build_apk">打包应用</string>
<string name="text_select">选择</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_config">配置</string>
<string name="text_app_name">应用名称</string> <string name="text_app_name">应用名称</string>
<string name="text_package_name">包名</string> <string name="text_package_name">包名</string>
<string name="text_version_name">版本名称</string> <string name="text_version_name">版本名称</string>
<string name="text_version_code">版本号</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"> <string-array name="ad_showing_mode_keys">

View File

@@ -2,6 +2,7 @@ package com.stardust.autojs.runtime.api.ui;
import android.content.Context; import android.content.Context;
import android.text.InputType; import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; 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.autojs.runtime.api.ui.xml.XmlConverter;
import com.stardust.util.MapEntries; 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.lang.reflect.Field;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
/** /**
* Created by Stardust on 2017/5/14. * Created by Stardust on 2017/5/14.
*/ */
@@ -33,10 +43,10 @@ public class ConvertLayoutInflater implements JsLayoutInflater {
.entry("password", InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT) .entry("password", InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT)
.entry("email", InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | 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("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(); .map();
@Override @Override
public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) { public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
if (!(view instanceof TextView)) if (!(view instanceof TextView))
return; return;
@@ -56,6 +66,7 @@ public class ConvertLayoutInflater implements JsLayoutInflater {
@Override @Override
public View inflate(Context context, String xml) { public View inflate(Context context, String xml) {
try { try {
String androidLayoutXml = XmlConverter.convertToAndroidLayout(xml); String androidLayoutXml = XmlConverter.convertToAndroidLayout(xml);
JsFrameLayout root = new JsFrameLayout(context); JsFrameLayout root = new JsFrameLayout(context);

View File

@@ -15,6 +15,6 @@
#Mon Jan 23 10:16:46 CST 2017 #Mon Jan 23 10:16:46 CST 2017
systemProp.https.proxyPort=1080 systemProp.https.proxyPort=1080
systemProp.http.proxyHost=127.0.0.1 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.https.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080 systemProp.http.proxyPort=1080

View File

@@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.stardust.auojs.inrt"> package="com.stardust.auojs.inrt">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="inrt"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:replace="android:label">
<activity android:name=".MainActivity"> <activity android:name=".MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

View File

@@ -1,5 +1,7 @@
package com.stardust.auojs.inrt; package com.stardust.auojs.inrt;
import android.Manifest;
import android.os.Build;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
@@ -14,6 +16,7 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
checkPermissions();
try { try {
InputStream is = getAssets().open("script.js"); InputStream is = getAssets().open("script.js");
byte[] data = new byte[is.available()]; byte[] data = new byte[is.available()];
@@ -25,4 +28,10 @@ public class MainActivity extends AppCompatActivity {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void checkPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 11124);
}
}
} }