diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..a1757ae
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 73a4c41..77b8385 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -47,6 +47,7 @@ dependencies {
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
// https://mvnrepository.com/artifact/com.jakewharton/butterknife
implementation 'com.jakewharton:butterknife:10.2.1'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
// https://mvnrepository.com/artifact/com.google.guava/guava
implementation group: 'com.google.guava', name: 'guava', version: '28.2-android'
@@ -54,9 +55,15 @@ dependencies {
implementation group: 'org.zeroturnaround', name: 'zt-zip', version: '1.14'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
+ // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
+ implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
+ implementation 'com.lzy.net:okgo:3.0.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.android.support:multidex:1.0.3'
+
+ compileOnly 'org.projectlombok:lombok:1.18.12'
+ annotationProcessor 'org.projectlombok:lombok:1.18.12'
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 1e7caf4..c4d602b 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -146,6 +146,13 @@
}
##---------------End: proguard configuration for Gson ----------
+#okhttp
+-dontwarn okhttp3.**
+-keep class okhttp3.**{*;}
+
+#okio
+-dontwarn okio.**
+-keep class okio.**{*;}
-keep class com.zane.** { *; }
-keep class pxb.android.** { *; }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 704abfe..e038f03 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,12 +2,14 @@
+
manifestEntries;
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContent.java b/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContent.java
new file mode 100644
index 0000000..000bd78
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContent.java
@@ -0,0 +1,11 @@
+package com.zane.smapiinstaller.entity;
+
+import lombok.Data;
+
+@Data
+public class DownloadableContent {
+ private String type;
+ private String name;
+ private String url;
+ private String description;
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContentList.java b/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContentList.java
new file mode 100644
index 0000000..2dbe167
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/entity/DownloadableContentList.java
@@ -0,0 +1,11 @@
+package com.zane.smapiinstaller.entity;
+
+import java.util.List;
+
+import lombok.Data;
+
+@Data
+public class DownloadableContentList {
+ private int version;
+ List contents;
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/entity/ManifestEntry.java b/app/src/main/java/com/zane/smapiinstaller/entity/ManifestEntry.java
index 99ffe6d..df8c1f1 100644
--- a/app/src/main/java/com/zane/smapiinstaller/entity/ManifestEntry.java
+++ b/app/src/main/java/com/zane/smapiinstaller/entity/ManifestEntry.java
@@ -1,40 +1,11 @@
package com.zane.smapiinstaller.entity;
+import lombok.Data;
+
+@Data
public class ManifestEntry {
private String targetPath;
private String assetPath;
private int compression;
private int origin;
-
- public String getTargetPath() {
- return targetPath;
- }
-
- public void setTargetPath(String targetPath) {
- this.targetPath = targetPath;
- }
-
- public String getAssetPath() {
- return assetPath;
- }
-
- public void setAssetPath(String assetPath) {
- this.assetPath = assetPath;
- }
-
- public int getCompression() {
- return compression;
- }
-
- public void setCompression(int compression) {
- this.compression = compression;
- }
-
- public int getOrigin() {
- return origin;
- }
-
- public void setOrigin(int origin) {
- this.origin = origin;
- }
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/entity/ModManifestEntry.java b/app/src/main/java/com/zane/smapiinstaller/entity/ModManifestEntry.java
index 59dc137..93cff1d 100644
--- a/app/src/main/java/com/zane/smapiinstaller/entity/ModManifestEntry.java
+++ b/app/src/main/java/com/zane/smapiinstaller/entity/ModManifestEntry.java
@@ -1,41 +1,12 @@
package com.zane.smapiinstaller.entity;
+import lombok.Data;
+
+@Data
public class ModManifestEntry {
private String assetPath;
private String Name;
private String UniqueID;
private String Description;
private ModManifestEntry ContentPackFor;
-
- public String getAssetPath() {
- return assetPath;
- }
-
- public void setAssetPath(String assetPath) {
- this.assetPath = assetPath;
- }
-
- public String getName() {
- return Name;
- }
-
- public void setName(String name) {
- Name = name;
- }
-
- public String getUniqueID() {
- return UniqueID;
- }
-
- public void setUniqueID(String uniqueID) {
- UniqueID = uniqueID;
- }
-
- public String getDescription() {
- return Description;
- }
-
- public void setDescription(String description) {
- Description = description;
- }
}
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 84e36f5..f5f4bcf 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java
@@ -11,16 +11,20 @@ import android.os.Environment;
import android.util.Log;
import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.gson.reflect.TypeToken;
import com.zane.smapiinstaller.BuildConfig;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
+import com.zane.smapiinstaller.entity.ApkFilesManifest;
import com.zane.smapiinstaller.entity.ManifestEntry;
import net.fornwall.apksigner.KeyStoreFileManager;
import net.fornwall.apksigner.ZipSigner;
+import org.apache.commons.lang3.StringUtils;
import org.zeroturnaround.zip.ByteSource;
import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
@@ -29,7 +33,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -48,6 +51,8 @@ public class ApkPatcher {
private static final String TAG = "PATCHER";
+ private AtomicReference errorMessage = new AtomicReference<>();
+
public ApkPatcher(Context context) {
this.context = context;
}
@@ -56,8 +61,10 @@ public class ApkPatcher {
PackageManager packageManager = context.getPackageManager();
List packageNames = CommonLogic.getAssetJson(context, "package_names.json", new TypeToken>() {
}.getType());
- if (packageNames == null)
+ if (packageNames == null) {
+ errorMessage.set(context.getString(R.string.error_game_not_found));
return null;
+ }
for (String packageName : packageNames) {
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
@@ -69,6 +76,7 @@ public class ApkPatcher {
File dest = new File(externalFilesDir.getAbsolutePath() + "/SMAPI Installer/");
if (!dest.exists()) {
if (!dest.mkdir()) {
+ errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
return null;
}
}
@@ -78,6 +86,7 @@ public class ApkPatcher {
}
} catch (PackageManager.NameNotFoundException | IOException e) {
Log.e(TAG, "Extract error", e);
+ errorMessage.set(e.getLocalizedMessage());
}
}
return null;
@@ -90,29 +99,36 @@ public class ApkPatcher {
if (!file.exists())
return false;
try {
- List manifestEntries = CommonLogic.getAssetJson(context, "apk_files_manifest.json", new TypeToken>() {
- }.getType());
- if (manifestEntries == null)
+ ApkFilesManifest apkFilesManifest = CommonLogic.getAssetJson(context, "apk_files_manifest.json", ApkFilesManifest.class);
+ if (apkFilesManifest == null)
return false;
+ List manifestEntries = apkFilesManifest.getManifestEntries();
List zipEntrySourceList = new ArrayList<>();
- for (ManifestEntry entry : manifestEntries) {
- zipEntrySourceList.add(new ByteSource(entry.getTargetPath(), CommonLogic.getAssetBytes(context, entry.getAssetPath()), entry.getCompression()));
- }
byte[] manifest = ZipUtil.unpackEntry(file, "AndroidManifest.xml");
- byte[] modifiedManifest = modifyManifest(manifest);
+ List apkFilesManifests = Lists.newArrayList(apkFilesManifest);
+ byte[] modifiedManifest = modifyManifest(manifest, apkFilesManifests);
+ if(apkFilesManifests.size() == 0) {
+ errorMessage.set(context.getString(R.string.error_no_supported_game_version));
+ return false;
+ }
if(modifiedManifest == null) {
+ errorMessage.set(context.getString(R.string.failed_to_process_manifest));
return false;
}
zipEntrySourceList.add(new ByteSource("AndroidManifest.xml", modifiedManifest, Deflater.DEFLATED));
+ for (ManifestEntry entry : manifestEntries) {
+ zipEntrySourceList.add(new ByteSource(entry.getTargetPath(), CommonLogic.getAssetBytes(context, entry.getAssetPath()), entry.getCompression()));
+ }
ZipUtil.addOrReplaceEntries(file, zipEntrySourceList.toArray(new ZipEntrySource[0]));
return true;
} catch (Exception e) {
Log.e(TAG, "Patch error", e);
+ errorMessage.set(e.getLocalizedMessage());
}
return false;
}
- private byte[] modifyManifest(byte[] bytes) {
+ private byte[] modifyManifest(byte[] bytes, List manifests) {
AtomicReference packageName = new AtomicReference<>();
Predicate processLogic = (attr) -> {
if (attr.type == NodeVisitor.TYPE_STRING) {
@@ -140,9 +156,32 @@ public class ApkPatcher {
break;
}
}
+ else if(attr.type == NodeVisitor.TYPE_FIRST_INT) {
+ if(StringUtils.equals(attr.name, "versionCode")){
+ long versionCode = (int) attr.obj;
+ Iterables.removeIf(manifests, manifest -> {
+ if (manifest.getMinBuildCode() != null) {
+ if (versionCode < manifest.getMinBuildCode()) {
+ return true;
+ }
+ }
+ if (manifest.getMaxBuildCode() != null) {
+ if (versionCode > manifest.getMinBuildCode()) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+ }
return true;
};
- return CommonLogic.modifyManifest(bytes, processLogic);
+ try {
+ return CommonLogic.modifyManifest(bytes, processLogic);
+ }catch (Exception e) {
+ errorMessage.set(e.getLocalizedMessage());
+ return null;
+ }
}
public String sign(String apkPath) {
@@ -156,16 +195,14 @@ public class ApkPatcher {
}
String alias = ks.aliases().nextElement();
X509Certificate publicKey = (X509Certificate) ks.getCertificate(alias);
- try {
- PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
- ZipSigner.signZip(publicKey, privateKey, "SHA1withRSA", apkPath, signApkPath);
- new File(apkPath).delete();
- return signApkPath;
- } catch (NoSuchAlgorithmException ignored) {
- }
+ PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
+ ZipSigner.signZip(publicKey, privateKey, "SHA1withRSA", apkPath, signApkPath);
+ new File(apkPath).delete();
+ return signApkPath;
}
} catch (Exception e) {
Log.e(TAG, "Sign error", e);
+ errorMessage.set(e.getLocalizedMessage());
}
return null;
}
@@ -179,6 +216,7 @@ public class ApkPatcher {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Install error", e);
+ errorMessage.set(e.getLocalizedMessage());
}
}
@@ -196,4 +234,7 @@ public class ApkPatcher {
return Uri.fromFile(file);
}
+ public AtomicReference getErrorMessage() {
+ return errorMessage;
+ }
}
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 fcd6dfc..090a2cc 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java
@@ -59,6 +59,17 @@ public class CommonLogic {
return null;
}
+ public static T getAssetJson(Context context, String filename, Class tClass) {
+ try {
+ InputStream inputStream = context.getAssets().open(filename);
+ try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
+ return new Gson().fromJson(CharStreams.toString(reader), tClass);
+ }
+ } catch (IOException ignored) {
+ }
+ return null;
+ }
+
public static T getAssetJson(Context context, String filename, Type type) {
try {
InputStream inputStream = context.getAssets().open(filename);
@@ -177,25 +188,16 @@ public class CommonLogic {
context.startActivity(intent);
}
- public static byte[] modifyManifest(byte[] bytes, Predicate processLogic) {
+ public static byte[] modifyManifest(byte[] bytes, Predicate processLogic) throws IOException {
AxmlReader reader = new AxmlReader(bytes);
AxmlWriter writer = new AxmlWriter();
- try {
- reader.accept(new AxmlVisitor(writer) {
- @Override
- public NodeVisitor child(String ns, String name) {
- NodeVisitor child = super.child(ns, name);
- return new ManifestTagVisitor(child, processLogic);
- }
- });
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- return writer.toByteArray();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
+ reader.accept(new AxmlVisitor(writer) {
+ @Override
+ public NodeVisitor child(String ns, String name) {
+ NodeVisitor child = super.child(ns, name);
+ return new ManifestTagVisitor(child, processLogic);
+ }
+ });
+ return writer.toByteArray();
}
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java b/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java
index 9a391b7..75edeb4 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java
@@ -26,8 +26,11 @@ public class GameLauncher {
CommonLogic.showAlertDialog(root, R.string.error, R.string.error_failed_to_repair);
return;
}
- Intent intent = packageManager.getLaunchIntentForPackage(Constants.TARGET_PACKAGE_NAME);
- context.startActivity(intent);
+ ModAssetsManager modAssetsManager = new ModAssetsManager(root);
+ if(modAssetsManager.checkModEnvironment()) {
+ Intent intent = packageManager.getLaunchIntentForPackage(Constants.TARGET_PACKAGE_NAME);
+ context.startActivity(intent);
+ }
} catch (PackageManager.NameNotFoundException ignored) {
CommonLogic.showAlertDialog(root, R.string.error, R.string.error_smapi_not_installed);
}
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 92711f6..5ea631d 100644
--- a/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java
+++ b/app/src/main/java/com/zane/smapiinstaller/logic/ModAssetsManager.java
@@ -16,6 +16,8 @@ import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.entity.ModManifestEntry;
+import org.apache.commons.lang3.StringUtils;
+import org.zeroturnaround.zip.NameMapper;
import org.zeroturnaround.zip.ZipUtil;
import java.io.File;
@@ -23,6 +25,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicLong;
public class ModAssetsManager {
@@ -43,7 +46,7 @@ public class ModAssetsManager {
if(currentFile != null && currentFile.exists()) {
boolean foundManifest = false;
for(File file : currentFile.listFiles(File::isFile)) {
- if(file.getName().equalsIgnoreCase("manifest.json")) {
+ if(StringUtils.equalsIgnoreCase(file.getName(), "manifest.json")) {
ModManifestEntry manifest = CommonLogic.getFileJson(file, new TypeToken(){}.getType());
foundManifest = true;
if(manifest != null) {
@@ -70,7 +73,7 @@ public class ModAssetsManager {
File modFolder = new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH);
ImmutableListMultimap installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
for (ModManifestEntry mod : modManifestEntries) {
- if(installedModMap.containsKey(mod.getUniqueID())) {
+ if(installedModMap.containsKey(mod.getUniqueID()) || installedModMap.containsKey(mod.getUniqueID().replace("ZaneYork.CustomLocalization", "SMAPI.CustomLocalization"))) {
ImmutableList installedMods = installedModMap.get(mod.getUniqueID());
if(installedMods.size() > 1) {
CommonLogic.showAlertDialog(root, R.string.error,
@@ -79,7 +82,7 @@ public class ModAssetsManager {
return false;
}
try {
- ZipUtil.unpackEntry(context.getAssets().open(mod.getAssetPath()), mod.getName(), new File(installedMods.get(0).getAssetPath()));
+ ZipUtil.unpack(context.getAssets().open(mod.getAssetPath()), new File(installedMods.get(0).getAssetPath()), (name)-> StringUtils.removeStart(name, mod.getName() + "/"));
} catch (IOException e) {
Log.e(TAG, "Install Mod Error", e);
}
@@ -94,4 +97,8 @@ public class ModAssetsManager {
}
return true;
}
+
+ public boolean checkModEnvironment() {
+ return true;
+ }
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
index 226562e..f2427c9 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigEditFragment.java
@@ -61,7 +61,7 @@ public class ConfigEditFragment extends Fragment {
}
}
catch (Exception e) {
- CommonLogic.showAlertDialog(getView(), R.string.error, e.getMessage());
+ CommonLogic.showAlertDialog(getView(), R.string.error, e.getLocalizedMessage());
}
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
index 9f3885f..8805734 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigFragment.java
@@ -17,7 +17,6 @@ import com.zane.smapiinstaller.R;
public class ConfigFragment extends Fragment {
- private ConfigViewModel configViewModel;
@BindView(R.id.view_mod_list)
RecyclerView recyclerView;
@@ -26,13 +25,11 @@ public class ConfigFragment extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config, container, false);
ButterKnife.bind(this, root);
- configViewModel = new ConfigViewModel(root);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
- recyclerView.setAdapter(new ModManifestAdapter(configViewModel.getModList().getValue()));
- recyclerView.addItemDecoration(new DividerItemDecoration(this.getContext(), DividerItemDecoration.VERTICAL));
- configViewModel.getModList().observe(getViewLifecycleOwner(), modList -> {
- recyclerView.getAdapter().notifyDataSetChanged();
- });
+ ConfigViewModel configViewModel = new ConfigViewModel(root);
+ ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel);
+ recyclerView.setAdapter(modManifestAdapter);
+ recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
return root;
}
}
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigViewModel.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigViewModel.java
index e5361fb..01a4430 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigViewModel.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ConfigViewModel.java
@@ -1,29 +1,55 @@
package com.zane.smapiinstaller.ui.config;
+import android.os.FileObserver;
import android.view.View;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.ModAssetsManager;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import androidx.annotation.NonNull;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
+import androidx.recyclerview.widget.RecyclerView;
class ConfigViewModel extends ViewModel {
- private MutableLiveData> modList;
+ @NonNull
+ private List modList;
- public ConfigViewModel(View root) {
+ private RecyclerView view;
+
+ ConfigViewModel(View root) {
ModAssetsManager manager = new ModAssetsManager(root);
- this.modList = new MutableLiveData<>();
- List entryList = manager.findAllInstalledMods();
- Collections.sort(entryList, (a, b)-> a.getName().compareTo(b.getName()));
- this.modList.setValue(entryList);
+ this.modList = manager.findAllInstalledMods();
+ Collections.sort(this.modList, (a, b)-> a.getName().compareTo(b.getName()));
}
- public MutableLiveData> getModList() {
+ @NonNull
+ public List getModList() {
return modList;
}
+
+ public List removeAll(Predicate predicate) {
+ List deletedId = new ArrayList<>();
+ for (int i = modList.size() - 1; i >=0 ; i--) {
+ if(predicate.apply(modList.get(i))) {
+ modList.remove(i);
+ deletedId.add(i);
+ }
+ }
+ return deletedId;
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/config/ModManifestAdapter.java b/app/src/main/java/com/zane/smapiinstaller/ui/config/ModManifestAdapter.java
index 228b5f1..1fb8f9d 100644
--- a/app/src/main/java/com/zane/smapiinstaller/ui/config/ModManifestAdapter.java
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/config/ModManifestAdapter.java
@@ -7,11 +7,16 @@ import android.widget.Button;
import android.widget.TextView;
import com.afollestad.materialdialogs.DialogAction;
+import com.google.common.collect.Iterables;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.CommonLogic;
+import org.apache.commons.lang3.StringUtils;
+import org.zeroturnaround.zip.commons.FileUtils;
+
import java.io.File;
+import java.io.IOException;
import java.util.List;
import androidx.annotation.NonNull;
@@ -23,21 +28,22 @@ import butterknife.ButterKnife;
import butterknife.OnClick;
public class ModManifestAdapter extends RecyclerView.Adapter {
- private List modList;
+ private ConfigViewModel model;
- public ModManifestAdapter(List modList){
- this.modList=modList;
+ public ModManifestAdapter(ConfigViewModel model){
+ this.model=model;
}
+
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.mod_list_item,null);
+ View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.mod_list_item,parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
- ModManifestEntry mod = modList.get(position);
+ ModManifestEntry mod = model.getModList().get(position);
holder.modName.setText(mod.getName());
holder.modDescription.setText(mod.getDescription());
holder.setModPath(mod.getAssetPath());
@@ -45,10 +51,10 @@ public class ModManifestAdapter extends RecyclerView.Adapter removed = model.removeAll(entry -> StringUtils.equals(entry.getAssetPath(), modPath));
+ for (int idx : removed) {
+ notifyItemRemoved(idx);
+ }
+ } catch (IOException e) {
+ CommonLogic.showAlertDialog(itemView, R.string.error, e.getMessage());
+ }
}
}
});
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java
new file mode 100644
index 0000000..725eede
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java
@@ -0,0 +1,69 @@
+package com.zane.smapiinstaller.ui.download;
+
+import androidx.recyclerview.widget.RecyclerView;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.zane.smapiinstaller.R;
+import com.zane.smapiinstaller.entity.DownloadableContent;
+
+import java.util.List;
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link DownloadableContent}
+ */
+public class DownloadableContentAdapter extends RecyclerView.Adapter {
+
+ private final List downloadableContentList;
+
+ public DownloadableContentAdapter(List items) {
+ downloadableContentList = items;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.download_content_item, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ holder.downloadableContent = downloadableContentList.get(position);
+ holder.typeTextView.setText(holder.downloadableContent.getType());
+ holder.nameTextView.setText(holder.downloadableContent.getName());
+ holder.descriptionTextView.setText(holder.downloadableContent.getDescription());
+ }
+
+ @Override
+ public int getItemCount() {
+ return downloadableContentList.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ @BindView(R.id.text_item_type)
+ TextView typeTextView;
+ @BindView(R.id.text_item_name)
+ TextView nameTextView;
+ @BindView(R.id.text_item_description)
+ TextView descriptionTextView;
+ public DownloadableContent downloadableContent;
+
+ public ViewHolder(View view) {
+ super(view);
+ ButterKnife.bind(this, itemView);
+ }
+ @OnClick(R.id.button_remove_content) void removeContent() {
+
+ }
+ @OnClick(R.id.button_download_content) void downloadContent() {
+
+ }
+ }
+}
diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentFragment.java
new file mode 100644
index 0000000..57ec392
--- /dev/null
+++ b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentFragment.java
@@ -0,0 +1,47 @@
+package com.zane.smapiinstaller.ui.download;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.zane.smapiinstaller.R;
+import com.zane.smapiinstaller.entity.DownloadableContentList;
+import com.zane.smapiinstaller.logic.CommonLogic;
+
+/**
+ * A fragment representing a list of Items.
+ */
+public class DownloadableContentFragment extends Fragment {
+
+ /**
+ * Mandatory empty constructor for the fragment manager to instantiate the
+ * fragment (e.g. upon screen orientation changes).
+ */
+ public DownloadableContentFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_download_content_list, container, false);
+
+ // Set the adapter
+ if (view instanceof RecyclerView) {
+ Context context = view.getContext();
+ RecyclerView recyclerView = (RecyclerView) view;
+ recyclerView.setLayoutManager(new LinearLayoutManager(context));
+ DownloadableContentList contentList = CommonLogic.getAssetJson(context, "downloadable_content_list.json", DownloadableContentList.class);
+ recyclerView.setAdapter(new DownloadableContentAdapter(contentList.getContents()));
+ recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
+ }
+ return view;
+ }
+}
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 5f2dd32..25ec3cd 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
@@ -13,6 +13,8 @@ import com.zane.smapiinstaller.logic.ApkPatcher;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.ModAssetsManager;
+import org.apache.commons.lang3.StringUtils;
+
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import butterknife.ButterKnife;
@@ -52,12 +54,12 @@ public class InstallFragment extends Fragment {
CommonLogic.setProgressDialogState(root, dialog, R.string.extracting_package, 0);
String path = patcher.extract();
if (path == null) {
- CommonLogic.showAlertDialog(root, R.string.error, R.string.error_game_not_found);
+ CommonLogic.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
return;
}
CommonLogic.setProgressDialogState(root, dialog, R.string.unpacking_smapi_files, 10);
if (!CommonLogic.unpackSmapiFiles(context, path, false)) {
- CommonLogic.showAlertDialog(root, R.string.error, R.string.failed_to_unpack_smapi_files);
+ CommonLogic.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_unpack_smapi_files)));
return;
}
ModAssetsManager modAssetsManager = new ModAssetsManager(root);
@@ -65,13 +67,13 @@ public class InstallFragment extends Fragment {
modAssetsManager.installDefaultMods();
CommonLogic.setProgressDialogState(root, dialog, R.string.patching_package, 25);
if (!patcher.patch(path)) {
- CommonLogic.showAlertDialog(root, R.string.error, R.string.failed_to_patch_game);
+ CommonLogic.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)));
return;
}
CommonLogic.setProgressDialogState(root, dialog, R.string.signing_package, 55);
String signPath = patcher.sign(path);
if (signPath == null) {
- CommonLogic.showAlertDialog(root, R.string.error, R.string.failed_to_sign_game);
+ CommonLogic.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_sign_game)));
return;
}
CommonLogic.setProgressDialogState(root, dialog, R.string.installing_package, 99);
diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/horizontal_divider.xml
similarity index 100%
rename from app/src/main/res/drawable/divider.xml
rename to app/src/main/res/drawable/horizontal_divider.xml
diff --git a/app/src/main/res/drawable/vertical_divider.xml b/app/src/main/res/drawable/vertical_divider.xml
new file mode 100644
index 0000000..5b27a81
--- /dev/null
+++ b/app/src/main/res/drawable/vertical_divider.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/download_content_item.xml b/app/src/main/res/layout/download_content_item.xml
new file mode 100644
index 0000000..77025c1
--- /dev/null
+++ b/app/src/main/res/layout/download_content_item.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_download_content_list.xml b/app/src/main/res/layout/fragment_download_content_list.xml
new file mode 100644
index 0000000..70c40a5
--- /dev/null
+++ b/app/src/main/res/layout/fragment_download_content_list.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/mod_list_item.xml b/app/src/main/res/layout/mod_list_item.xml
index cfd492e..c4758e5 100644
--- a/app/src/main/res/layout/mod_list_item.xml
+++ b/app/src/main/res/layout/mod_list_item.xml
@@ -3,13 +3,13 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content">
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical">
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index ed88d5e..652f147 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -40,4 +40,8 @@
暂处于内测阶段, Q群:860453392
日志
SMAPI星露谷物语
+ 无法创建以下文件: %s
+ 无法处理AndroidManifest.xml文件
+ 游戏版本不支持,请更新版本或者下载兼容包
+ 下载
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 4ab4520..4639294 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -5,4 +5,5 @@
8dp
176dp
16dp
+ 16dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8be2320..05edd5f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -40,4 +40,8 @@
Still at test stage now, not release yet.
Logs
SMAPI Stardew Valley
+ Failed to create target file : %s
+ Failed to process AndroidManifest.xml file
+ Game version not supported, upgrade or download compat package first
+ Download