diff --git a/.idea/misc.xml b/.idea/misc.xml index 55c0ec2..43ae24e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,5 @@ - - + diff --git a/app/build.gradle b/app/build.gradle index 1d36471..bef7aec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { minSdkVersion 21 //noinspection ExpiredTargetSdkVersion targetSdkVersion 30 - versionCode 76 - versionName "3.18.3.2" + versionCode 77 + versionName "3.18.3.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/app/src/main/assets/apk/StardewModdingAPI.dll b/app/src/main/assets/apk/StardewModdingAPI.dll index f32089a..ee0049b 100644 Binary files a/app/src/main/assets/apk/StardewModdingAPI.dll and b/app/src/main/assets/apk/StardewModdingAPI.dll differ diff --git a/app/src/main/assets/mods_manifest.json b/app/src/main/assets/mods_manifest.json index dcfa0d3..38231d8 100644 --- a/app/src/main/assets/mods_manifest.json +++ b/app/src/main/assets/mods_manifest.json @@ -6,7 +6,7 @@ }, { "assetPath":"mods/console-commands.zip", - "Name": "Console Commands", + "Name": "ConsoleCommands", "UniqueID": "SMAPI.ConsoleCommands" } ] \ No newline at end of file diff --git a/app/src/main/java/androidx/documentfile/provider/DocumentUtils.java b/app/src/main/java/androidx/documentfile/provider/DocumentUtils.java new file mode 100644 index 0000000..fbee44a --- /dev/null +++ b/app/src/main/java/androidx/documentfile/provider/DocumentUtils.java @@ -0,0 +1,86 @@ +package androidx.documentfile.provider; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.DocumentsContract; +import android.text.TextUtils; +import android.util.Log; + + +import java.util.ArrayList; +import java.util.List; + +public class DocumentUtils { + public interface IFileFilter { + boolean accept(String name); + } + private static TreeDocumentFile findFile(Context context, TreeDocumentFile file, String name) { + final ContentResolver resolver = context.getContentResolver(); + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(file.getUri(), DocumentsContract.getDocumentId(file.getUri())); + Cursor c = null; + try { + c = resolver.query(childrenUri, new String[] {DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME}, null, null, null); + while (c.moveToNext()) { + final String documentName = c.getString(1); + if (TextUtils.equals(name, documentName)) { + final String documentId = c.getString(0); + final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(file.getUri(), documentId); + return new TreeDocumentFile(file, context, documentUri); + } + } + } catch (Exception e) { + Log.w("DocumentUtils", "Failed query: " + e); + } finally { + if (c != null) { + c.close(); + } + } + return null; + } + public static DocumentFile findFile(Context context, DocumentFile documentFile, String name) { + if (documentFile instanceof TreeDocumentFile) + return findFile(context, (TreeDocumentFile)documentFile, name); + return documentFile.findFile(name); + } + private static List filterFiles(Context context, TreeDocumentFile file, IFileFilter filter) { + ContentResolver resolver = context.getContentResolver(); + Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(file.getUri(), DocumentsContract.getDocumentId(file.getUri())); + List filtered = new ArrayList<>(); + Cursor c = null; + try { + c = resolver.query(childrenUri, new String[] {DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME}, null, null, null); + while (c.moveToNext()) { + String documentName = c.getString(1); + String documentId = c.getString(0); + Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(file.getUri(), documentId); + TreeDocumentFile child = new TreeDocumentFile(file, context, documentUri); + if (child.isDirectory()) + filtered.addAll(filterFiles(context, child, filter)); + else if (filter.accept(documentName)) + filtered.add(child); + } + } catch (Exception e) { + Log.w("DocumentUtils", "Failed query: " + e); + } finally { + if (c != null) { + c.close(); + } + } + return filtered; + } + public static List filterFiles(Context context, DocumentFile documentFile, IFileFilter filter) { + if (documentFile instanceof TreeDocumentFile) + return filterFiles(context, (TreeDocumentFile)documentFile, filter); + List filtered = new ArrayList<>(); + DocumentFile[] files = documentFile.listFiles(); + if (files != null) { + for (DocumentFile file : files) { + if (filter.accept(file.getName())) + filtered.add(file); + } + } + return filtered; + } +} diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java index 3e27237..ce69bf1 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java @@ -173,7 +173,6 @@ public Tuple2 extract(int advancedStage) { * 将指定APK文件重新打包,添加SMAPI,修改AndroidManifest.xml,同时验证版本是否正确 * * @param apkPath APK文件路径 - * @param second * @param targetFile 目标文件 * @param isAdvanced 是否高级模式 * @return 是否成功打包 diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java index 0044645..379f55c 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java @@ -22,6 +22,7 @@ import androidx.annotation.RequiresApi; import androidx.documentfile.provider.DocumentFile; +import androidx.documentfile.provider.DocumentUtils; import com.afollestad.materialdialogs.MaterialDialog; import com.fasterxml.jackson.core.type.TypeReference; @@ -275,19 +276,17 @@ public static boolean unpackSmapiFiles(Activity context, String apkPath, boolean if (CommonLogic.checkDataRootPermission(context)) { Uri targetDirUri = pathToTreeUri(Constants.TARGET_DATA_FILE_URI); DocumentFile documentFile = DocumentFile.fromTreeUri(context, targetDirUri); - for (DocumentFile file : documentFile.listFiles()) { - if (file.getName().equals("files")) { - copyDocument(context, new File(basePath, "smapi-internal"), file); - copyDocument(context, new File(basePath, "Mods"), file); - } + DocumentFile filesDoc = DocumentUtils.findFile(context, documentFile, "files"); + if(filesDoc != null) { + copyDocument(context, new File(basePath, "smapi-internal"), filesDoc); } } return true; } - private static void copyDocument(Activity context, File src, DocumentFile dest) { + public static void copyDocument(Activity context, File src, DocumentFile dest) { if (src.isDirectory()) { - DocumentFile documentFile = dest.findFile(src.getName()); + DocumentFile documentFile = DocumentUtils.findFile(context, dest, src.getName()); if (documentFile == null) { documentFile = dest.createDirectory(src.getName()); } @@ -295,7 +294,7 @@ private static void copyDocument(Activity context, File src, DocumentFile dest) copyDocument(context, file, documentFile); } } else { - DocumentFile documentFile = dest.findFile(src.getName()); + DocumentFile documentFile = DocumentUtils.findFile(context, dest, src.getName()); if (documentFile == null) { documentFile = dest.createFile("application/x-binary", src.getName()); } @@ -512,7 +511,7 @@ public static void openPermissionSetting(Activity activity) { } } - public static boolean checkDataRootPermission(Activity context) { + public static boolean checkDataRootPermission(Context context) { File pathFrom = new File(FileUtils.getStadewValleyBasePath(), "Android/data/" + Constants.TARGET_PACKAGE_NAME + "/files/"); if (!pathFrom.exists()) { return false; diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java b/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java index c9550d9..9a45f59 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.pm.PackageInfo; +import android.net.Uri; import android.util.Log; import android.view.View; import android.widget.Toast; @@ -45,6 +46,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import androidx.documentfile.provider.DocumentFile; +import androidx.documentfile.provider.DocumentUtils; import androidx.navigation.NavController; import androidx.navigation.Navigation; @@ -169,6 +172,7 @@ public boolean installDefaultMods() { } File modFolder = new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH); ImmutableListMultimap installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID); + List unpackedMods = new ArrayList<>(); for (ModManifestEntry mod : modManifestEntries) { if (installedModMap.containsKey(mod.getUniqueID())) { ImmutableList installedMods = installedModMap.get(mod.getUniqueID()); @@ -202,7 +206,9 @@ public boolean installDefaultMods() { } if (installedMods.size() > 0) { try { - ZipUtil.unpack(context.getAssets().open(mod.getAssetPath()), new File(installedMods.get(0).getAssetPath()), (name) -> StringUtils.removeStart(name, mod.getName() + "/")); + File targetFile = new File(installedMods.get(0).getAssetPath()); + ZipUtil.unpack(context.getAssets().open(mod.getAssetPath()), targetFile, (name) -> StringUtils.removeStart(name, mod.getName() + "/")); + unpackedMods.add(targetFile); } catch (IOException e) { Log.e(TAG, "Install Mod Error", e); } @@ -211,10 +217,25 @@ public boolean installDefaultMods() { } try { ZipUtil.unpack(context.getAssets().open(mod.getAssetPath()), modFolder); + unpackedMods.add(new File(modFolder, mod.getName())); } catch (IOException e) { Log.e(TAG, "Install Mod Error", e); } } + if (CommonLogic.checkDataRootPermission(context)) { + Uri targetDirUri = CommonLogic.pathToTreeUri(Constants.TARGET_DATA_FILE_URI); + DocumentFile documentFile = DocumentFile.fromTreeUri(context, targetDirUri); + if(documentFile != null) { + DocumentFile filesDoc = DocumentUtils.findFile(context, documentFile, "files"); + DocumentFile modsDoc = DocumentUtils.findFile(context, filesDoc, "Mods"); + if (modsDoc == null) { + modsDoc = filesDoc.createDirectory("Mods"); + } + for (File mod : unpackedMods) { + CommonLogic.copyDocument(context, mod, modsDoc); + } + } + } return true; } diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java index 6105c3d..ff6f2cf 100644 --- a/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java +++ b/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java @@ -59,9 +59,15 @@ private void joinQQ() { case 1: CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, baseUrl + "kshK7BavcS2jXZ6exDvezc18ksLB8YsM")); break; - default: + case 2: CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, baseUrl + "zqsWYGBuAxPx0n9RI_ONs-7NA1Mm48QY")); break; + case 3: + CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, baseUrl + "uYnxVTCGlWuLbeb3XA3mDXoO0tlYhy3J")); + break; + default: + CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, "https://s.zaneyork.cn:8443/s/qc")); + break; } }); } diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java index 84ddeda..7e9138e 100644 --- a/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java +++ b/app/src/main/java/com/zane/smapiinstaller/ui/install/InstallFragment.java @@ -57,7 +57,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, binding.layoutAdvInstall.setVisibility(View.VISIBLE); } try { - String firstLine = Files.asCharSource(new File(FileUtils.getStadewValleyBasePath(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine(); + String firstLine = Files.asCharSource(FileUtils.docOverlayFetch(context, Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine(); if (StringUtils.isNoneBlank(firstLine)) { String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+"); versionString = RegExUtils.removePattern(versionString, "\\s+with.+"); @@ -158,7 +158,6 @@ private void installLogic(boolean isAdv) { 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.signing_package, null); diff --git a/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java b/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java index aa8969e..96c8293 100644 --- a/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java +++ b/app/src/main/java/com/zane/smapiinstaller/utils/FileUtils.java @@ -1,9 +1,14 @@ package com.zane.smapiinstaller.utils; +import android.app.Activity; import android.content.Context; +import android.net.Uri; import android.os.Environment; import android.util.Log; +import androidx.documentfile.provider.DocumentFile; +import androidx.documentfile.provider.DocumentUtils; + import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -12,6 +17,8 @@ import com.google.common.io.CharStreams; import com.google.common.io.Files; import com.hjq.language.MultiLanguages; +import com.zane.smapiinstaller.constant.Constants; +import com.zane.smapiinstaller.logic.CommonLogic; import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.lang3.StringUtils; @@ -22,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.List; @@ -327,4 +335,34 @@ public static List listAll(String basePath, Predicate filter) { File::getAbsolutePath) ); } + + public static File docOverlayFetch(Context context, String relativePath) { + relativePath = relativePath.replace("StardewValley/", ""); + if (CommonLogic.checkDataRootPermission(context)) { + Uri targetDirUri = CommonLogic.pathToTreeUri(Constants.TARGET_DATA_FILE_URI); + DocumentFile documentFile = DocumentFile.fromTreeUri(context, targetDirUri); + DocumentFile filesDoc = DocumentUtils.findFile(context, documentFile, "files"); + if(filesDoc != null) { + String[] split = relativePath.split("/"); + DocumentFile currentDoc = filesDoc; + for (String path : split) { + currentDoc = DocumentUtils.findFile(context, currentDoc, path); + if(currentDoc == null) { + break; + } + } + if(currentDoc != null && currentDoc.isFile()) { + try (InputStream inputStream = context.getContentResolver().openInputStream(currentDoc.getUri())) { + File tempFile = File.createTempFile(currentDoc.getName(), null); + tempFile.deleteOnExit(); + FileUtils.copy(inputStream, tempFile); + return tempFile; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + return new File(FileUtils.getStadewValleyBasePath(), relativePath); + } } diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3f6b0a5..5084229 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -61,7 +61,7 @@ Registro detallado Firmando paquete deinstalación SMAPI Stardew Valley - Versión SMAPI: 3.18.3.2 + Versión SMAPI: 3.18.3.3 Nota: Requiere la versión del juego 1.5.6.39 o superior El cuerpo del juego debe instalarse durante la actualización o instalación Desempacando diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6754e50..28873ab 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -61,7 +61,7 @@ Journalisation détaillée Signature SMAPI Stardew Valley - Version SMAPI: 3.18.3.2 + Version SMAPI: 3.18.3.3 Remarques: La version du jeu 1.5.6.39 ou ultérieure est requise. Le jeu de base est requis lors de la mise à jour / installation. Déballage diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 3ca8e69..c8fab09 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -61,7 +61,7 @@ Catatan Terperinci Menandatangani SMAPI Stardew Valley - Versi SMAPI: 3.18.3.2 + Versi SMAPI: 3.18.3.3 Catatan: Dibutuhkan Stardew Valley versi 1.5.6.39 atau yang lebih baru. Permainan dasar diperlukan saat memperbarui/menginstal. Membongkar diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 34d1cb1..457d080 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -61,7 +61,7 @@ 자세한 로그 설치 패키지 서명 SMAPI Stardew Valley - SMAPI버전: 3.18.3.2 + SMAPI버전: 3.18.3.3 참고 : 게임 버전 1.5.6.39 이상이 필요합니다 업데이트 또는 설치 중에 게임 본체를 설치해야합니다 포장 풀기 diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 8bbb5e1..bf7cc2f 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -61,7 +61,7 @@ Log detalhado Assinatura SMAPI Stardew Valley - Versão SMAPI: 3.18.3.2 + Versão SMAPI: 3.18.3.3 Notas: É necessária a versão do jogo 1.5.6.39 ou posterior. O jogo base é necessário ao atualizar / instalar. Desembalar diff --git a/app/src/main/res/values-th/arrays.xml b/app/src/main/res/values-th/arrays.xml index f3ce67b..95f79cc 100644 --- a/app/src/main/res/values-th/arrays.xml +++ b/app/src/main/res/values-th/arrays.xml @@ -35,5 +35,7 @@ 860453392 1078428449 980882763 + 1156021254 + QQ频道 diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 3d7cfda..0b4e7d3 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -61,7 +61,7 @@ บันทึกอย่างละเอียด กำลังลงทะเบียนแอป SMAPI Stardew Valley - เวอร์ชั่น SMAPI: 3.18.3.2 + เวอร์ชั่น SMAPI: 3.18.3.3 หมายเหตุ: ต้องการเกมเวอร์ชั่น 1.5.6.39 หรือใหม่กว่า ต้องการเกมหลักเมื่อทำการอัปเดต / ติดตั้ง กำลังแกะกล่อง diff --git a/app/src/main/res/values-uk/arrays.xml b/app/src/main/res/values-uk/arrays.xml index 83f3d5c..c83be6c 100644 --- a/app/src/main/res/values-uk/arrays.xml +++ b/app/src/main/res/values-uk/arrays.xml @@ -35,6 +35,8 @@ 860453392 1078428449 980882763 + 1156021254 + QQ频道 Графіка diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 90f99fc..70231b7 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -60,7 +60,7 @@ Докдадний вхід Реєстрація SMAPI Stardew Valley - Версія SMAPI: 3.18.3.2 + Версія SMAPI: 3.18.3.3 Примітка: Необхідна версія гри 1.5.6.39 або вище. Базова гра потрібна при оновленні/встановленні. Розпакування diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9e58f53..96ee66c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -60,7 +60,7 @@ 詳細日誌 正在簽名安裝包 SMAPI 星露谷物語 - SMAPI版本: 3.18.3.2 + SMAPI版本: 3.18.3.3 注意:需要 1.5.6.39 以上遊戲版本 更新或安裝期間需要安裝遊戲 正在解包 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 4d0974e..06e3949 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -60,7 +60,7 @@ 详细日志 正在签名安装包 SMAPI星露谷物语 - SMAPI版本: 3.18.3.2 + SMAPI版本: 3.18.3.3 注意:需要不低于1.5.6.39版本的游戏本体 更新或安装期间需要安装游戏本体 正在解包 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 30361dd..86b34da 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -35,6 +35,8 @@ 860453392 1078428449 980882763 + 1156021254 + QQ频道 Graphics diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 76c0ec9..dbaa91a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,7 +60,7 @@ Verbose Logging Signing SMAPI Stardew Valley - SMAPI Version: 3.18.3.2 + SMAPI Version: 3.18.3.3 Notes: Game version 1.5.6.39 or later is required. The base game is required when updating/installing. Unpacking