Fix for Android SAF framework
This commit is contained in:
parent
c7fcaa26f3
commit
6b101f394d
|
@ -16,6 +16,5 @@
|
|||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
<option name="offlineMode" value="true" />
|
||||
</component>
|
||||
</project>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,23 +3,5 @@
|
|||
"assetPath":"mods/virtual-keyboard.zip",
|
||||
"Name": "VirtualKeyboard",
|
||||
"UniqueID": "VirtualKeyboard"
|
||||
},
|
||||
{
|
||||
"assetPath":"mods/custom-localization.zip",
|
||||
"Name": "CustomLocalization",
|
||||
"UniqueID": "ZaneYork.CustomLocalization",
|
||||
"CleanInstall": true,
|
||||
"Version": "1.1.0",
|
||||
"OriginUniqueId": ["SMAPI.CustomLocalization"]
|
||||
},
|
||||
{
|
||||
"assetPath":"mods/console-commands.zip",
|
||||
"Name": "Console Commands",
|
||||
"UniqueID": "SMAPI.ConsoleCommands"
|
||||
},
|
||||
{
|
||||
"assetPath":"mods/save-backup.zip",
|
||||
"Name": "SaveBackup",
|
||||
"UniqueID": "SMAPI.SaveBackup"
|
||||
}
|
||||
]
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"targetPath": "smapi-internal/",
|
||||
"assetPath": "assemblies/*.dll",
|
||||
"assetPath": "assemblies/",
|
||||
"origin": 1
|
||||
}
|
||||
]
|
|
@ -26,6 +26,8 @@ public class Constants {
|
|||
* 安装包目标包名
|
||||
*/
|
||||
public static final String TARGET_PACKAGE_NAME = "com.zane.stardewvalley";
|
||||
|
||||
public static final String TARGET_DATA_FILE_URI = "Android/data/" + Constants.TARGET_PACKAGE_NAME;
|
||||
/**
|
||||
* 安装包目标包名
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,9 @@ import android.view.animation.Animation;
|
|||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
@ -39,6 +42,7 @@ import com.zane.smapiinstaller.utils.FileUtils;
|
|||
import com.zane.smapiinstaller.utils.StringUtils;
|
||||
import com.zane.smapiinstaller.utils.ZipUtils;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
|
@ -48,6 +52,7 @@ import java.io.FileFilter;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.Channels;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -58,9 +63,6 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Function;
|
||||
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;
|
||||
|
@ -215,7 +217,7 @@ public class CommonLogic {
|
|||
* @param versionCode 版本号
|
||||
* @return 操作是否成功
|
||||
*/
|
||||
public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode, String packageName, long versionCode) {
|
||||
public static boolean unpackSmapiFiles(Activity context, String apkPath, boolean checkMode, String packageName, long versionCode) {
|
||||
List<ApkFilesManifest> apkFilesManifests = CommonLogic.findAllApkFileManifest(context);
|
||||
filterManifest(apkFilesManifests, packageName, versionCode);
|
||||
List<ManifestEntry> manifestEntries = null;
|
||||
|
@ -270,15 +272,49 @@ public class CommonLogic {
|
|||
break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void copyDocument(Activity context, File src, DocumentFile dest) {
|
||||
if (src.isDirectory()) {
|
||||
DocumentFile documentFile = dest.findFile(src.getName());
|
||||
if (documentFile == null) {
|
||||
documentFile = dest.createDirectory(src.getName());
|
||||
}
|
||||
for (File file : src.listFiles()) {
|
||||
copyDocument(context, file, documentFile);
|
||||
}
|
||||
} else {
|
||||
DocumentFile documentFile = dest.findFile(src.getName());
|
||||
if (documentFile == null) {
|
||||
documentFile = dest.createFile("application/x-binary", src.getName());
|
||||
}
|
||||
if(documentFile.length() != src.length()) {
|
||||
try (OutputStream outputStream = context.getContentResolver().openOutputStream(documentFile.getUri())) {
|
||||
FileUtils.copy(src, outputStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkMusic(Context context) {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
|
||||
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(!checkObbRootPermission((Activity) context, ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION, (success) -> checkMusic(context))) {
|
||||
if (!checkObbRootPermission((Activity) context, ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION, (success) -> checkMusic(context))) {
|
||||
return false;
|
||||
}
|
||||
if (!pathTo.exists()) {
|
||||
|
@ -314,7 +350,7 @@ public class CommonLogic {
|
|||
} catch (IOException ignore) {
|
||||
}
|
||||
} else {
|
||||
ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile);
|
||||
ZipUtil.unpack(new File(apkPath), targetFile, name -> name.startsWith(entry.getAssetPath()) ? FilenameUtils.getName(name) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -476,9 +512,25 @@ public class CommonLogic {
|
|||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
public static boolean checkDataRootPermission(Activity context) {
|
||||
File pathFrom = new File(FileUtils.getStadewValleyBasePath(), "Android/data/" + Constants.TARGET_PACKAGE_NAME + "/files/");
|
||||
if (!pathFrom.exists()) {
|
||||
return false;
|
||||
}
|
||||
Uri targetDirUri = pathToTreeUri(Constants.TARGET_DATA_FILE_URI);
|
||||
if (checkPathPermission(context, targetDirUri)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean requestDataRootPermission(Activity context, int REQUEST_CODE_FOR_DIR, Consumer<Boolean> callback) {
|
||||
File pathFrom = new File(FileUtils.getStadewValleyBasePath(), "Android/data/" + Constants.TARGET_PACKAGE_NAME + "/files");
|
||||
if (!pathFrom.exists()) {
|
||||
return true;
|
||||
}
|
||||
Uri targetDirUri = pathToTreeUri(Constants.TARGET_DATA_FILE_URI);
|
||||
if (checkPathPermission(context, targetDirUri)) {
|
||||
return true;
|
||||
}
|
||||
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_DATA_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||
|
@ -504,31 +556,31 @@ public class CommonLogic {
|
|||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
context.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
context.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_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 Uri pathToTreeUri(String path) {
|
||||
return Uri.parse("content://com.android.externalstorage.documents/tree/primary%3A" + path.replace("/", "%2F"));
|
||||
}
|
||||
|
||||
public static Uri pathToSingleUri(String path) {
|
||||
return Uri.parse("content://com.android.externalstorage.documents/document/primary%3A" + path.replace("/", "%2F"));
|
||||
}
|
||||
|
||||
public static boolean checkPathPermission(Context context, Uri targetDirUri) {
|
||||
if(DocumentFile.fromTreeUri(context, targetDirUri).canWrite()) {
|
||||
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)) {
|
||||
Uri targetDirUri = pathToTreeUri("Android/obb");
|
||||
if (checkPathPermission(context, targetDirUri)) {
|
||||
return true;
|
||||
}
|
||||
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_OBB_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||
|
|
|
@ -15,23 +15,21 @@ 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;
|
||||
import com.zane.smapiinstaller.ui.main.MainTabsFragmentDirections;
|
||||
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;
|
||||
|
@ -115,9 +113,9 @@ 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 (!CommonLogic.requestDataRootPermission(context, ActivityResultHandler.REQUEST_CODE_DATA_FILES_ACCESS_PERMISSION, (success) -> installLogic(isAdv))) {
|
||||
return;
|
||||
}
|
||||
if (task != null) {
|
||||
task.interrupt();
|
||||
}
|
||||
|
@ -138,14 +136,6 @@ public class InstallFragment extends Fragment {
|
|||
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
|
||||
return;
|
||||
}
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, null);
|
||||
// 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(paths.getFirst(), paths.getSecond(), targetApk, isAdv, false)) {
|
||||
|
@ -162,21 +152,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, paths.getSecond(), targetResourcePack, false, true);
|
||||
// resourcePacks.add(targetResourcePack.getAbsolutePath());
|
||||
// }
|
||||
// }
|
||||
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, null);
|
||||
if (!CommonLogic.unpackSmapiFiles(context, targetApk.getAbsolutePath(), 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.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);
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue