1.Mod disable/enable feature

2.Mods location switch setting
3.Bug fix
This commit is contained in:
ZaneYork 2020-03-16 21:11:58 +08:00
parent 006196ae42
commit ff28b20fda
18 changed files with 190 additions and 38 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "com.zane.smapiinstaller"
minSdkVersion 19
targetSdkVersion 28
versionCode 12
versionName "1.2.3"
versionCode 13
versionName "1.3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View File

@ -3,6 +3,7 @@ package com.zane.smapiinstaller;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
@ -14,6 +15,11 @@ import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.entity.FrameworkConfig;
import com.zane.smapiinstaller.logic.ConfigManager;
import com.zane.smapiinstaller.logic.GameLauncher;
import com.zane.smapiinstaller.utils.DialogUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
@ -57,7 +63,7 @@ public class MainActivity extends AppCompatActivity {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initView();
} else {
requestPermissions();
this.finish();
}
}
@ -102,6 +108,7 @@ public class MainActivity extends AppCompatActivity {
menu.findItem(R.id.settings_verbose_logging).setChecked(config.isVerboseLogging());
menu.findItem(R.id.settings_check_for_updates).setChecked(config.isCheckForUpdates());
menu.findItem(R.id.settings_developer_mode).setChecked(config.isDeveloperMode());
Constants.MOD_PATH = config.getModsPath();
return super.onPrepareOptionsMenu(menu);
}
@ -125,6 +132,22 @@ public class MainActivity extends AppCompatActivity {
case R.id.settings_developer_mode:
config.setDeveloperMode(item.isChecked());
break;
case R.id.settings_set_mod_path:
DialogUtils.showInputDialog(this, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
if(StringUtils.isNoneBlank(input)) {
String pathString = input.toString();
File file = new File(Environment.getExternalStorageDirectory(), pathString);
if(file.exists() && file.isDirectory()) {
Constants.MOD_PATH = pathString;
config.setModsPath(pathString);
manager.flushConfig();
}
else {
DialogUtils.showAlertDialog(drawer, R.string.error, R.string.error_illegal_path);
}
}
});
return true;
default:
return super.onOptionsItemSelected(item);
}

View File

