修复 打包应用app.viewFile()等涉及文件被其他应用打开时报错的问题

This commit is contained in:
hyb1996
2018-11-11 22:22:50 +08:00
parent b805cefeac
commit 821fd35c9b
4 changed files with 129 additions and 71 deletions

View File

@@ -4,7 +4,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import com.stardust.app.GlobalAppContext; import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.apkbuilder.ApkBuilder; import com.stardust.autojs.apkbuilder.ApkPackager;
import com.stardust.autojs.apkbuilder.ManifestEditor; import com.stardust.autojs.apkbuilder.ManifestEditor;
import com.stardust.autojs.apkbuilder.util.StreamUtils; import com.stardust.autojs.apkbuilder.util.StreamUtils;
import com.stardust.autojs.project.BuildInfo; import com.stardust.autojs.project.BuildInfo;
@@ -13,6 +13,7 @@ import com.stardust.pio.PFiles;
import org.autojs.autojs.build.TinySign; import org.autojs.autojs.build.TinySign;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -22,23 +23,27 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import pxb.android.StringItem;
import pxb.android.axml.AxmlWriter;
import zhao.arsceditor.ResDecoder.ARSCDecoder;
import zhao.arsceditor.ResDecoder.data.ResTable;
/** /**
* Created by Stardust on 2017/10/24. * Created by Stardust on 2017/10/24.
*/ */
public class AutoJsApkBuilder extends ApkBuilder { public class ApkBuilder {
public interface ProgressCallback { public interface ProgressCallback {
void onPrepare(AutoJsApkBuilder builder); void onPrepare(ApkBuilder builder);
void onBuild(AutoJsApkBuilder builder); void onBuild(ApkBuilder builder);
void onSign(AutoJsApkBuilder builder); void onSign(ApkBuilder builder);
void onClean(AutoJsApkBuilder builder); void onClean(ApkBuilder builder);
} }
@@ -73,38 +78,38 @@ public class AutoJsApkBuilder extends ApkBuilder {
} }
public AppConfig setAppName(String appName) { public AppConfig setAppName(String appName) {
this.appName = appName; appName = appName;
return this; return this;
} }
public AppConfig setVersionName(String versionName) { public AppConfig setVersionName(String versionName) {
this.versionName = versionName; versionName = versionName;
return this; return this;
} }
public AppConfig setVersionCode(int versionCode) { public AppConfig setVersionCode(int versionCode) {
this.versionCode = versionCode; versionCode = versionCode;
return this; return this;
} }
public AppConfig setSourcePath(String sourcePath) { public AppConfig setSourcePath(String sourcePath) {
this.sourcePath = sourcePath; sourcePath = sourcePath;
return this; return this;
} }
public AppConfig setPackageName(String packageName) { public AppConfig setPackageName(String packageName) {
this.packageName = packageName; packageName = packageName;
return this; return this;
} }
public AppConfig setIcon(Callable<Bitmap> icon) { public AppConfig setIcon(Callable<Bitmap> icon) {
this.icon = icon; icon = icon;
return this; return this;
} }
public AppConfig setIcon(String iconPath) { public AppConfig setIcon(String iconPath) {
this.icon = () -> BitmapFactory.decodeFile(iconPath); icon = () -> BitmapFactory.decodeFile(iconPath);
return this; return this;
} }
@@ -130,39 +135,36 @@ public class AutoJsApkBuilder extends ApkBuilder {
} }
private ProgressCallback mProgressCallback; private ProgressCallback mProgressCallback;
private ApkPackager mApkPackager;
private String mArscPackageName;
private ManifestEditor mManifestEditor; private ManifestEditor mManifestEditor;
private String mWorkspacePath; private String mWorkspacePath;
private AppConfig mAppConfig; private AppConfig mAppConfig;
private final File mOutApkFile; private final File mOutApkFile;
public AutoJsApkBuilder(InputStream apkInputStream, File outApkFile, String workspacePath) { public ApkBuilder(InputStream apkInputStream, File outApkFile, String workspacePath) {
super(apkInputStream, outApkFile, workspacePath);
mWorkspacePath = workspacePath; mWorkspacePath = workspacePath;
mOutApkFile = outApkFile; mOutApkFile = outApkFile;
mApkPackager = new ApkPackager(apkInputStream, mWorkspacePath);
PFiles.ensureDir(outApkFile.getPath()); PFiles.ensureDir(outApkFile.getPath());
} }
public AutoJsApkBuilder(File inFile, File outFile, String workspacePath) throws FileNotFoundException { public ApkBuilder setProgressCallback(ProgressCallback callback) {
super(inFile, outFile, workspacePath);
mOutApkFile = outFile;
mWorkspacePath = workspacePath;
}
public AutoJsApkBuilder setProgressCallback(ProgressCallback callback) {
mProgressCallback = callback; mProgressCallback = callback;
return this; return this;
} }
@Override public ApkBuilder prepare() throws IOException {
public AutoJsApkBuilder prepare() throws IOException {
if (mProgressCallback != null) { if (mProgressCallback != null) {
GlobalAppContext.post(() -> mProgressCallback.onPrepare(AutoJsApkBuilder.this)); GlobalAppContext.post(() -> mProgressCallback.onPrepare(ApkBuilder.this));
} }
return (AutoJsApkBuilder) super.prepare(); (new File(mWorkspacePath)).mkdirs();
mApkPackager.unzip();
return this;
} }
public AutoJsApkBuilder setScriptFile(String path) throws IOException { public ApkBuilder setScriptFile(String path) throws IOException {
if (PFiles.isDir(path)) { if (PFiles.isDir(path)) {
copyDir("assets/project/", path); copyDir("assets/project/", path);
} else { } else {
@@ -173,7 +175,7 @@ public class AutoJsApkBuilder extends ApkBuilder {
public void copyDir(String relativePath, String path) throws IOException { public void copyDir(String relativePath, String path) throws IOException {
File fromDir = new File(path); File fromDir = new File(path);
File toDir = new File(this.mWorkspacePath, relativePath); File toDir = new File(mWorkspacePath, relativePath);
toDir.mkdir(); toDir.mkdir();
for (File child : fromDir.listFiles()) { for (File child : fromDir.listFiles()) {
if (child.isFile()) { if (child.isFile()) {
@@ -187,13 +189,12 @@ public class AutoJsApkBuilder extends ApkBuilder {
} }
} }
@Override
public ApkBuilder replaceFile(String relativePath, String newFilePath) throws IOException { public ApkBuilder replaceFile(String relativePath, String newFilePath) throws IOException {
StreamUtils.write(new FileInputStream(newFilePath), new FileOutputStream(new File(this.mWorkspacePath, relativePath))); StreamUtils.write(new FileInputStream(newFilePath), new FileOutputStream(new File(mWorkspacePath, relativePath)));
return this; return this;
} }
public AutoJsApkBuilder withConfig(AppConfig config) throws IOException { public ApkBuilder withConfig(AppConfig config) throws IOException {
mAppConfig = config; mAppConfig = config;
mManifestEditor = editManifest() mManifestEditor = editManifest()
.setAppName(config.appName) .setAppName(config.appName)
@@ -206,6 +207,15 @@ public class AutoJsApkBuilder extends ApkBuilder {
return this; return this;
} }
public ManifestEditor editManifest() throws FileNotFoundException {
mManifestEditor = new ManifestEditorWithAuthorities(new FileInputStream(getManifestFile()));
return mManifestEditor;
}
protected File getManifestFile() {
return new File(mWorkspacePath, "AndroidManifest.xml");
}
private void updateProjectConfig(AppConfig config) { private void updateProjectConfig(AppConfig config) {
if (!PFiles.isDir(config.sourcePath)) { if (!PFiles.isDir(config.sourcePath)) {
return; return;
@@ -213,29 +223,12 @@ public class AutoJsApkBuilder extends ApkBuilder {
ProjectConfig projectConfig = ProjectConfig.fromProjectDir(config.sourcePath); ProjectConfig projectConfig = ProjectConfig.fromProjectDir(config.sourcePath);
long buildNumber = projectConfig.getBuildInfo().getBuildNumber(); long buildNumber = projectConfig.getBuildInfo().getBuildNumber();
projectConfig.setBuildInfo(BuildInfo.generate(buildNumber + 1)); projectConfig.setBuildInfo(BuildInfo.generate(buildNumber + 1));
//updateProjectConfigAssets(projectConfig, config.sourcePath, config.sourcePath);
PFiles.write(ProjectConfig.configFileOfDir(config.sourcePath), projectConfig.toJson()); PFiles.write(ProjectConfig.configFileOfDir(config.sourcePath), projectConfig.toJson());
} }
private void updateProjectConfigAssets(ProjectConfig config, String projectDir, String dir) { public ApkBuilder build() throws IOException {
File main = new File(projectDir, config.getMainScriptFile());
for (File file : new File(dir).listFiles()) {
if (file.isDirectory()) {
updateProjectConfigAssets(config, projectDir, file.getPath());
continue;
}
if (file.equals(main)) {
continue;
}
String relative = new File(projectDir).toURI().relativize(file.toURI()).getPath();
config.addAsset(relative);
}
}
@Override
public AutoJsApkBuilder build() throws IOException {
if (mProgressCallback != null) { if (mProgressCallback != null) {
GlobalAppContext.post(() -> mProgressCallback.onBuild(AutoJsApkBuilder.this)); GlobalAppContext.post(() -> mProgressCallback.onBuild(ApkBuilder.this));
} }
mManifestEditor.commit(); mManifestEditor.commit();
if (mAppConfig.icon != null) { if (mAppConfig.icon != null) {
@@ -249,25 +242,75 @@ public class AutoJsApkBuilder extends ApkBuilder {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
return (AutoJsApkBuilder) super.build(); if (mManifestEditor != null) {
mManifestEditor.writeTo(new FileOutputStream(getManifestFile()));
}
if (mArscPackageName != null) {
buildArsc();
}
return this;
} }
@Override public ApkBuilder sign() throws Exception {
public AutoJsApkBuilder sign() throws Exception {
if (mProgressCallback != null) { if (mProgressCallback != null) {
GlobalAppContext.post(() -> mProgressCallback.onSign(AutoJsApkBuilder.this)); GlobalAppContext.post(() -> mProgressCallback.onSign(ApkBuilder.this));
} }
FileOutputStream fos = new FileOutputStream(mOutApkFile); FileOutputStream fos = new FileOutputStream(mOutApkFile);
TinySign.sign(new File(this.mWorkspacePath), fos); TinySign.sign(new File(mWorkspacePath), fos);
fos.close(); fos.close();
return this; return this;
} }
@Override public ApkBuilder cleanWorkspace() {
public AutoJsApkBuilder cleanWorkspace() {
if (mProgressCallback != null) { if (mProgressCallback != null) {
GlobalAppContext.post(() -> mProgressCallback.onClean(AutoJsApkBuilder.this)); GlobalAppContext.post(() -> mProgressCallback.onClean(ApkBuilder.this));
}
delete(new File(mWorkspacePath));
return this;
}
public ApkBuilder setArscPackageName(String packageName) throws IOException {
mArscPackageName = packageName;
return this;
}
private void buildArsc() throws IOException {
File oldArsc = new File(mWorkspacePath, "resources.arsc");
File newArsc = new File(mWorkspacePath, "resources.arsc.new");
ARSCDecoder decoder = new ARSCDecoder(new BufferedInputStream(new FileInputStream(oldArsc)), (ResTable) null, false);
FileOutputStream fos = new FileOutputStream(newArsc);
decoder.CloneArsc(fos, mArscPackageName, true);
oldArsc.delete();
newArsc.renameTo(oldArsc);
}
private void delete(File file) {
if (file.isFile()) {
file.delete();
} else {
File[] files = file.listFiles();
for (File child : files) {
delete(child);
}
file.delete();
}
}
private class ManifestEditorWithAuthorities extends ManifestEditor {
ManifestEditorWithAuthorities(InputStream manifestInputStream) {
super(manifestInputStream);
}
@Override
public void onAttr(AxmlWriter.Attr attr) {
if ("authorities".equals(attr.name.data) && attr.value instanceof StringItem) {
((StringItem) attr.value).data = mAppConfig.packageName + ".fileprovider";
} else {
super.onAttr(attr);
}
} }
return (AutoJsApkBuilder) super.cleanWorkspace();
} }
} }

View File

@@ -26,7 +26,7 @@ import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.ViewById;
import org.autojs.autojs.Pref; import org.autojs.autojs.Pref;
import org.autojs.autojs.R; import org.autojs.autojs.R;
import org.autojs.autojs.autojs.build.AutoJsApkBuilder; import org.autojs.autojs.autojs.build.ApkBuilder;
import org.autojs.autojs.build.ApkBuilderPluginHelper; import org.autojs.autojs.build.ApkBuilderPluginHelper;
import org.autojs.autojs.external.fileprovider.AppFileProvider; import org.autojs.autojs.external.fileprovider.AppFileProvider;
import org.autojs.autojs.model.script.ScriptFile; import org.autojs.autojs.model.script.ScriptFile;
@@ -51,7 +51,7 @@ 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 implements AutoJsApkBuilder.ProgressCallback { public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCallback {
private static final int REQUEST_CODE = 44401; private static final int REQUEST_CODE = 44401;
@@ -250,7 +250,7 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private void doBuildingApk() { private void doBuildingApk() {
AutoJsApkBuilder.AppConfig appConfig = createAppConfig(); ApkBuilder.AppConfig appConfig = createAppConfig();
File tmpDir = new File(getCacheDir(), "build/"); File tmpDir = new File(getCacheDir(), "build/");
File outApk = new File(mOutputPath.getText().toString(), File outApk = new File(mOutputPath.getText().toString(),
String.format("%s_v%s.apk", appConfig.getAppName(), appConfig.getVersionName())); String.format("%s_v%s.apk", appConfig.getAppName(), appConfig.getVersionName()));
@@ -262,16 +262,16 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog
this::onBuildFailed); this::onBuildFailed);
} }
private AutoJsApkBuilder.AppConfig createAppConfig() { private ApkBuilder.AppConfig createAppConfig() {
if (mProjectConfig != null) { if (mProjectConfig != null) {
return AutoJsApkBuilder.AppConfig.fromProjectConfig(mSource, mProjectConfig); return ApkBuilder.AppConfig.fromProjectConfig(mSource, mProjectConfig);
} }
String jsPath = mSourcePath.getText().toString(); String jsPath = mSourcePath.getText().toString();
String versionName = mVersionName.getText().toString(); String versionName = mVersionName.getText().toString();
int versionCode = Integer.parseInt(mVersionCode.getText().toString()); int versionCode = Integer.parseInt(mVersionCode.getText().toString());
String appName = mAppName.getText().toString(); String appName = mAppName.getText().toString();
String packageName = mPackageName.getText().toString(); String packageName = mPackageName.getText().toString();
return new AutoJsApkBuilder.AppConfig() return new ApkBuilder.AppConfig()
.setAppName(appName) .setAppName(appName)
.setSourcePath(jsPath) .setSourcePath(jsPath)
.setPackageName(packageName) .setPackageName(packageName)
@@ -282,9 +282,9 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog
); );
} }
private AutoJsApkBuilder callApkBuilder(File tmpDir, File outApk, AutoJsApkBuilder.AppConfig appConfig) throws Exception { private ApkBuilder callApkBuilder(File tmpDir, File outApk, ApkBuilder.AppConfig appConfig) throws Exception {
InputStream templateApk = ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this); InputStream templateApk = ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this);
return new AutoJsApkBuilder(templateApk, outApk, tmpDir.getPath()) return new ApkBuilder(templateApk, outApk, tmpDir.getPath())
.setProgressCallback(BuildActivity.this) .setProgressCallback(BuildActivity.this)
.prepare() .prepare()
.withConfig(appConfig) .withConfig(appConfig)
@@ -326,24 +326,24 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog
} }
@Override @Override
public void onPrepare(AutoJsApkBuilder builder) { public void onPrepare(ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_prepare); mProgressDialog.setContent(R.string.apk_builder_prepare);
} }
@Override @Override
public void onBuild(AutoJsApkBuilder builder) { public void onBuild(ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_build); mProgressDialog.setContent(R.string.apk_builder_build);
} }
@Override @Override
public void onSign(AutoJsApkBuilder builder) { public void onSign(ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_package); mProgressDialog.setContent(R.string.apk_builder_package);
} }
@Override @Override
public void onClean(AutoJsApkBuilder builder) { public void onClean(ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_clean); mProgressDialog.setContent(R.string.apk_builder_clean);
} }

View File

@@ -66,6 +66,15 @@
<action android:name="android.service.notification.NotificationListenerService"/> <action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter> </intent-filter>
</service> </service>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.autojs.autojs.inrt.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
<files-path name="project" path="project"/>
<root-path name="root_files" path="." />
</paths>