parent
cf9ceb67fb
commit
3569afb695
|
@ -12,8 +12,8 @@ android {
|
||||||
applicationId "com.zane.smapiinstaller"
|
applicationId "com.zane.smapiinstaller"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 69
|
versionCode 70
|
||||||
versionName "3.7.6.7"
|
versionName "3.7.6.8"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
@ -22,11 +22,11 @@ android {
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
minifyEnabled false
|
minifyEnabled true
|
||||||
shrinkResources false
|
shrinkResources false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,13 +119,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
# For Jackson
|
# For Jackson
|
||||||
-keepattributes Signature
|
-keepattributes *Annotation*,EnclosingMethod,Signature
|
||||||
-keepattributes *Annotation*
|
-keepnames class com.fasterxml.jackson.** { *; }
|
||||||
-keep class sun.misc.Unsafe { *; }
|
|
||||||
-dontwarn org.codehaus.jackson.**
|
|
||||||
-dontwarn com.fasterxml.jackson.databind.**
|
-dontwarn com.fasterxml.jackson.databind.**
|
||||||
-keep class org.codehaus.jackson.** { *;}
|
-keep class org.codehaus.** { *; }
|
||||||
-keep class com.fasterxml.jackson.** { *; }
|
-keepclassmembers public final enum org.codehaus.jackson.annotate.JsonAutoDetect$Visibility {
|
||||||
|
public static final org.codehaus.jackson.annotate.JsonAutoDetect$Visibility *; }
|
||||||
|
|
||||||
#okhttp
|
#okhttp
|
||||||
-dontwarn okhttp3.**
|
-dontwarn okhttp3.**
|
||||||
|
@ -139,9 +138,13 @@
|
||||||
-keep class pxb.android.** { *; }
|
-keep class pxb.android.** { *; }
|
||||||
-keep class net.fornwall.apksigner.** { *; }
|
-keep class net.fornwall.apksigner.** { *; }
|
||||||
-keep class com.android.apksig.** { *; }
|
-keep class com.android.apksig.** { *; }
|
||||||
|
|
||||||
#Warning:org.bouncycastle.jce.provider.X509LDAPCertStoreSpi: can't find referenced class javax.naming.NamingEnumeration
|
#Warning:org.bouncycastle.jce.provider.X509LDAPCertStoreSpi: can't find referenced class javax.naming.NamingEnumeration
|
||||||
-dontwarn javax.naming.**
|
-dontwarn javax.naming.**
|
||||||
-keep class org.bouncycastle.** {*;}
|
-keep class org.bouncycastle.jcajce.provider.** { *; }
|
||||||
|
-keep class org.bouncycastle.jce.provider.** { *; }
|
||||||
|
#-keep class org.bouncycastle.** {*;}
|
||||||
|
|
||||||
-keep class org.slf4j.**
|
-keep class org.slf4j.**
|
||||||
-keep class com.hjq.language.** {*;}
|
-keep class com.hjq.language.** {*;}
|
||||||
-keep class net.jpountz.** {*;}
|
-keep class net.jpountz.** {*;}
|
||||||
|
@ -158,3 +161,28 @@ public static java.lang.String TABLENAME;
|
||||||
-dontwarn net.sqlcipher.database.**
|
-dontwarn net.sqlcipher.database.**
|
||||||
# If you do NOT use RxJava:
|
# If you do NOT use RxJava:
|
||||||
-dontwarn rx.**
|
-dontwarn rx.**
|
||||||
|
|
||||||
|
-keep class com.sun.org.apache.xml.internal.utils.PrefixResolver
|
||||||
|
-keep class java.rmi.Remote
|
||||||
|
-keep class java.rmi.server.*
|
||||||
|
-keep class javax.annotation.processing.AbstractProcessor
|
||||||
|
-keep class javax.el.*
|
||||||
|
-keep class javax.servlet.http.*
|
||||||
|
-keep class javax.servlet.jsp.el.VariableResolver
|
||||||
|
-keep class javax.servlet.jsp.*
|
||||||
|
-keep class javax.servlet.jsp.tagext.*
|
||||||
|
-keep class javax.servlet.*
|
||||||
|
-keep class javax.swing.JTree
|
||||||
|
-keep class javax.swing.tree.TreeNode
|
||||||
|
-keep class lombok.core.configuration.ConfigurationKey
|
||||||
|
-keep class org.apache.tools.ant.Task
|
||||||
|
-keep class org.apache.tools.ant.taskdefs.MatchingTask
|
||||||
|
-keep class org.apache.xml.utils.PrefixResolver
|
||||||
|
-keep class org.jaxen.dom.*
|
||||||
|
-keep class org.jaxen.dom4j.Dom4jXPath
|
||||||
|
-keep class org.jaxen.jdom.JDOMXPath
|
||||||
|
-keep class org.jaxen.*
|
||||||
|
-keep class org.jdom.output.XMLOutputter
|
||||||
|
-keep class org.python.core.PyObject
|
||||||
|
-keep class org.python.util.PythonInterpreter
|
||||||
|
-keep class org.zeroturnaround.javarebel.ClassEventListener
|
||||||
|
|
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.
|
@ -101,9 +101,16 @@
|
||||||
"assetPath": "apk/System.Xml.dll",
|
"assetPath": "apk/System.Xml.dll",
|
||||||
"origin": 0
|
"origin": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"targetPath": "smapi-internal/BmFont.dll",
|
||||||
|
"assetPath": "assemblies/BmFont.dll",
|
||||||
|
"isXALZ": true,
|
||||||
|
"origin": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"targetPath": "smapi-internal/xTile.dll",
|
"targetPath": "smapi-internal/xTile.dll",
|
||||||
"assetPath": "apk/xTile.dll",
|
"assetPath": "assemblies/xTile.dll",
|
||||||
"origin": 0
|
"isXALZ": true,
|
||||||
|
"origin": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -62,18 +62,28 @@ public class Constants {
|
||||||
/**
|
/**
|
||||||
* 文本文件打开大小阈值
|
* 文本文件打开大小阈值
|
||||||
*/
|
*/
|
||||||
public static int TEXT_FILE_OPEN_SIZE_LIMIT = 16 * 1024 * 1024;
|
public static final int TEXT_FILE_OPEN_SIZE_LIMIT = 16 * 1024 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMAPI版本
|
* SMAPI版本
|
||||||
*/
|
*/
|
||||||
public static final String SMAPI_VERSION = "3.7.6";
|
public static final String SMAPI_VERSION = "3.7.6";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mono Android 10 起始版本号
|
||||||
|
*/
|
||||||
|
public static final int MONO_10_VERSION_CODE = 148;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用名称
|
* 应用名称
|
||||||
*/
|
*/
|
||||||
public static String PATCHED_APP_NAME = null;
|
public static String PATCHED_APP_NAME = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manifest中使用的路径分隔符
|
||||||
|
*/
|
||||||
|
public static final String FILE_SEPARATOR = "/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 平台
|
* 平台
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -89,12 +89,9 @@ public class ApkPatcher {
|
||||||
/**
|
/**
|
||||||
* 依次扫描package_names.json文件对应的包名,抽取找到的第一个游戏APK到SMAPI Installer路径
|
* 依次扫描package_names.json文件对应的包名,抽取找到的第一个游戏APK到SMAPI Installer路径
|
||||||
*
|
*
|
||||||
|
* @param advancedStage 0: 初始化,1: 高级安装,-1: 普通安装
|
||||||
* @return 抽取后的APK文件路径,如果抽取失败返回null
|
* @return 抽取后的APK文件路径,如果抽取失败返回null
|
||||||
*/
|
*/
|
||||||
public String extract() {
|
|
||||||
return extract(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String extract(int advancedStage) {
|
public String extract(int advancedStage) {
|
||||||
emitProgress(0);
|
emitProgress(0);
|
||||||
PackageManager packageManager = context.getPackageManager();
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
@ -169,12 +166,9 @@ public class ApkPatcher {
|
||||||
* 将指定APK文件重新打包,添加SMAPI,修改AndroidManifest.xml,同时验证版本是否正确
|
* 将指定APK文件重新打包,添加SMAPI,修改AndroidManifest.xml,同时验证版本是否正确
|
||||||
*
|
*
|
||||||
* @param apkPath APK文件路径
|
* @param apkPath APK文件路径
|
||||||
|
* @param isAdvanced 是否高级模式
|
||||||
* @return 是否成功打包
|
* @return 是否成功打包
|
||||||
*/
|
*/
|
||||||
public boolean patch(String apkPath) {
|
|
||||||
return patch(apkPath, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean patch(String apkPath, boolean isAdvanced) {
|
public boolean patch(String apkPath, boolean isAdvanced) {
|
||||||
if (apkPath == null) {
|
if (apkPath == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -230,15 +224,15 @@ public class ApkPatcher {
|
||||||
if (entry.isAdvanced() && !isAdvanced) {
|
if (entry.isAdvanced() && !isAdvanced) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (entry.getTargetPath().endsWith("/") && entry.getAssetPath().contains("*")) {
|
if (entry.getTargetPath().endsWith(Constants.FILE_SEPARATOR) && entry.getAssetPath().contains("*")) {
|
||||||
String path = StringUtils.substringBeforeLast(entry.getAssetPath(), "/");
|
String path = StringUtils.substringBeforeLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||||
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), "/");
|
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), Constants.FILE_SEPARATOR);
|
||||||
try {
|
try {
|
||||||
if (entry.getOrigin() == 1) {
|
if (entry.getOrigin() == 1) {
|
||||||
ArrayList<ZipUtils.ZipEntrySource> list = new ArrayList<>();
|
ArrayList<ZipUtils.ZipEntrySource> list = new ArrayList<>();
|
||||||
ZipUtil.iterate(apkFile, (in, zipEntry) -> {
|
ZipUtil.iterate(apkFile, (in, zipEntry) -> {
|
||||||
String entryPath = StringUtils.substringBeforeLast(zipEntry.getName(), "/");
|
String entryPath = StringUtils.substringBeforeLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||||
String filename = StringUtils.substringAfterLast(zipEntry.getName(), "/");
|
String filename = StringUtils.substringAfterLast(zipEntry.getName(), Constants.FILE_SEPARATOR);
|
||||||
if (entryPath.equals(path) && StringUtils.wildCardMatch(filename, pattern)) {
|
if (entryPath.equals(path) && StringUtils.wildCardMatch(filename, pattern)) {
|
||||||
byte[] bytes = ByteStreams.toByteArray(in);
|
byte[] bytes = ByteStreams.toByteArray(in);
|
||||||
ZipUtils.ZipEntrySource source;
|
ZipUtils.ZipEntrySource source;
|
||||||
|
@ -256,7 +250,7 @@ public class ApkPatcher {
|
||||||
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
||||||
.map(filename -> new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> {
|
.map(filename -> new ZipUtils.ZipEntrySource(entry.getTargetPath() + filename, entry.getCompression(), () -> {
|
||||||
try {
|
try {
|
||||||
return FileUtils.getLocalAsset(context, path + "/" + filename);
|
return FileUtils.getLocalAsset(context, path + Constants.FILE_SEPARATOR + filename);
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -342,7 +336,7 @@ public class ApkPatcher {
|
||||||
}
|
}
|
||||||
case "name":
|
case "name":
|
||||||
if (strObj.contains(ManifestPatchConstants.PATTERN_MAIN_ACTIVITY)) {
|
if (strObj.contains(ManifestPatchConstants.PATTERN_MAIN_ACTIVITY)) {
|
||||||
if (versionCode.get() > 147) {
|
if (versionCode.get() > Constants.MONO_10_VERSION_CODE) {
|
||||||
attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "crc648e5438a58262f792.SMainActivity");
|
attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "crc648e5438a58262f792.SMainActivity");
|
||||||
} else {
|
} else {
|
||||||
attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "md5723872fa9a204f7f942686e9ed9d0b7d.SMainActivity");
|
attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "md5723872fa9a204f7f942686e9ed9d0b7d.SMainActivity");
|
||||||
|
|
|
@ -40,9 +40,7 @@ import org.zeroturnaround.zip.ZipUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -204,8 +202,8 @@ public class CommonLogic {
|
||||||
* @param context context
|
* @param context context
|
||||||
* @param apkPath 安装包路径
|
* @param apkPath 安装包路径
|
||||||
* @param checkMode 是否为校验模式
|
* @param checkMode 是否为校验模式
|
||||||
* @param packageName
|
* @param packageName 包名
|
||||||
* @param versionCode
|
* @param versionCode 版本号
|
||||||
* @return 操作是否成功
|
* @return 操作是否成功
|
||||||
*/
|
*/
|
||||||
public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode, String packageName, long versionCode) {
|
public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode, String packageName, long versionCode) {
|
||||||
|
@ -254,56 +252,10 @@ public class CommonLogic {
|
||||||
File targetFile = new File(basePath, entry.getTargetPath());
|
File targetFile = new File(basePath, entry.getTargetPath());
|
||||||
switch (entry.getOrigin()) {
|
switch (entry.getOrigin()) {
|
||||||
case 0:
|
case 0:
|
||||||
if(entry.isExternal() && apkFilesManifest != null){
|
unpackFromInstaller(context, checkMode, apkFilesManifest, basePath, entry, targetFile);
|
||||||
byte[] bytes = FileUtils.getAssetBytes(context, apkFilesManifest.getBasePath() + entry.getAssetPath());
|
|
||||||
try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
|
|
||||||
outputStream.write(bytes);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (entry.getTargetPath().endsWith("/") && entry.getAssetPath().contains("*")) {
|
|
||||||
String path = StringUtils.substring(entry.getAssetPath(), 0, StringUtils.lastIndexOf(entry.getAssetPath(), "/"));
|
|
||||||
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), "/");
|
|
||||||
try {
|
|
||||||
Stream.of(context.getAssets().list(path))
|
|
||||||
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
|
||||||
.forEach(filename -> {
|
|
||||||
unpackFile(context, checkMode, path + "/" + filename, new File(basePath, entry.getTargetPath() + filename));
|
|
||||||
});
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unpackFile(context, checkMode, entry.getAssetPath(), targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (!checkMode || !targetFile.exists()) {
|
unpackFromApk(apkPath, checkMode, entry, targetFile);
|
||||||
if(entry.isXALZ()){
|
|
||||||
byte[] bytes = ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath());
|
|
||||||
if (entry.isXALZ()) {
|
|
||||||
bytes = ZipUtils.decompressXALZ(bytes);
|
|
||||||
}
|
|
||||||
FileOutputStream stream = null;
|
|
||||||
try {
|
|
||||||
stream = FileUtils.openOutputStream(targetFile);
|
|
||||||
stream.write(bytes);
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if(stream != null) {
|
|
||||||
try {
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -312,6 +264,48 @@ public class CommonLogic {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void unpackFromApk(String apkPath, boolean checkMode, ManifestEntry entry, File targetFile) {
|
||||||
|
if (!checkMode || !targetFile.exists()) {
|
||||||
|
if(entry.isXALZ()){
|
||||||
|
byte[] bytes = ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath());
|
||||||
|
if (entry.isXALZ()) {
|
||||||
|
bytes = ZipUtils.decompressXALZ(bytes);
|
||||||
|
}
|
||||||
|
try (FileOutputStream stream = FileUtils.openOutputStream(targetFile)) {
|
||||||
|
stream.write(bytes);
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unpackFromInstaller(Context context, boolean checkMode, ApkFilesManifest apkFilesManifest, File basePath, ManifestEntry entry, File targetFile) {
|
||||||
|
if(entry.isExternal() && apkFilesManifest != null){
|
||||||
|
byte[] bytes = FileUtils.getAssetBytes(context, apkFilesManifest.getBasePath() + entry.getAssetPath());
|
||||||
|
try (FileOutputStream outputStream = new FileOutputStream(targetFile)) {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (entry.getTargetPath().endsWith("/") && entry.getAssetPath().contains("*")) {
|
||||||
|
String path = StringUtils.substring(entry.getAssetPath(), 0, StringUtils.lastIndexOf(entry.getAssetPath(), "/"));
|
||||||
|
String pattern = StringUtils.substringAfterLast(entry.getAssetPath(), "/");
|
||||||
|
try {
|
||||||
|
Stream.of(context.getAssets().list(path))
|
||||||
|
.filter(filename -> StringUtils.wildCardMatch(filename, pattern))
|
||||||
|
.forEach(filename -> unpackFile(context, checkMode, path + "/" + filename, new File(basePath, entry.getTargetPath() + filename)));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unpackFile(context, checkMode, entry.getAssetPath(), targetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void filterManifest(List<ApkFilesManifest> manifests, String packageName, long versionCode){
|
public static void filterManifest(List<ApkFilesManifest> manifests, String packageName, long versionCode){
|
||||||
Iterables.removeIf(manifests, manifest -> {
|
Iterables.removeIf(manifests, manifest -> {
|
||||||
if (manifest == null) {
|
if (manifest == null) {
|
||||||
|
@ -325,10 +319,7 @@ public class CommonLogic {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (manifest.getTargetPackageName() != null && packageName != null && !manifest.getTargetPackageName().contains(packageName)) {
|
return manifest.getTargetPackageName() != null && packageName != null && !manifest.getTargetPackageName().contains(packageName);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private static void unpackFile(Context context, boolean checkMode, String assertPath, File targetFile) {
|
private static void unpackFile(Context context, boolean checkMode, String assertPath, File targetFile) {
|
||||||
|
@ -398,7 +389,6 @@ public class CommonLogic {
|
||||||
try {
|
try {
|
||||||
PackageManager manager = activity.getPackageManager();
|
PackageManager manager = activity.getPackageManager();
|
||||||
PackageInfo info = manager.getPackageInfo(activity.getPackageName(), 0);
|
PackageInfo info = manager.getPackageInfo(activity.getPackageName(), 0);
|
||||||
String version = info.versionName;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
return info.getLongVersionCode();
|
return info.getLongVersionCode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,7 @@ import android.view.View;
|
||||||
import com.microsoft.appcenter.crashes.Crashes;
|
import com.microsoft.appcenter.crashes.Crashes;
|
||||||
import com.zane.smapiinstaller.R;
|
import com.zane.smapiinstaller.R;
|
||||||
import com.zane.smapiinstaller.constant.Constants;
|
import com.zane.smapiinstaller.constant.Constants;
|
||||||
import com.zane.smapiinstaller.constant.ManifestPatchConstants;
|
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
import com.zane.smapiinstaller.utils.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏启动器
|
* 游戏启动器
|
||||||
|
|
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import pxb.android.axml.NodeVisitor;
|
import pxb.android.axml.NodeVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -44,7 +44,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
@ -260,13 +259,7 @@ public class ModAssetsManager {
|
||||||
DialogUtils.showConfirmDialog(root, R.string.error,
|
DialogUtils.showConfirmDialog(root, R.string.error,
|
||||||
root.getContext().getString(R.string.duplicate_mod_found, Joiner.on(";").join(list)),
|
root.getContext().getString(R.string.duplicate_mod_found, Joiner.on(";").join(list)),
|
||||||
R.string.continue_text, R.string.abort,
|
R.string.continue_text, R.string.abort,
|
||||||
((dialog, which) -> {
|
((dialog, which) -> returnCallback.accept(which == DialogAction.POSITIVE)));
|
||||||
if (which == DialogAction.POSITIVE) {
|
|
||||||
returnCallback.accept(true);
|
|
||||||
} else {
|
|
||||||
returnCallback.accept(false);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
returnCallback.accept(true);
|
returnCallback.accept(true);
|
||||||
}
|
}
|
||||||
|
@ -466,10 +459,7 @@ public class ModAssetsManager {
|
||||||
if (StringUtils.isBlank(dependency.getMinimumVersion())) {
|
if (StringUtils.isBlank(dependency.getMinimumVersion())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (VersionUtil.compareVersion(version, dependency.getMinimumVersion()) < 0) {
|
return VersionUtil.compareVersion(version, dependency.getMinimumVersion()) < 0;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tuple2<String, String> checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
private Tuple2<String, String> checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ import com.zane.smapiinstaller.databinding.FragmentAboutBinding;
|
||||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +29,7 @@ public class AboutFragment extends Fragment {
|
||||||
private FragmentAboutBinding binding;
|
private FragmentAboutBinding binding;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentAboutBinding.inflate(inflater, container, false);
|
binding = FragmentAboutBinding.inflate(inflater, container, false);
|
||||||
binding.buttonRelease.setOnClickListener(v -> release());
|
binding.buttonRelease.setOnClickListener(v -> release());
|
||||||
|
|
|
@ -77,9 +77,7 @@ public class ConfigEditFragment extends Fragment {
|
||||||
File file = new File(configPath);
|
File file = new File(configPath);
|
||||||
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
|
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
|
||||||
initAssetWebView();
|
initAssetWebView();
|
||||||
binding.scrollView.post(() -> {
|
binding.scrollView.post(() -> CommonLogic.doOnNonNull(this.getContext(), (context -> onScrollViewRendered(file, context))));
|
||||||
CommonLogic.doOnNonNull(this.getContext(), (context -> onScrollViewRendered(file, context)));
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
|
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
|
||||||
if (which == DialogAction.POSITIVE) {
|
if (which == DialogAction.POSITIVE) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
* @author Zane
|
* @author Zane
|
||||||
*/
|
*/
|
||||||
public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.ViewHolder> {
|
public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.ViewHolder> {
|
||||||
private ConfigViewModel model;
|
private final ConfigViewModel model;
|
||||||
private List<ModManifestEntry> modList;
|
private List<ModManifestEntry> modList;
|
||||||
|
|
||||||
public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList) {
|
public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList) {
|
||||||
|
@ -66,7 +66,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder extends RecyclerView.ViewHolder {
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
private ModListItemBinding binding;
|
private final ModListItemBinding binding;
|
||||||
private ModManifestEntry modInfo;
|
private ModManifestEntry modInfo;
|
||||||
private List<String> configList;
|
private List<String> configList;
|
||||||
|
|
||||||
|
@ -181,9 +181,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
|
||||||
if (configList.size() > 0) {
|
if (configList.size() > 0) {
|
||||||
if (configList.size() > 1) {
|
if (configList.size() > 1) {
|
||||||
List<String> selections = configList.stream().map(path -> StringUtils.removeStart(path, modInfo.getAssetPath())).collect(Collectors.toList());
|
List<String> selections = configList.stream().map(path -> StringUtils.removeStart(path, modInfo.getAssetPath())).collect(Collectors.toList());
|
||||||
DialogUtils.showListItemsDialog(itemView, R.string.menu_config_edit, selections, (materialDialog, index) -> {
|
DialogUtils.showListItemsDialog(itemView, R.string.menu_config_edit, selections, (materialDialog, index) -> navigateToConfigEditor(configList.get(index)));
|
||||||
navigateToConfigEditor(configList.get(index));
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
navigateToConfigEditor(configList.get(0));
|
navigateToConfigEditor(configList.get(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
private DownloadContentItemBinding binding;
|
private final DownloadContentItemBinding binding;
|
||||||
|
|
||||||
private final AtomicBoolean downloading = new AtomicBoolean(false);
|
private final AtomicBoolean downloading = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class HelpItemAdapter extends RecyclerView.Adapter<HelpItemAdapter.ViewHo
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
private HelpListItemBinding binding;
|
private final HelpListItemBinding binding;
|
||||||
|
|
||||||
public ViewHolder(View view) {
|
public ViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import androidx.fragment.app.Fragment;
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
|
|
||||||
class MainTabPagerAdapter extends FragmentStateAdapter {
|
class MainTabPagerAdapter extends FragmentStateAdapter {
|
||||||
private Fragment fragment;
|
private final Fragment fragment;
|
||||||
|
|
||||||
public MainTabPagerAdapter(Fragment fragment) {
|
public MainTabPagerAdapter(Fragment fragment) {
|
||||||
super(fragment);
|
super(fragment);
|
||||||
|
|
|
@ -47,11 +47,7 @@ public class MainTabsFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position) {
|
public void onPageSelected(int position) {
|
||||||
super.onPageSelected(position);
|
super.onPageSelected(position);
|
||||||
if (position >= 3) {
|
MainActivity.instance.setFloatingBarVisibility(position < 3);
|
||||||
MainActivity.instance.setFloatingBarVisibility(false);
|
|
||||||
} else {
|
|
||||||
MainActivity.instance.setFloatingBarVisibility(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class ModUpdateAdapter extends RecyclerView.Adapter<ModUpdateAdapter.View
|
||||||
|
|
||||||
private final ImmutableListMultimap<String, ModManifestEntry> installedModMap;
|
private final ImmutableListMultimap<String, ModManifestEntry> installedModMap;
|
||||||
|
|
||||||
private List<ModUpdateCheckResponseDto> updateInfoList;
|
private final List<ModUpdateCheckResponseDto> updateInfoList;
|
||||||
|
|
||||||
public ModUpdateAdapter(List<ModUpdateCheckResponseDto> items) {
|
public ModUpdateAdapter(List<ModUpdateCheckResponseDto> items) {
|
||||||
updateInfoList = items;
|
updateInfoList = items;
|
||||||
|
@ -56,7 +56,7 @@ public class ModUpdateAdapter extends RecyclerView.Adapter<ModUpdateAdapter.View
|
||||||
|
|
||||||
class ViewHolder extends RecyclerView.ViewHolder {
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
public ModUpdateCheckResponseDto updateInfo;
|
public ModUpdateCheckResponseDto updateInfo;
|
||||||
private UpdatableModListItemBinding binding;
|
private final UpdatableModListItemBinding binding;
|
||||||
|
|
||||||
public void setUpdateInfo(ModUpdateCheckResponseDto updateInfo) {
|
public void setUpdateInfo(ModUpdateCheckResponseDto updateInfo) {
|
||||||
this.updateInfo = updateInfo;
|
this.updateInfo = updateInfo;
|
||||||
|
|
|
@ -12,6 +12,8 @@ import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
|
||||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
@ -35,7 +37,7 @@ public class ModUpdateFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentModUpdateListBinding.inflate(inflater, container, false);
|
binding = FragmentModUpdateListBinding.inflate(inflater, container, false);
|
||||||
// Set the adapter
|
// Set the adapter
|
||||||
|
|
|
@ -24,10 +24,8 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件工具类
|
* 文件工具类
|
||||||
|
|
|
@ -7,9 +7,9 @@ import java.math.BigInteger;
|
||||||
*/
|
*/
|
||||||
public class MathUtils {
|
public class MathUtils {
|
||||||
|
|
||||||
private static int INT_MAX_BIT_VALUE = 31;
|
private static final int INT_MAX_BIT_VALUE = 31;
|
||||||
|
|
||||||
private static int LONG_MAX_BIT_VALUE = 63;
|
private static final int LONG_MAX_BIT_VALUE = 63;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code long} argument;
|
* Returns the value of the {@code long} argument;
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class VersionUtil {
|
||||||
* @return 是否为空版本段
|
* @return 是否为空版本段
|
||||||
*/
|
*/
|
||||||
private static boolean isZero(List<String> versionSections) {
|
private static boolean isZero(List<String> versionSections) {
|
||||||
return !versionSections.stream().anyMatch(version -> {
|
return versionSections.stream().noneMatch(version -> {
|
||||||
try {
|
try {
|
||||||
int i = Integer.parseInt(version);
|
int i = Integer.parseInt(version);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
|
|
@ -32,8 +32,10 @@ import lombok.EqualsAndHashCode;
|
||||||
*/
|
*/
|
||||||
public class ZipUtils {
|
public class ZipUtils {
|
||||||
|
|
||||||
|
private final static String FILE_HEADER_XALZ = "XALZ";
|
||||||
|
|
||||||
public static byte[] decompressXALZ(byte[] bytes) {
|
public static byte[] decompressXALZ(byte[] bytes) {
|
||||||
if ("XALZ".equals(new String(ByteUtils.subArray(bytes, 0, 4), StandardCharsets.ISO_8859_1))) {
|
if (FILE_HEADER_XALZ.equals(new String(ByteUtils.subArray(bytes, 0, 4), StandardCharsets.ISO_8859_1))) {
|
||||||
byte[] length = ByteUtils.subArray(bytes, 8, 12);
|
byte[] length = ByteUtils.subArray(bytes, 8, 12);
|
||||||
int len = (length[0] & 0xff) | ((length[1] & 0xff) << 8) | ((length[2] & 0xff) << 16) | ((length[3] & 0xff) << 24);
|
int len = (length[0] & 0xff) | ((length[1] & 0xff) << 8) | ((length[2] & 0xff) << 16) | ((length[3] & 0xff) << 24);
|
||||||
bytes = LZ4Factory.fastestJavaInstance().fastDecompressor().decompress(bytes, 12, len);
|
bytes = LZ4Factory.fastestJavaInstance().fastDecompressor().decompress(bytes, 12, len);
|
||||||
|
|
|
@ -7,27 +7,19 @@ import android.text.Editable;
|
||||||
*/
|
*/
|
||||||
public abstract class TextChangedWatcher implements android.text.TextWatcher {
|
public abstract class TextChangedWatcher implements android.text.TextWatcher {
|
||||||
/**
|
/**
|
||||||
* Do nothing
|
* {@inheritDoc}
|
||||||
* @param s origin string
|
|
||||||
* @param start modify position
|
|
||||||
* @param count modify count
|
|
||||||
* @param after modified string
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text changed event
|
* {@inheritDoc}
|
||||||
* @param s modified string
|
|
||||||
* @param start modify position
|
|
||||||
* @param count modify count
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public abstract void onTextChanged(CharSequence s, int start, int before, int count);
|
public abstract void onTextChanged(CharSequence s, int start, int before, int count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do nothing
|
* {@inheritDoc}
|
||||||
* @param s target view
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {}
|
public void afterTextChanged(Editable s) {}
|
||||||
|
|
Loading…
Reference in New Issue