modify logic
This commit is contained in:
parent
c3014f05de
commit
a6c21a83a8
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -72,9 +72,9 @@
|
|||
},
|
||||
{
|
||||
"targetPath": "assemblies/",
|
||||
"assetPath": "assemblies/assemblies.blob",
|
||||
"assetPath": "assemblies/assemblies",
|
||||
"origin": 1,
|
||||
"isXABB": true,
|
||||
"isXABA": true,
|
||||
"compression": 0,
|
||||
"external": false
|
||||
}
|
||||
|
|
|
@ -15,4 +15,6 @@ public class ManifestPatchConstants {
|
|||
public static final CharSequence APP_PACKAGE_NAME = "com.chucklefish.stardewvalley";
|
||||
|
||||
public static final String PATTERN_VERSION_AMAZON = "amazon";
|
||||
|
||||
public static final String PATTERN_EXTRACT_NATIVE_LIBS = "extractNativeLibs";
|
||||
}
|
||||
|
|
|
@ -50,4 +50,9 @@ public class ManifestEntry {
|
|||
*/
|
||||
@JsonProperty("isXALZ")
|
||||
private boolean isXALZ;
|
||||
/**
|
||||
* 是否为XABA压缩格式
|
||||
*/
|
||||
@JsonProperty("isXABA")
|
||||
private boolean isXABA;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ public class ActivityResultHandler {
|
|||
public static final int REQUEST_CODE_APP_INSTALL = 1001;
|
||||
public static final int REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION = 1002;
|
||||
public static final int REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION = 1003;
|
||||
public static final int REQUEST_CODE_DATA_FILES_ACCESS_PERMISSION = 1004;
|
||||
|
||||
public static ConcurrentHashMap<Integer, BiConsumer<Integer, Intent>> listenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import com.zane.smapiinstaller.utils.ZipUtils;
|
|||
|
||||
import net.fornwall.apksigner.KeyStoreFileManager;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
|
@ -50,6 +51,7 @@ import java.security.cert.X509Certificate;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -64,6 +66,7 @@ import java.util.zip.Deflater;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import pxb.android.axml.NodeVisitor;
|
||||
|
||||
/**
|
||||
|
@ -109,7 +112,7 @@ public class ApkPatcher {
|
|||
* @param advancedStage 0: 初始化,1: 高级安装,-1: 普通安装
|
||||
* @return 抽取后的APK文件路径,如果抽取失败返回null
|
||||
*/
|
||||
public String extract(int advancedStage) {
|
||||
public Tuple2<String, String[]> extract(int advancedStage) {
|
||||
emitProgress(0);
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
List<String> packageNames = FileUtils.getAssetJson(context, "package_names.json", new TypeReference<List<String>>() {
|
||||
|
@ -143,7 +146,7 @@ public class ApkPatcher {
|
|||
}
|
||||
return null;
|
||||
});
|
||||
return apkFile.getAbsolutePath();
|
||||
return new Tuple2<>(apkFile.getAbsolutePath(), null);
|
||||
} else if (advancedStage == 1) {
|
||||
File contentFolder = new File(stadewValleyBasePath + "/StardewValley/Content");
|
||||
if (contentFolder.exists()) {
|
||||
|
@ -154,10 +157,10 @@ public class ApkPatcher {
|
|||
} else {
|
||||
extract(0);
|
||||
}
|
||||
return apkFile.getAbsolutePath();
|
||||
return new Tuple2<>(apkFile.getAbsolutePath(), null);
|
||||
}
|
||||
emitProgress(5);
|
||||
return apkFile.getAbsolutePath();
|
||||
return new Tuple2<>(apkFile.getAbsolutePath(), packageInfo.applicationInfo.splitSourceDirs);
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
|
@ -174,7 +177,7 @@ public class ApkPatcher {
|
|||
* @param isAdvanced 是否高级模式
|
||||
* @return 是否成功打包
|
||||
*/
|
||||
public boolean patch(String apkPath, File targetFile, boolean isAdvanced) {
|
||||
public boolean patch(String apkPath, File targetFile, boolean isAdvanced, boolean isResourcePack) {
|
||||
if (apkPath == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -199,7 +202,7 @@ public class ApkPatcher {
|
|||
ApkFilesManifest apkFilesManifest = apkFilesManifests.get(0);
|
||||
List<ManifestEntry> manifestEntries = apkFilesManifest.getManifestEntries();
|
||||
errorMessage.set(null);
|
||||
List<ZipUtils.ZipEntrySource> entries = manifestEntries.stream()
|
||||
List<ZipUtils.ZipEntrySource> entries = isResourcePack ? new ArrayList<>() : manifestEntries.stream()
|
||||
.map(entry -> processFileEntry(file, apkFilesManifest, entry, isAdvanced))
|
||||
.filter(Objects::nonNull).flatMap(Stream::of).distinct().collect(Collectors.toList());
|
||||
entries.add(new ZipUtils.ZipEntrySource("AndroidManifest.xml", modifiedManifest, Deflater.DEFLATED));
|
||||
|
@ -208,7 +211,7 @@ public class ApkPatcher {
|
|||
stopwatch.reset();
|
||||
stopwatch.start();
|
||||
originSignInfo = ZipUtils.addOrReplaceEntries(apkPath, entries, targetFile.getAbsolutePath(),
|
||||
isAdvanced ? (entryName) -> entryName.startsWith("assets/Content") : null,
|
||||
(entryName) -> entryName.startsWith("assemblies/assemblies.") || (isAdvanced && entryName.startsWith("assets/Content")),
|
||||
(progress) -> emitProgress((int) (baseProgress + (progress / 100.0) * 35)));
|
||||
stopwatch.stop();
|
||||
emitProgress(46);
|
||||
|
@ -225,40 +228,51 @@ public class ApkPatcher {
|
|||
if (entry.isAdvanced() && !isAdvanced) {
|
||||
return null;
|
||||
}
|
||||
if (entry.getTargetPath().endsWith(Constants.FILE_SEPARATOR) && entry.getAssetPath().contains("*")) {
|
||||
String path = StringUtils.substringBeforeLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||
try {
|
||||
if (entry.getOrigin() == 1) {
|
||||
ArrayList<ZipUtils.ZipEntrySource> list = new ArrayList<>();
|
||||
ZipUtil.iterate(apkFile, (in, zipEntry) -> {
|
||||
String entryPath = StringUtils.substringBeforeLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||
String filename = StringUtils.substringAfterLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||
if (entryPath.equals(path) && StringUtils.wildCardMatch(filename, pattern)) {
|
||||
byte[] bytes = ByteStreams.toByteArray(in);
|
||||
ZipUtils.ZipEntrySource source;
|
||||
if (entry.isXALZ()) {
|
||||
source = new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> new ByteArrayInputStream(ZipUtils.decompressXALZ(bytes)));
|
||||
} else {
|
||||
source = new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, bytes, entry.getCompression());
|
||||
}
|
||||
list.add(source);
|
||||
}
|
||||
});
|
||||
return list.toArray(new ZipUtils.ZipEntrySource[0]);
|
||||
} else {
|
||||
return Stream.of(context.getAssets().list(path))
|
||||
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
||||
.map(filename -> new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> {
|
||||
try {
|
||||
return FileUtils.getLocalAsset(context, path + Constants.FILE_SEPARATOR + filename);
|
||||
} catch (IOException ignored) {
|
||||
if (entry.getTargetPath().endsWith(Constants.FILE_SEPARATOR)) {
|
||||
if (entry.isXABA()) {
|
||||
byte[] manifestBytes = ZipUtil.unpackEntry(apkFile, entry.getAssetPath() + ".manifest");
|
||||
byte[] xabaBytes = ZipUtil.unpackEntry(apkFile, entry.getAssetPath() + ".blob");
|
||||
Map<String, byte[]> unpackedAssemblies = ZipUtils.unpackXABA(manifestBytes, xabaBytes);
|
||||
ArrayList<ZipUtils.ZipEntrySource> list = new ArrayList<>();
|
||||
unpackedAssemblies.forEach((filename, bytes) -> {
|
||||
list.add(new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, bytes, entry.getCompression()));
|
||||
});
|
||||
return list.toArray(new ZipUtils.ZipEntrySource[0]);
|
||||
} else if (entry.getAssetPath().contains("*")) {
|
||||
String path = StringUtils.substringBeforeLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||
try {
|
||||
if (entry.getOrigin() == 1) {
|
||||
ArrayList<ZipUtils.ZipEntrySource> list = new ArrayList<>();
|
||||
ZipUtil.iterate(apkFile, (in, zipEntry) -> {
|
||||
String entryPath = StringUtils.substringBeforeLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||
String filename = StringUtils.substringAfterLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||
if (entryPath.equals(path) && StringUtils.wildCardMatch(filename, pattern)) {
|
||||
byte[] bytes = ByteStreams.toByteArray(in);
|
||||
ZipUtils.ZipEntrySource source;
|
||||
if (entry.isXALZ()) {
|
||||
source = new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> new ByteArrayInputStream(ZipUtils.decompressXALZ(bytes)));
|
||||
} else {
|
||||
source = new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, bytes, entry.getCompression());
|
||||
}
|
||||
return null;
|
||||
}))
|
||||
.toArray(ZipUtils.ZipEntrySource[]::new);
|
||||
list.add(source);
|
||||
}
|
||||
});
|
||||
return list.toArray(new ZipUtils.ZipEntrySource[0]);
|
||||
} else {
|
||||
return Stream.of(context.getAssets().list(path))
|
||||
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
||||
.map(filename -> new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> {
|
||||
try {
|
||||
return FileUtils.getLocalAsset(context, path + Constants.FILE_SEPARATOR + filename);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return null;
|
||||
}))
|
||||
.toArray(ZipUtils.ZipEntrySource[]::new);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -351,6 +365,10 @@ public class ApkPatcher {
|
|||
if (StringUtils.equals(attr.name, ManifestPatchConstants.PATTERN_VERSION_CODE)) {
|
||||
versionCode.set((int) attr.obj);
|
||||
}
|
||||
} else if (attr.type == NodeVisitor.TYPE_INT_BOOLEAN) {
|
||||
if (StringUtils.equals(attr.name, ManifestPatchConstants.PATTERN_EXTRACT_NATIVE_LIBS)) {
|
||||
attr.obj = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -390,7 +408,7 @@ public class ApkPatcher {
|
|||
try {
|
||||
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||
emitProgress(47);
|
||||
String signApkPath = stadewValleyBasePath + "/SMAPI Installer/base_signed.apk";
|
||||
String signApkPath = stadewValleyBasePath + "/SMAPI Installer/" + FilenameUtils.getBaseName(apkPath) + "_signed.apk";
|
||||
KeyStore ks = new KeyStoreFileManager.JksKeyStore();
|
||||
try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
|
||||
ks.load(fis, PASSWORD.toCharArray());
|
||||
|
@ -407,11 +425,11 @@ public class ApkPatcher {
|
|||
Collections.singletonList(publicKey))
|
||||
.build());
|
||||
DefaultApkSignerEngine signerEngine = new DefaultApkSignerEngine.Builder(engineSignerConfigs, 19)
|
||||
.setV1SigningEnabled(true)
|
||||
.setV2SigningEnabled(true)
|
||||
.setV3SigningEnabled(false)
|
||||
.build();
|
||||
if(originSignInfo != null && originSignInfo.getFirst() != null) {
|
||||
.setV1SigningEnabled(true)
|
||||
.setV2SigningEnabled(true)
|
||||
.setV3SigningEnabled(false)
|
||||
.build();
|
||||
if (originSignInfo != null && originSignInfo.getFirst() != null) {
|
||||
signerEngine.initWith(originSignInfo.getFirst(), originSignInfo.getSecond());
|
||||
}
|
||||
long zipOpElapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
|
||||
|
@ -432,7 +450,7 @@ public class ApkPatcher {
|
|||
}
|
||||
});
|
||||
thread.start();
|
||||
try(RandomAccessFile inputApkFile = new RandomAccessFile(apkPath, "r")) {
|
||||
try (RandomAccessFile inputApkFile = new RandomAccessFile(apkPath, "r")) {
|
||||
ApkSigner signer = new ApkSigner.Builder(signerEngine)
|
||||
.setInputApk(DataSources.asDataSource(inputApkFile, 0, inputApkFile.length()))
|
||||
.setOutputApk(outputFile)
|
||||
|
@ -527,7 +545,7 @@ public class ApkPatcher {
|
|||
}
|
||||
|
||||
private void emitProgress(int progress) {
|
||||
if(lastProgress < progress) {
|
||||
if (lastProgress < progress) {
|
||||
lastProgress = progress;
|
||||
for (Consumer<Integer> consumer : progressListener) {
|
||||
consumer.accept(progress);
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Activity;
|
|||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
|
@ -59,6 +60,7 @@ import java.util.stream.Stream;
|
|||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import pxb.android.axml.AxmlReader;
|
||||
import pxb.android.axml.AxmlVisitor;
|
||||
import pxb.android.axml.AxmlWriter;
|
||||
|
@ -214,9 +216,6 @@ public class CommonLogic {
|
|||
* @return 操作是否成功
|
||||
*/
|
||||
public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode, String packageName, long versionCode) {
|
||||
if(!checkMusic(context)){
|
||||
return false;
|
||||
}
|
||||
List<ApkFilesManifest> apkFilesManifests = CommonLogic.findAllApkFileManifest(context);
|
||||
filterManifest(apkFilesManifests, packageName, versionCode);
|
||||
List<ManifestEntry> manifestEntries = null;
|
||||
|
@ -279,20 +278,8 @@ public class CommonLogic {
|
|||
File pathFrom = new File(FileUtils.getStadewValleyBasePath(), "Android/obb/" + Constants.ORIGIN_PACKAGE_NAME_GOOGLE);
|
||||
File pathTo = new File(FileUtils.getStadewValleyBasePath(), "StardewValley");
|
||||
if (pathFrom.exists() && pathFrom.isDirectory()) {
|
||||
if (!pathFrom.canRead()) {
|
||||
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
Uri uri = data.getData();
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
});
|
||||
openObbRootPermission((Activity) context, ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION);
|
||||
if(!checkObbRootPermission((Activity) context, ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION, (success) -> checkMusic(context))) {
|
||||
return false;
|
||||
}
|
||||
if (!pathTo.exists()) {
|
||||
pathTo.mkdirs();
|
||||
|
@ -475,14 +462,13 @@ public class CommonLogic {
|
|||
intent.setData(Uri.parse("package:" + activity.getPackageName()));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
activity.startActivityForResult(intent, ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION);
|
||||
} catch (ActivityNotFoundException ignored){
|
||||
} catch (ActivityNotFoundException ignored) {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setData(Uri.parse("package:" + activity.getPackageName()));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
try {
|
||||
activity.startActivityForResult(intent, ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION);
|
||||
}
|
||||
catch (ActivityNotFoundException ignored2) {
|
||||
} catch (ActivityNotFoundException ignored2) {
|
||||
intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
activity.startActivityForResult(intent, ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION);
|
||||
|
@ -490,9 +476,15 @@ public class CommonLogic {
|
|||
}
|
||||
}
|
||||
|
||||
public static void openObbRootPermission(Activity context, int REQUEST_CODE_FOR_DIR) {
|
||||
Uri uri1 = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fobb");
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(context, uri1);
|
||||
public static boolean checkDataRootPermission(Activity context, int REQUEST_CODE_FOR_DIR, Consumer<Boolean> callback) {
|
||||
Uri targetDirUri = pathToUri("Android/data/" + Constants.ORIGIN_PACKAGE_NAME_GOOGLE);
|
||||
if(checkPathPermission(context, targetDirUri)) {
|
||||
return true;
|
||||
}
|
||||
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_DATA_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||
takePermission(resultCode, data, context.getContentResolver(), callback);
|
||||
});
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(context, targetDirUri);
|
||||
Intent intent1 = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent1.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
|
@ -500,6 +492,57 @@ public class CommonLogic {
|
|||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
intent1.putExtra(DocumentsContract.EXTRA_INITIAL_URI, documentFile.getUri());
|
||||
context.startActivityForResult(intent1, REQUEST_CODE_FOR_DIR);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void takePermission(Integer resultCode, Intent data, ContentResolver context, Consumer<Boolean> callback) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
Uri uri = data.getData();
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
context.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
callback.accept(true);
|
||||
} else {
|
||||
callback.accept(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri pathToUri(String path) {
|
||||
return Uri.parse("content://com.android.externalstorage.documents/tree/primary%3A" + path.replace("/", "%3A"));
|
||||
}
|
||||
|
||||
public static boolean checkPathPermission(Context context, Uri targetDirUri) {
|
||||
if(DocumentFile.fromTreeUri(context, targetDirUri).canWrite()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean checkPathPermission(Context context, String path) {
|
||||
return checkPathPermission(context, path);
|
||||
}
|
||||
|
||||
public static boolean checkObbRootPermission(Activity context, int REQUEST_CODE_FOR_DIR, Consumer<Boolean> callback) {
|
||||
Uri targetDirUri = pathToUri("Android/obb");
|
||||
if(checkPathPermission(context, targetDirUri)) {
|
||||
return true;
|
||||
}
|
||||
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||
takePermission(resultCode, data, context.getContentResolver(), callback);
|
||||
});
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(context, targetDirUri);
|
||||
Intent intent1 = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent1.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
intent1.putExtra(DocumentsContract.EXTRA_INITIAL_URI, documentFile.getUri());
|
||||
context.startActivityForResult(intent1, REQUEST_CODE_FOR_DIR);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void showPrivacyPolicy(View view, BiConsumer<MaterialDialog, DialogAction> callback) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
import com.zane.smapiinstaller.MainApplication;
|
||||
import com.zane.smapiinstaller.R;
|
||||
|
@ -14,6 +15,8 @@ import com.zane.smapiinstaller.constant.AppConfigKeyConstants;
|
|||
import com.zane.smapiinstaller.constant.Constants;
|
||||
import com.zane.smapiinstaller.constant.DialogAction;
|
||||
import com.zane.smapiinstaller.databinding.FragmentInstallBinding;
|
||||
import com.zane.smapiinstaller.dto.Tuple2;
|
||||
import com.zane.smapiinstaller.logic.ActivityResultHandler;
|
||||
import com.zane.smapiinstaller.logic.ApkPatcher;
|
||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||
import com.zane.smapiinstaller.logic.ModAssetsManager;
|
||||
|
@ -22,12 +25,16 @@ import com.zane.smapiinstaller.utils.ConfigUtils;
|
|||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
@ -95,12 +102,12 @@ public class InstallFragment extends Fragment {
|
|||
if (task != null) {
|
||||
task.interrupt();
|
||||
}
|
||||
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog)->{
|
||||
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog) -> {
|
||||
ApkPatcher patcher = new ApkPatcher(context);
|
||||
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(binding.getRoot(), dialog, null, progress));
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.extracting_package, null);
|
||||
String path = patcher.extract(0);
|
||||
if (path == null) {
|
||||
Tuple2<String, String[]> paths = patcher.extract(0);
|
||||
if (paths == null) {
|
||||
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
|
||||
}
|
||||
}));
|
||||
|
@ -111,17 +118,20 @@ public class InstallFragment extends Fragment {
|
|||
* 安装逻辑
|
||||
*/
|
||||
private void installLogic(boolean isAdv) {
|
||||
// if (!CommonLogic.checkDataRootPermission(context, ActivityResultHandler.REQUEST_CODE_DATA_FILES_ACCESS_PERMISSION, (success) -> installLogic(isAdv))) {
|
||||
// return;
|
||||
// }
|
||||
if (task != null) {
|
||||
task.interrupt();
|
||||
}
|
||||
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog)-> {
|
||||
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog) -> {
|
||||
ApkPatcher patcher = new ApkPatcher(context);
|
||||
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(binding.getRoot(), dialog, null, progress));
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.extracting_package, null);
|
||||
String path = patcher.extract(isAdv ? 1 : -1);
|
||||
Tuple2<String, String[]> paths = patcher.extract(isAdv ? 1 : -1);
|
||||
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||
File dest = new File(stadewValleyBasePath + "/SMAPI Installer/");
|
||||
boolean failed = path == null;
|
||||
boolean failed = paths == null;
|
||||
if (!dest.exists()) {
|
||||
if (!dest.mkdir()) {
|
||||
failed = true;
|
||||
|
@ -132,16 +142,16 @@ public class InstallFragment extends Fragment {
|
|||
return;
|
||||
}
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, null);
|
||||
if (!CommonLogic.unpackSmapiFiles(context, path, false, patcher.getGamePackageName(), patcher.getGameVersionCode())) {
|
||||
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_unpack_smapi_files)));
|
||||
return;
|
||||
}
|
||||
ModAssetsManager modAssetsManager = new ModAssetsManager(binding.getRoot());
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, 6);
|
||||
modAssetsManager.installDefaultMods();
|
||||
// if (!CommonLogic.unpackSmapiFiles(context, apkPath, false, patcher.getGamePackageName(), patcher.getGameVersionCode())) {
|
||||
// DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_unpack_smapi_files)));
|
||||
// return;
|
||||
// }
|
||||
// ModAssetsManager modAssetsManager = new ModAssetsManager(binding.getRoot());
|
||||
// DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, 6);
|
||||
// modAssetsManager.installDefaultMods();
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.patching_package, 8);
|
||||
File targetApk = new File(dest, "base.apk");
|
||||
if (!patcher.patch(path, targetApk, isAdv)) {
|
||||
if (!patcher.patch(paths.getFirst(), targetApk, isAdv, false)) {
|
||||
int target = patcher.getSwitchAction().getAndSet(0);
|
||||
if (target == R.string.menu_download) {
|
||||
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)), R.string.menu_download, R.string.cancel, (d, which) -> {
|
||||
|
@ -155,12 +165,21 @@ public class InstallFragment extends Fragment {
|
|||
}
|
||||
return;
|
||||
}
|
||||
List<String> resourcePacks = new ArrayList<>();
|
||||
if (paths.getSecond() != null) {
|
||||
for (String resourcePack : paths.getSecond()) {
|
||||
File targetResourcePack = new File(dest, FilenameUtils.getName(resourcePack));
|
||||
patcher.patch(resourcePack, targetResourcePack, false, true);
|
||||
resourcePacks.add(targetResourcePack.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.signing_package, null);
|
||||
String signPath = patcher.sign(targetApk.getAbsolutePath());
|
||||
if (signPath == null) {
|
||||
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_sign_game)));
|
||||
return;
|
||||
}
|
||||
List<String> signedResourcePacks = resourcePacks.stream().map(patcher::sign).collect(Collectors.toList());
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.installing_package, null);
|
||||
patcher.install(signPath);
|
||||
}));
|
||||
|
|
|
@ -68,7 +68,7 @@ public class ZipUtils {
|
|||
public static Map<String, byte[]> unpackXABA(byte[] manifestBytes, byte[] xabaBytes) {
|
||||
List<List<String>> manifest = Splitter.on('\n').omitEmptyStrings().splitToList(new String(manifestBytes, StandardCharsets.UTF_8))
|
||||
.stream().skip(1).map(line -> Splitter.on(CharMatcher.whitespace()).omitEmptyStrings().splitToList(line)).collect(Collectors.toList());
|
||||
ByteSource source = ByteSource.wrap(manifestBytes);
|
||||
ByteSource source = ByteSource.wrap(xabaBytes);
|
||||
Map<String, byte[]> result = new HashMap<>();
|
||||
try {
|
||||
int offset = 0;
|
||||
|
|
Loading…
Reference in New Issue