@ -7,7 +7,7 @@ public class Constants {
/**
* Mod安装路径
*/
public static final String MOD_PATH = "StardewValley/Mods";
public static String MOD_PATH = "StardewValley/Mods";
/**
* 日志路径
*/

View File

@ -27,4 +27,10 @@ public class FrameworkConfig {
*/
@JsonProperty("DeveloperMode")
private boolean DeveloperMode = false;
/**
* Mod存放位置
*/
@JsonProperty("ModsPath")
private String ModsPath = "StardewValley/Mods";
}

View File

@ -84,6 +84,15 @@ public class ModAssetsManager {
* @return Mod信息列表
*/
public static List<ModManifestEntry> findAllInstalledMods() {
return findAllInstalledMods(false);
}
/**
* 查找全部已识别Mod
* @param ignoreDisabledMod 是否忽略禁用的mod
* @return Mod信息列表
*/
public static List<ModManifestEntry> findAllInstalledMods(boolean ignoreDisabledMod) {
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
List<ModManifestEntry> mods = new ArrayList<>(30);
@ -98,6 +107,9 @@ public class ModAssetsManager {
ModManifestEntry manifest = FileUtils.getFileJson(file, ModManifestEntry.class);
foundManifest = true;
if (manifest != null && StringUtils.isNoneBlank(manifest.getUniqueID())) {
if(ignoreDisabledMod && StringUtils.startsWith(file.getParentFile().getName(), ".")) {
break;
}
manifest.setAssetPath(file.getParentFile().getAbsolutePath());
mods.add(manifest);
}
@ -161,7 +173,7 @@ public class ModAssetsManager {
* @param returnCallback 回调函数
*/
public void checkModEnvironment(Consumer<Boolean> returnCallback) {
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(true), ModManifestEntry::getUniqueID);
checkDuplicateMod(installedModMap, (isConfirm) -> {
if (isConfirm)
checkUnsatisfiedDependencies(installedModMap, (isConfirm2) -> {

View File

@ -1,25 +1,16 @@
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;
@ -48,6 +39,15 @@ class ConfigViewModel extends ViewModel {
return modList;
}
public Integer findFirst(Predicate<ModManifestEntry> predicate) {
for (int i = 0; i < modList.size(); i++) {
if(predicate.apply(modList.get(i))) {
return i;
}
}
return null;
}
public List<Integer> removeAll(Predicate<ModManifestEntry> predicate) {
List<Integer> deletedId = new ArrayList<>();
for (int i = modList.size() - 1; i >=0 ; i--) {

View File

@ -1,5 +1,6 @@
package com.zane.smapiinstaller.ui.config;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -10,9 +11,9 @@ import com.afollestad.materialdialogs.DialogAction;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.zeroturnaround.zip.commons.FileUtils;
import java.io.File;
import java.io.IOException;
@ -64,6 +65,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
else {
configModButton.setVisibility(View.VISIBLE);
}
setStrike();
}
@BindView(R.id.button_config_mod)
Button configModButton;
@ -75,6 +77,17 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
super(itemView);
ButterKnife.bind(this, itemView);
}
private void setStrike() {
File file = new File(modPath);
if(StringUtils.startsWith(file.getName(), ".")) {
modName.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
}
else {
modName.getPaint().setFlags(modName.getPaint().getFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
}
@OnClick(R.id.button_remove_mod) void removeMod() {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which)->{
if (which == DialogAction.POSITIVE) {
@ -93,6 +106,37 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
}
});
}
@OnClick(R.id.button_disable_mod) void disableMod() {
File file = new File(modPath);
if(file.exists() && file.isDirectory()) {
if(StringUtils.startsWith(file.getName(), ".")) {
File newFile = new File(file.getParent(), StringUtils.stripStart(file.getName(), "."));
moveMod(file, newFile);
}
else {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_disable_mod, (dialog, which)-> {
if(which == DialogAction.POSITIVE) {
File newFile = new File(file.getParent(), "." + file.getName());
moveMod(file, newFile);
}
});
}
}
}
private void moveMod(File file, File newFile) {
try {
FileUtils.moveDirectory(file, newFile);
Integer idx = model.findFirst(mod -> StringUtils.equalsIgnoreCase(mod.getAssetPath(), modPath));
if (idx != null) {
model.getModList().get(idx).setAssetPath(newFile.getAbsolutePath());
notifyItemChanged(idx);
}
} catch (IOException e) {
DialogUtils.showAlertDialog(itemView, R.string.error, e.getLocalizedMessage());
}
}
@OnClick(R.id.button_config_mod) void configMod() {
File file = new File(modPath, "config.json");
if(file.exists()) {

View File

@ -23,10 +23,10 @@ import com.zane.smapiinstaller.entity.DownloadableContent;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.commons.FileUtils;
import java.io.File;
import java.io.IOException;
@ -152,9 +152,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
public void onError(Response<File> response) {
super.onError(response);
MaterialDialog dialog = dialogRef.get();
if (dialog != null && !dialog.isCancelled()) {
dialog.dismiss();
}
DialogUtils.dismissDialog(itemView, dialog);
downloading.set(false);
DialogUtils.showAlertDialog(itemView, R.string.error, R.string.error_failed_to_download);
}
@ -172,9 +170,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
@Override
public void onSuccess(Response<File> response) {
MaterialDialog dialog = dialogRef.get();
if (dialog != null && !dialog.isCancelled()) {
dialog.dismiss();
}
DialogUtils.dismissDialog(itemView, dialog);
downloading.set(false);
File downloadedFile = response.body();
String hash = com.zane.smapiinstaller.utils.FileUtils.getFileHash(downloadedFile);

View File

@ -105,9 +105,7 @@ public class InstallFragment extends Fragment {
DialogUtils.showAlertDialog(root, R.string.error, e.getLocalizedMessage());
}
finally {
if (!dialog.isCancelled()) {
dialog.dismiss();
}
DialogUtils.dismissDialog(root, dialog);
}
});
task.start();

View File

@ -1,6 +1,8 @@
package com.zane.smapiinstaller.utils;
import android.app.Activity;
import android.app.Dialog;
import android.text.InputType;
import android.view.View;
import com.afollestad.materialdialogs.MaterialDialog;
@ -9,12 +11,15 @@ import com.zane.smapiinstaller.logic.CommonLogic;
import java.util.concurrent.atomic.AtomicReference;
import androidx.drawerlayout.widget.DrawerLayout;
/**
* 对话框相关工具类
*/
public class DialogUtils {
/**
* 设置进度条状态
*
* @param view context容器
* @param dialog 对话框
* @param message 消息
@ -32,6 +37,7 @@ public class DialogUtils {
/**
* 显示警告对话框
*
* @param view context容器
* @param title 标题
* @param message 消息
@ -45,8 +51,9 @@ public class DialogUtils {
/**
* 显示警告对话框
* @param view context容器
* @param title 标题
*
* @param view context容器
* @param title 标题
* @param message 消息
*/
public static void showAlertDialog(View view, int title, int message) {
@ -58,6 +65,7 @@ public class DialogUtils {
/**
* 显示确认对话框
*
* @param view context容器
* @param title 标题
* @param message 消息
@ -72,6 +80,7 @@ public class DialogUtils {
/**
* 显示确认对话框
*
* @param view context容器
* @param title 标题
* @param message 消息
@ -86,6 +95,7 @@ public class DialogUtils {
/**
* 显示进度条
*
* @param view context容器
* @param title 标题
* @param message 消息
@ -107,4 +117,25 @@ public class DialogUtils {
}
return reference;
}
public static void dismissDialog(View view, MaterialDialog dialog) {
Activity activity = CommonLogic.getActivityFromView(view);
if (activity != null && !activity.isFinishing()) {
if (dialog != null && !dialog.isCancelled()) {
dialog.dismiss();
}
}
}
public static void showInputDialog(Activity activity, int title, int content, String hint, String prefill, MaterialDialog.InputCallback callback) {
if (activity != null && !activity.isFinishing()) {
activity.runOnUiThread(() -> new MaterialDialog.Builder(activity)
.title(title)
.content(content)
.inputType(InputType.TYPE_CLASS_TEXT)
.input(hint, prefill, callback)
.show()
);
}
}
}

View File

@ -25,7 +25,7 @@ import java.nio.charset.StandardCharsets;
/**
* 文件工具类
*/
public class FileUtils {
public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
/**
* 读取文本文件
* @param file 文件

View File

@ -12,13 +12,30 @@
android:divider="@drawable/horizontal_divider"
android:showDividers="middle"
android:orientation="vertical">
<TextView
android:id="@+id/text_view_mod_name"
android:gravity="start"
android:textSize="20sp"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/text_view_mod_name"
android:gravity="start"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_disable_mod"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="0px"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_disable_mod"
app:layout_constraintStart_toEndOf="@id/text_view_mod_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@android:drawable/ic_menu_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/text_view_mod_description"
android:gravity="start"

View File

@ -19,4 +19,9 @@
android:title="@string/settings_developer_mode"
app:showAsAction="never" />
</group>
<item
android:id="@+id/settings_set_mod_path"
android:orderInCategory="103"
android:title="@string/settings_set_mod_path"
app:showAsAction="never" />
</menu>

View File

@ -54,8 +54,13 @@
<string name="button_donation_text">捐贈</string>
<string name="button_qq_group_2_text">QQ群②: 1078428449</string>
<string name="button_gplay" >谷歌商店详情</string>
<string name="smapi_version">SMAPI版本: 3.3.2.2</string>
<string name="smapi_version">SMAPI版本: 3.3.2.3</string>
<string name="settings_verbose_logging">詳細日誌</string>
<string name="settings_check_for_updates">檢查更新</string>
<string name="settings_developer_mode">開發者模式</string>
<string name="confirm_disable_mod">確定禁用該內容?</string>
<string name="settings_set_mod_path">設置Mods位置</string>
<string name="input">輸入</string>
<string name="input_mods_path">請輸入Mods路徑</string>
<string name="error_illegal_path">請輸入一個有效的路徑</string>
</resources>

View File

@ -54,8 +54,13 @@
<string name="button_donation_text">捐贈</string>
<string name="button_qq_group_2_text">QQ群②: 1078428449</string>
<string name="button_gplay" >谷歌商店详情</string>
<string name="smapi_version">SMAPI版本: 3.3.2.2</string>
<string name="smapi_version">SMAPI版本: 3.3.2.3</string>
<string name="settings_verbose_logging">詳細日誌</string>
<string name="settings_developer_mode">開發者模式</string>
<string name="settings_check_for_updates">檢查更新</string>
<string name="confirm_disable_mod">確定禁用該內容?</string>
<string name="settings_set_mod_path">設置Mods位置</string>
<string name="input">輸入</string>
<string name="input_mods_path">請輸入Mods路徑</string>
<string name="error_illegal_path">請輸入一個有效的路徑</string>
</resources>

View File

@ -54,8 +54,13 @@
<string name="button_qq_group_2_text">QQ群②: 1078428449</string>
<string name="error_depends_on_mod_version">%1$s依赖%2$s %3$s版本请先更新它</string>
<string name="button_gplay">谷歌商店详情</string>
<string name="smapi_version">SMAPI版本: 3.3.2.2</string>
<string name="smapi_version">SMAPI版本: 3.3.2.3</string>
<string name="settings_verbose_logging">详细日志</string>
<string name="settings_developer_mode">开发者模式</string>
<string name="settings_check_for_updates">检查更新</string>
<string name="confirm_disable_mod">确定禁用该内容?</string>
<string name="settings_set_mod_path">设置Mods位置</string>
<string name="input">输入</string>
<string name="input_mods_path">请输入Mods路径</string>
<string name="error_illegal_path">请输入一个有效的路径</string>
</resources>

View File

@ -58,8 +58,13 @@
<string name="button_donation_text">Donation</string>
<string name="toast_redpacket_message" translatable="false">红包码已复制\n支付宝首页搜索“9188262” 立即领红包</string>
<string name="button_gplay">View in Play Store</string>
<string name="smapi_version">SMAPI Version: 3.3.2.2</string>
<string name="smapi_version">SMAPI Version: 3.3.2.3</string>
<string name="settings_verbose_logging">Verbose Logging</string>
<string name="settings_check_for_updates">Check For Updates</string>
<string name="settings_developer_mode">Developer Mode</string>
<string name="confirm_disable_mod">Are you sure to disable it?</string>
<string name="settings_set_mod_path">Set Mods Path</string>
<string name="input">Input</string>
<string name="input_mods_path">Please input mods path</string>
<string name="error_illegal_path">Please input a valid path</string>
</resources>