1. Optimize patch logic

2. Fix for PyTK 1.19.6
This commit is contained in:
ZaneYork 2021-02-04 14:40:51 +08:00
parent cf9ceb67fb
commit 3569afb695
50 changed files with 142 additions and 138 deletions

View File

@ -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'
} }

View File

@ -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.

View File

@ -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
} }
] ]

View File

@ -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 = "/";
/** /**
* 平台 * 平台
*/ */

View File

@ -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");

View File

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

View File

@ -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;
/** /**
* 游戏启动器 * 游戏启动器

View File

@ -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;
/** /**

View File

@ -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) {

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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

View File

@ -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;
/** /**
* 文件工具类 * 文件工具类

View File

@ -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;

View File

@ -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) {

View File

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

View File

@ -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) {}