1.Mod list search feature

2.Indonesia translate file
3.Code clean up
This commit is contained in:
ZaneYork 2020-03-20 18:30:21 +08:00
parent 4761d3d998
commit 36a64e4ad3
30 changed files with 325 additions and 94 deletions

View File

@ -70,6 +70,7 @@ public class MainActivity extends AppCompatActivity {
} }
} }
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initView(); initView();
@ -126,11 +127,12 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if(item.isCheckable()) { if(item.isCheckable()) {
if (item.isChecked()) if (item.isChecked()) {
item.setChecked(false); item.setChecked(false);
else } else {
item.setChecked(true); item.setChecked(true);
} }
}
ConfigManager manager = new ConfigManager(); ConfigManager manager = new ConfigManager();
FrameworkConfig config = manager.getConfig(); FrameworkConfig config = manager.getConfig();
switch (item.getItemId()) { switch (item.getItemId()) {
@ -190,6 +192,9 @@ public class MainActivity extends AppCompatActivity {
case 8: case 8:
restart = LanguagesManager.setAppLanguage(this, new Locale("pt", "")); restart = LanguagesManager.setAppLanguage(this, new Locale("pt", ""));
break; break;
case 9:
restart = LanguagesManager.setAppLanguage(this, new Locale("in", ""));
break;
default: default:
return; return;
} }
@ -208,23 +213,28 @@ public class MainActivity extends AppCompatActivity {
AppConfig activeTranslator = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.ACTIVE_TRANSLATOR)).build().unique(); AppConfig activeTranslator = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.ACTIVE_TRANSLATOR)).build().unique();
switch (position) { switch (position) {
case 0: case 0:
if(activeTranslator != null) if(activeTranslator != null) {
appConfigDao.delete(activeTranslator); appConfigDao.delete(activeTranslator);
}
break; break;
case 1: case 1:
if(activeTranslator == null) if(activeTranslator == null) {
activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.GOOGLE); activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.GOOGLE);
else } else {
activeTranslator.setValue(TranslateUtil.GOOGLE); activeTranslator.setValue(TranslateUtil.GOOGLE);
}
appConfigDao.insertOrReplace(activeTranslator); appConfigDao.insertOrReplace(activeTranslator);
break; break;
case 2: case 2:
if(activeTranslator == null) if(activeTranslator == null) {
activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.YOU_DAO); activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.YOU_DAO);
else } else {
activeTranslator.setValue(TranslateUtil.YOU_DAO); activeTranslator.setValue(TranslateUtil.YOU_DAO);
}
appConfigDao.insertOrReplace(activeTranslator); appConfigDao.insertOrReplace(activeTranslator);
break; break;
default:
break;
} }
}).show()); }).show());
return true; return true;

View File

@ -22,7 +22,8 @@ public class MainApplication extends Application {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
OkHttpClient okHttpClient = new OkHttpClient.Builder() OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new GzipRequestInterceptor())//开启Gzip压缩 //开启Gzip压缩
.addInterceptor(new GzipRequestInterceptor())
.build(); .build();
OkGo.getInstance().setOkHttpClient(okHttpClient).init(this); OkGo.getInstance().setOkHttpClient(okHttpClient).init(this);
LanguagesManager.init(this); LanguagesManager.init(this);

View File

@ -34,6 +34,12 @@ public class Constants {
*/ */
public static final String APP_CENTER_SECRET = "cb44e94a-7b2f-431e-9ad9-48013ec8c208"; public static final String APP_CENTER_SECRET = "cb44e94a-7b2f-431e-9ad9-48013ec8c208";
public static final String RED_PACKET_CODE = "9188262";
public static final String HIDDEN_FILE_PREFIX = ".";
public static final int URL_LENGTH_LIMIT = 4096;
/** /**
* 有道翻译服务 * 有道翻译服务
*/ */

View File

@ -0,0 +1,7 @@
package com.zane.smapiinstaller.constant;
public class DownloadableContentTypes {
public static final String LOCALE = "LOCALE";
public static final String COMPAT = "COMPAT";
}

View File

@ -0,0 +1,9 @@
package com.zane.smapiinstaller.constant;
public class ManifestPatchConstants {
public static final String APP_NAME = "Stardew Valley";
public static final String PATTERN_MAIN_ACTIVITY = ".MainActivity";
public static final String PATTERN_VERSION_CODE = "versionCode";
}

View File

@ -9,6 +9,7 @@ import lombok.Data;
/** /**
* SMAPI的配置 * SMAPI的配置
*/ */
@SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
@Data @Data
@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility= JsonAutoDetect.Visibility.NONE) @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility= JsonAutoDetect.Visibility.NONE)
public class FrameworkConfig { public class FrameworkConfig {

View File

@ -7,6 +7,7 @@ import lombok.Data;
/** /**
* Mod信息 * Mod信息
*/ */
@SuppressWarnings("ALL")
@Data @Data
public class ModManifestEntry { public class ModManifestEntry {
/** /**
@ -47,7 +48,7 @@ public class ModManifestEntry {
*/ */
private Boolean IsRequired; private Boolean IsRequired;
/* /**
* 翻译后的Description * 翻译后的Description
*/ */
private transient String translatedDescription; private transient String translatedDescription;

View File

@ -6,7 +6,6 @@ import lombok.Data;
@Data @Data
public class YouDaoTranslationDto { public class YouDaoTranslationDto {
//{"type":"ZH_CN2EN","errorCode":0,"elapsedTime":2,"translateResult":[[{"src":"云计算","tgt":"Cloud computing"}],[{"src":"前往合肥","tgt":"Travel to hefei"}]]}
private String type; private String type;
private int errorCode; private int errorCode;
private int elapsedTime; private int elapsedTime;

View File

@ -17,6 +17,7 @@ import com.google.common.io.Files;
import com.zane.smapiinstaller.BuildConfig; import com.zane.smapiinstaller.BuildConfig;
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.entity.ApkFilesManifest; import com.zane.smapiinstaller.entity.ApkFilesManifest;
import com.zane.smapiinstaller.entity.ManifestEntry; import com.zane.smapiinstaller.entity.ManifestEntry;
import com.zane.smapiinstaller.utils.FileUtils; import com.zane.smapiinstaller.utils.FileUtils;
@ -101,11 +102,13 @@ public class ApkPatcher {
* @return 是否成功打包 * @return 是否成功打包
*/ */
public boolean patch(String apkPath) { public boolean patch(String apkPath) {
if (apkPath == null) if (apkPath == null) {
return false; return false;
}
File file = new File(apkPath); File file = new File(apkPath);
if (!file.exists()) if (!file.exists()) {
return false; return false;
}
try { try {
List<ZipEntrySource> zipEntrySourceList = new ArrayList<>(); List<ZipEntrySource> zipEntrySourceList = new ArrayList<>();
byte[] manifest = ZipUtil.unpackEntry(file, "AndroidManifest.xml"); byte[] manifest = ZipUtil.unpackEntry(file, "AndroidManifest.xml");
@ -158,7 +161,7 @@ public class ApkPatcher {
} }
break; break;
case "label": case "label":
if (strObj.contains("Stardew Valley")) { if (strObj.contains(ManifestPatchConstants.APP_NAME)) {
attr.obj = context.getString(R.string.smapi_game_name); attr.obj = context.getString(R.string.smapi_game_name);
} }
break; break;
@ -167,14 +170,16 @@ public class ApkPatcher {
attr.obj = strObj.replace(packageName.get(), Constants.TARGET_PACKAGE_NAME); attr.obj = strObj.replace(packageName.get(), Constants.TARGET_PACKAGE_NAME);
} }
case "name": case "name":
if (strObj.contains(".MainActivity")) { if (strObj.contains(ManifestPatchConstants.PATTERN_MAIN_ACTIVITY)) {
attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "md5723872fa9a204f7f942686e9ed9d0b7d.SMainActivity"); attr.obj = strObj.replaceFirst("\\w+\\.MainActivity", "md5723872fa9a204f7f942686e9ed9d0b7d.SMainActivity");
} }
break; break;
default:
break;
} }
} }
else if(attr.type == NodeVisitor.TYPE_FIRST_INT) { else if(attr.type == NodeVisitor.TYPE_FIRST_INT) {
if(StringUtils.equals(attr.name, "versionCode")){ if(StringUtils.equals(attr.name, ManifestPatchConstants.PATTERN_VERSION_CODE)){
long versionCode = (int) attr.obj; long versionCode = (int) attr.obj;
Iterables.removeIf(manifests, manifest -> { Iterables.removeIf(manifests, manifest -> {
if (versionCode < manifest.getMinBuildCode()) { if (versionCode < manifest.getMinBuildCode()) {
@ -252,11 +257,12 @@ public class ApkPatcher {
*/ */
private Uri fromFile(File file) { private Uri fromFile(File file) {
//Android versions greater than Nougat use FileProvider, others use the URI.fromFile. //Android versions greater than Nougat use FileProvider, others use the URI.fromFile.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file); return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
else } else {
return Uri.fromFile(file); return Uri.fromFile(file);
} }
}
/** /**
* 获取报错内容 * 获取报错内容

View File

@ -137,8 +137,9 @@ public class CommonLogic {
*/ */
public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode) { public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMode) {
List<ManifestEntry> manifestEntries = FileUtils.getAssetJson(context, "smapi_files_manifest.json", new TypeReference<List<ManifestEntry>>() { }); List<ManifestEntry> manifestEntries = FileUtils.getAssetJson(context, "smapi_files_manifest.json", new TypeReference<List<ManifestEntry>>() { });
if (manifestEntries == null) if (manifestEntries == null) {
return false; return false;
}
File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/"); File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/");
if (!basePath.exists()) { if (!basePath.exists()) {
if (!basePath.mkdir()) { if (!basePath.mkdir()) {
@ -176,6 +177,8 @@ public class CommonLogic {
ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile); ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile);
} }
break; break;
default:
break;
} }
} }
return true; return true;

View File

@ -135,8 +135,9 @@ public class ModAssetsManager {
public boolean installDefaultMods() { public boolean installDefaultMods() {
Activity context = CommonLogic.getActivityFromView(root); Activity context = CommonLogic.getActivityFromView(root);
List<ModManifestEntry> modManifestEntries = FileUtils.getAssetJson(context, "mods_manifest.json", new TypeReference<List<ModManifestEntry>>() { }); List<ModManifestEntry> modManifestEntries = FileUtils.getAssetJson(context, "mods_manifest.json", new TypeReference<List<ModManifestEntry>>() { });
if (modManifestEntries == null) if (modManifestEntries == null) {
return false; return false;
}
File modFolder = new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH); File modFolder = new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH);
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID); ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
for (ModManifestEntry mod : modManifestEntries) { for (ModManifestEntry mod : modManifestEntries) {
@ -175,15 +176,17 @@ public class ModAssetsManager {
public void checkModEnvironment(Consumer<Boolean> returnCallback) { public void checkModEnvironment(Consumer<Boolean> returnCallback) {
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(true), ModManifestEntry::getUniqueID); ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(true), ModManifestEntry::getUniqueID);
checkDuplicateMod(installedModMap, (isConfirm) -> { checkDuplicateMod(installedModMap, (isConfirm) -> {
if (isConfirm) if (isConfirm) {
checkUnsatisfiedDependencies(installedModMap, (isConfirm2) -> { checkUnsatisfiedDependencies(installedModMap, (isConfirm2) -> {
if (isConfirm2) if (isConfirm2) {
checkContentpacks(installedModMap, returnCallback); checkContentpacks(installedModMap, returnCallback);
else } else {
returnCallback.accept(false); returnCallback.accept(false);
}
}); });
else } else {
returnCallback.accept(false); returnCallback.accept(false);
}
}); });
} }
@ -213,9 +216,10 @@ public class ModAssetsManager {
} }
})); }));
} }
else else {
returnCallback.accept(true); returnCallback.accept(true);
} }
}
/** /**
* 检查是否有依赖关系缺失 * 检查是否有依赖关系缺失
@ -239,8 +243,9 @@ public class ModAssetsManager {
} }
} }
} }
if (entries.size() != 1) if (entries.size() != 1) {
return true; return true;
}
String version = entries.get(0).getVersion(); String version = entries.get(0).getVersion();
if (StringUtils.isBlank(version)) { if (StringUtils.isBlank(version)) {
return true; return true;
@ -271,9 +276,10 @@ public class ModAssetsManager {
} }
})); }));
} }
else else {
returnCallback.accept(true); returnCallback.accept(true);
} }
}
/** /**
* 检查是否有资源包依赖Mod没有安装 * 检查是否有资源包依赖Mod没有安装
@ -297,8 +303,9 @@ public class ModAssetsManager {
} }
} }
} }
if (entries.size() != 1) if (entries.size() != 1) {
return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID()); return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID());
}
String version = entries.get(0).getVersion(); String version = entries.get(0).getVersion();
if (!StringUtils.isBlank(version)) { if (!StringUtils.isBlank(version)) {
if (StringUtils.isBlank(dependency.getMinimumVersion())) { if (StringUtils.isBlank(dependency.getMinimumVersion())) {
@ -324,7 +331,8 @@ public class ModAssetsManager {
} }
})); }));
} }
else else {
returnCallback.accept(true); returnCallback.accept(true);
} }
}
} }

View File

@ -1,7 +1,6 @@
package com.zane.smapiinstaller.ui.about; package com.zane.smapiinstaller.ui.about;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -21,11 +20,10 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.zane.smapiinstaller.R; import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
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 java.time.Duration;
public class AboutFragment extends Fragment { public class AboutFragment extends Fragment {
@Override @Override
@ -41,7 +39,7 @@ public class AboutFragment extends Fragment {
@OnClick(R.id.button_gplay) void gplay() { @OnClick(R.id.button_gplay) void gplay() {
try try
{ {
this.OpenPlayStore("market://details?id=" + this.getActivity().getPackageName()); this.openPlayStore("market://details?id=" + this.getActivity().getPackageName());
} }
catch (ActivityNotFoundException ex) catch (ActivityNotFoundException ex)
{ {
@ -49,7 +47,7 @@ public class AboutFragment extends Fragment {
} }
} }
private void OpenPlayStore(String url) private void openPlayStore(String url)
{ {
Intent intent = new Intent("android.intent.action.VIEW"); Intent intent = new Intent("android.intent.action.VIEW");
intent.setData(Uri.parse(url)); intent.setData(Uri.parse(url));
@ -90,7 +88,7 @@ public class AboutFragment extends Fragment {
case 3: case 3:
hasInstalledAlipayClient = AlipayDonate.hasInstalledAlipayClient(context); hasInstalledAlipayClient = AlipayDonate.hasInstalledAlipayClient(context);
if (hasInstalledAlipayClient) { if (hasInstalledAlipayClient) {
if (CommonLogic.copyToClipboard(context, "9188262")) { if (CommonLogic.copyToClipboard(context, Constants.RED_PACKET_CODE)) {
PackageManager packageManager = context.getPackageManager(); PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone"); Intent intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -99,6 +97,8 @@ public class AboutFragment extends Fragment {
} }
} }
break; break;
default:
break;
} }
}).show()); }).show());
} }

View File

@ -33,6 +33,7 @@ public class ConfigEditFragment extends Fragment {
@BindView(R.id.button_config_cancel) @BindView(R.id.button_config_cancel)
Button buttonConfigCancel; Button buttonConfigCancel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config_edit, container, false); View root = inflater.inflate(R.layout.fragment_config_edit, container, false);

View File

@ -12,22 +12,25 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import com.zane.smapiinstaller.R; import com.zane.smapiinstaller.R;
public class ConfigFragment extends Fragment { public class ConfigFragment extends Fragment {
@BindView(R.id.view_mod_list) @BindView(R.id.view_mod_list)
RecyclerView recyclerView; RecyclerView recyclerView;
private ConfigViewModel configViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config, container, false); View root = inflater.inflate(R.layout.fragment_config, container, false);
ButterKnife.bind(this, root); ButterKnife.bind(this, root);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
ConfigViewModel configViewModel = new ConfigViewModel(root); configViewModel = new ConfigViewModel(root);
ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel); ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel, configViewModel.getModList());
recyclerView.setAdapter(modManifestAdapter); recyclerView.setAdapter(modManifestAdapter);
configViewModel.registerListChangeListener((list) -> { configViewModel.registerListChangeListener((list) -> {
modManifestAdapter.setList(list); modManifestAdapter.setList(list);
@ -36,4 +39,8 @@ public class ConfigFragment extends Fragment {
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL)); recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
return root; return root;
} }
@OnTextChanged(R.id.button_search) void onSearchMod(CharSequence text){
configViewModel.filter(text);
}
} }

View File

@ -21,6 +21,7 @@ import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.ModAssetsManager; import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.TranslateUtil; import com.zane.smapiinstaller.utils.TranslateUtil;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.greendao.query.Query; import org.greenrobot.greendao.query.Query;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,10 +35,24 @@ class ConfigViewModel extends ViewModel {
@NonNull @NonNull
private List<ModManifestEntry> modList; private List<ModManifestEntry> modList;
private List<ModManifestEntry> filteredModList;
private List<Predicate<List<ModManifestEntry>>> onChangedListener = new ArrayList<>(); private List<Predicate<List<ModManifestEntry>>> onChangedListener = new ArrayList<>();
ConfigViewModel(View root) { ConfigViewModel(View root) {
this.modList = ModAssetsManager.findAllInstalledMods(); this.modList = ModAssetsManager.findAllInstalledMods();
translateLogic(root);
Collections.sort(this.modList, (a, b) -> {
if (a.getContentPackFor() != null && b.getContentPackFor() == null) {
return 1;
} else if (b.getContentPackFor() != null) {
return -1;
}
return a.getName().compareTo(b.getName());
});
}
private void translateLogic(View root) {
MainApplication app = CommonLogic.getApplicationFromView(root); MainApplication app = CommonLogic.getApplicationFromView(root);
if (null != app) { if (null != app) {
DaoSession daoSession = app.getDaoSession(); DaoSession daoSession = app.getDaoSession();
@ -79,14 +94,6 @@ class ConfigViewModel extends ViewModel {
} }
} }
} }
Collections.sort(this.modList, (a, b) -> {
if (a.getContentPackFor() != null && b.getContentPackFor() == null) {
return 1;
} else if (b.getContentPackFor() != null) {
return -1;
}
return a.getName().compareTo(b.getName());
});
} }
@NonNull @NonNull
@ -94,24 +101,12 @@ class ConfigViewModel extends ViewModel {
return modList; return modList;
} }
public Integer findFirst(Predicate<ModManifestEntry> predicate) { public void removeAll(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--) { for (int i = modList.size() - 1; i >= 0; i--) {
if (predicate.apply(modList.get(i))) { if (predicate.apply(modList.get(i))) {
modList.remove(i); modList.remove(i);
deletedId.add(i);
} }
} }
return deletedId;
} }
/** /**
@ -122,4 +117,26 @@ class ConfigViewModel extends ViewModel {
public void registerListChangeListener(Predicate<List<ModManifestEntry>> onChanged) { public void registerListChangeListener(Predicate<List<ModManifestEntry>> onChanged) {
this.onChangedListener.add(onChanged); this.onChangedListener.add(onChanged);
} }
public void filter(CharSequence text) {
if(StringUtils.isBlank(text)) {
filteredModList = modList;
}
else {
filteredModList = Lists.newArrayList(Iterables.filter(modList, mod -> {
if(StringUtils.containsIgnoreCase(mod.getName(), text)) {
return true;
}
if(StringUtils.isNoneBlank(mod.getTranslatedDescription())){
return StringUtils.containsIgnoreCase(mod.getTranslatedDescription(), text);
}
else {
return StringUtils.containsIgnoreCase(mod.getDescription(), text);
}
}));
}
for (Predicate<List<ModManifestEntry>> listener : onChangedListener) {
listener.apply(filteredModList);
}
}
} }

View File

@ -8,7 +8,9 @@ import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.DialogAction;
import com.google.common.base.Predicate;
import com.zane.smapiinstaller.R; import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.entity.ModManifestEntry; import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils; import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils; import com.zane.smapiinstaller.utils.FileUtils;
@ -17,6 +19,7 @@ import org.apache.commons.lang3.StringUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -29,9 +32,11 @@ import butterknife.OnClick;
public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.ViewHolder> { public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.ViewHolder> {
private ConfigViewModel model; private ConfigViewModel model;
private List<ModManifestEntry> modList;
public ModManifestAdapter(ConfigViewModel model){ public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList){
this.model=model; this.model=model;
this.modList = modList;
} }
@NonNull @NonNull
@ -43,7 +48,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ModManifestEntry mod = model.getModList().get(position); ModManifestEntry mod = modList.get(position);
holder.modName.setText(mod.getName()); holder.modName.setText(mod.getName());
holder.modDescription.setText(StringUtils.firstNonBlank(mod.getTranslatedDescription(), mod.getDescription())); holder.modDescription.setText(StringUtils.firstNonBlank(mod.getTranslatedDescription(), mod.getDescription()));
holder.setModPath(mod.getAssetPath()); holder.setModPath(mod.getAssetPath());
@ -51,10 +56,11 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
@Override @Override
public int getItemCount() { public int getItemCount() {
return model.getModList().size(); return modList.size();
} }
public void setList(List<ModManifestEntry> list) { public void setList(List<ModManifestEntry> list) {
this.modList = list;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@ -84,7 +90,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
private void setStrike() { private void setStrike() {
File file = new File(modPath); File file = new File(modPath);
if(StringUtils.startsWith(file.getName(), ".")) { if(StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
modName.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG); modName.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
} }
else { else {
@ -92,6 +98,17 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
} }
} }
public List<Integer> removeAll(Predicate<ModManifestEntry> predicate) {
List<Integer> 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;
}
@OnClick(R.id.button_remove_mod) void removeMod() { @OnClick(R.id.button_remove_mod) void removeMod() {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which)->{ DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which)->{
if (which == DialogAction.POSITIVE) { if (which == DialogAction.POSITIVE) {
@ -99,7 +116,8 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
if (file.exists()) { if (file.exists()) {
try { try {
FileUtils.forceDelete(file); FileUtils.forceDelete(file);
List<Integer> removed = model.removeAll(entry -> StringUtils.equals(entry.getAssetPath(), modPath)); model.removeAll(entry -> StringUtils.equals(entry.getAssetPath(), modPath));
List<Integer> removed = removeAll(entry -> StringUtils.equals(entry.getAssetPath(), modPath));
for (int idx : removed) { for (int idx : removed) {
notifyItemRemoved(idx); notifyItemRemoved(idx);
} }
@ -113,7 +131,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
@OnClick(R.id.button_disable_mod) void disableMod() { @OnClick(R.id.button_disable_mod) void disableMod() {
File file = new File(modPath); File file = new File(modPath);
if(file.exists() && file.isDirectory()) { if(file.exists() && file.isDirectory()) {
if(StringUtils.startsWith(file.getName(), ".")) { if(StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
File newFile = new File(file.getParent(), StringUtils.stripStart(file.getName(), ".")); File newFile = new File(file.getParent(), StringUtils.stripStart(file.getName(), "."));
moveMod(file, newFile); moveMod(file, newFile);
} }
@ -128,12 +146,21 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
} }
} }
public Integer findFirst(Predicate<ModManifestEntry> predicate) {
for (int i = 0; i < modList.size(); i++) {
if (predicate.apply(modList.get(i))) {
return i;
}
}
return null;
}
private void moveMod(File file, File newFile) { private void moveMod(File file, File newFile) {
try { try {
FileUtils.moveDirectory(file, newFile); FileUtils.moveDirectory(file, newFile);
Integer idx = model.findFirst(mod -> StringUtils.equalsIgnoreCase(mod.getAssetPath(), modPath)); Integer idx = findFirst(mod -> StringUtils.equalsIgnoreCase(mod.getAssetPath(), modPath));
if (idx != null) { if (idx != null) {
model.getModList().get(idx).setAssetPath(newFile.getAbsolutePath()); modList.get(idx).setAssetPath(newFile.getAbsolutePath());
notifyItemChanged(idx); notifyItemChanged(idx);
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -19,6 +19,8 @@ import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.model.Progress; import com.lzy.okgo.model.Progress;
import com.lzy.okgo.model.Response; import com.lzy.okgo.model.Response;
import com.zane.smapiinstaller.R; import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DownloadableContentTypes;
import com.zane.smapiinstaller.entity.DownloadableContent; import com.zane.smapiinstaller.entity.DownloadableContent;
import com.zane.smapiinstaller.entity.ModManifestEntry; import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.ModAssetsManager; import com.zane.smapiinstaller.logic.ModAssetsManager;
@ -126,7 +128,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
void downloadContent() { void downloadContent() {
Context context = itemView.getContext(); Context context = itemView.getContext();
ModManifestEntry modManifestEntry = null; ModManifestEntry modManifestEntry = null;
if (StringUtils.equals(downloadableContent.getType(), "LOCALE")) { if (StringUtils.equals(downloadableContent.getType(), DownloadableContentTypes.LOCALE)) {
modManifestEntry = ModAssetsManager.findFirstModIf(mod -> StringUtils.equals(mod.getUniqueID(), "ZaneYork.CustomLocalization") || StringUtils.equals(mod.getUniqueID(), "SMAPI.CustomLocalization")); modManifestEntry = ModAssetsManager.findFirstModIf(mod -> StringUtils.equals(mod.getUniqueID(), "ZaneYork.CustomLocalization") || StringUtils.equals(mod.getUniqueID(), "SMAPI.CustomLocalization"));
if (modManifestEntry == null) { if (modManifestEntry == null) {
DialogUtils.showAlertDialog(itemView, R.string.error, String.format(context.getString(R.string.error_depends_on_mod), context.getString(R.string.locale_pack), "ZaneYork.CustomLocalization")); DialogUtils.showAlertDialog(itemView, R.string.error, String.format(context.getString(R.string.error_depends_on_mod), context.getString(R.string.locale_pack), "ZaneYork.CustomLocalization"));
@ -142,8 +144,9 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
return; return;
} }
} }
if (downloading.get()) if (downloading.get()) {
return; return;
}
downloading.set(true); downloading.set(true);
ModManifestEntry finalModManifestEntry = modManifestEntry; ModManifestEntry finalModManifestEntry = modManifestEntry;
AtomicReference<MaterialDialog> dialogRef = DialogUtils.showProgressDialog(itemView, R.string.progress, ""); AtomicReference<MaterialDialog> dialogRef = DialogUtils.showProgressDialog(itemView, R.string.progress, "");
@ -184,7 +187,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
} }
private void unpackLogic(Context context, File downloadedFile, ModManifestEntry finalModManifestEntry) { private void unpackLogic(Context context, File downloadedFile, ModManifestEntry finalModManifestEntry) {
if (StringUtils.equals(downloadableContent.getType(), "LOCALE")) { if (StringUtils.equals(downloadableContent.getType(), DownloadableContentTypes.LOCALE)) {
if (finalModManifestEntry != null) { if (finalModManifestEntry != null) {
ZipUtil.unpack(downloadedFile, new File(finalModManifestEntry.getAssetPath())); ZipUtil.unpack(downloadedFile, new File(finalModManifestEntry.getAssetPath()));
} }

View File

@ -30,6 +30,7 @@ public class HelpFragment extends Fragment {
@BindView(R.id.view_help_list) @BindView(R.id.view_help_list)
RecyclerView recyclerView; RecyclerView recyclerView;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_help, container, false); View root = inflater.inflate(R.layout.fragment_help, container, false);

View File

@ -32,6 +32,7 @@ public class InstallFragment extends Fragment {
private View root; private View root;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_install, container, false); root = inflater.inflate(R.layout.fragment_install, container, false);
@ -41,7 +42,7 @@ public class InstallFragment extends Fragment {
} }
@OnClick(R.id.button_install) @OnClick(R.id.button_install)
void Install() { void install() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
DialogUtils.showConfirmDialog(root, R.string.confirm, R.string.android_version_confirm, ((dialog, which) -> { DialogUtils.showConfirmDialog(root, R.string.confirm, R.string.android_version_confirm, ((dialog, which) -> {
if (which == DialogAction.POSITIVE) { if (which == DialogAction.POSITIVE) {

View File

@ -1,5 +1,7 @@
package com.zane.smapiinstaller.utils; package com.zane.smapiinstaller.utils;
import com.lzy.okgo.model.HttpHeaders;
import java.io.IOException; import java.io.IOException;
import okhttp3.Interceptor; import okhttp3.Interceptor;
@ -15,12 +17,12 @@ public class GzipRequestInterceptor implements Interceptor {
@Override @Override
public Response intercept(Chain chain) throws IOException { public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request(); Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { if (originalRequest.body() == null || originalRequest.header(HttpHeaders.HEAD_KEY_CONTENT_ENCODING) != null) {
return chain.proceed(originalRequest); return chain.proceed(originalRequest);
} }
Request compressedRequest = originalRequest.newBuilder() Request compressedRequest = originalRequest.newBuilder()
.header("Content-Encoding", "gzip") .header(HttpHeaders.HEAD_KEY_CONTENT_ENCODING, "gzip")
.method(originalRequest.method(), gzip(originalRequest.body())) .method(originalRequest.method(), gzip(originalRequest.body()))
.build(); .build();
return chain.proceed(compressedRequest); return chain.proceed(compressedRequest);

View File

@ -12,16 +12,16 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* JSON工具类 * JSON工具类
*/ */
public class JSONUtil { public class JSONUtil {
private static final ObjectMapper mapper = new ObjectMapper(); private static final ObjectMapper MAPPER = new ObjectMapper();
static { static {
// 允许未定义的属性 // 允许未定义的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
// 允许尾部额外的逗号 // 允许尾部额外的逗号
mapper.configure(JsonReadFeature.ALLOW_TRAILING_COMMA.mappedFeature(), true); MAPPER.configure(JsonReadFeature.ALLOW_TRAILING_COMMA.mappedFeature(), true);
// 允许数组设置空值 // 允许数组设置空值
mapper.configure(JsonReadFeature.ALLOW_MISSING_VALUES.mappedFeature(), true); MAPPER.configure(JsonReadFeature.ALLOW_MISSING_VALUES.mappedFeature(), true);
// 允许Java注释 // 允许Java注释
mapper.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS.mappedFeature(), true); MAPPER.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS.mappedFeature(), true);
} }
/** /**
@ -31,7 +31,7 @@ public class JSONUtil {
* @throws Exception 异常 * @throws Exception 异常
*/ */
public static String toJson(Object object) throws Exception { public static String toJson(Object object) throws Exception {
return mapper.writeValueAsString(object); return MAPPER.writeValueAsString(object);
} }
/** /**
@ -40,7 +40,7 @@ public class JSONUtil {
* @throws JsonProcessingException 异常 * @throws JsonProcessingException 异常
*/ */
public static void checkJson(String jsonString) throws JsonProcessingException { public static void checkJson(String jsonString) throws JsonProcessingException {
mapper.readValue(jsonString, Object.class); MAPPER.readValue(jsonString, Object.class);
} }
/** /**
@ -52,7 +52,7 @@ public class JSONUtil {
*/ */
public static <T> T fromJson(String jsonString, Class<T> cls) { public static <T> T fromJson(String jsonString, Class<T> cls) {
try { try {
return mapper.readValue(jsonString, cls); return MAPPER.readValue(jsonString, cls);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
Log.e("JSON", "Deserialize error", e); Log.e("JSON", "Deserialize error", e);
} }
@ -68,7 +68,7 @@ public class JSONUtil {
*/ */
public static <T> T fromJson(String jsonString, TypeReference<T> type) { public static <T> T fromJson(String jsonString, TypeReference<T> type) {
try { try {
return mapper.readValue(jsonString, type); return MAPPER.readValue(jsonString, type);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
Log.e("JSON", "Deserialize error", e); Log.e("JSON", "Deserialize error", e);
} }

View File

@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
public class TranslateUtil { public class TranslateUtil {
@ -26,23 +27,25 @@ public class TranslateUtil {
public static final String YOU_DAO = "YouDao"; public static final String YOU_DAO = "YouDao";
public static void translateText(List<String> textList, String translator, String locale, Predicate<List<TranslationResult>> resultCallback) { public static void translateText(List<String> textList, String translator, String locale, Predicate<List<TranslationResult>> resultCallback) {
if(textList == null || textList.size() == 0) if(textList == null || textList.size() == 0) {
return; return;
}
textList = Lists.newArrayList(Iterables.filter(textList, item -> StringUtils.isNoneBlank(item) && !item.contains("\n"))); textList = Lists.newArrayList(Iterables.filter(textList, item -> StringUtils.isNoneBlank(item) && !item.contains("\n")));
if(textList.size() == 0) { if(textList.size() == 0) {
return; return;
} }
String queryText = Joiner.on("%0A").join(textList); String queryText = Joiner.on("%0A").join(textList);
if(queryText.length() > 4096) { if(queryText.length() > Constants.URL_LENGTH_LIMIT) {
if(textList.size() == 1) if(textList.size() == 1) {
return; return;
}
List<String> subListA = textList.subList(0, textList.size() / 2); List<String> subListA = textList.subList(0, textList.size() / 2);
translateText(subListA, translator, locale, resultCallback); translateText(subListA, translator, locale, resultCallback);
List<String> subListB = textList.subList(textList.size() / 2, textList.size()); List<String> subListB = textList.subList(textList.size() / 2, textList.size());
translateText(subListB, translator, locale, resultCallback); translateText(subListB, translator, locale, resultCallback);
} }
if(StringUtils.equalsIgnoreCase(translator, YOU_DAO)) { if(StringUtils.equalsIgnoreCase(translator, YOU_DAO)) {
if (!StringUtils.equalsAnyIgnoreCase(locale, "zh")) { if (!StringUtils.equalsAnyIgnoreCase(locale, Locale.CHINA.getLanguage())) {
return; return;
} }
OkGo.<String>get(String.format(Constants.TRANSLATE_SERVICE_URL_YOUDAO, queryText)).execute(new StringCallback() { OkGo.<String>get(String.format(Constants.TRANSLATE_SERVICE_URL_YOUDAO, queryText)).execute(new StringCallback() {
@ -60,8 +63,9 @@ public class TranslateUtil {
result.setTranslator(translator); result.setTranslator(translator);
result.setCreateTime(System.currentTimeMillis()); result.setCreateTime(System.currentTimeMillis());
for (YouDaoTranslationDto.Entry entry : list) { for (YouDaoTranslationDto.Entry entry : list) {
if (entry == null) if (entry == null) {
continue; continue;
}
result.setOrigin(result.getOrigin() + entry.getSrc()); result.setOrigin(result.getOrigin() + entry.getSrc());
result.setTranslation(result.getTranslation() + entry.getTgt()); result.setTranslation(result.getTranslation() + entry.getTgt());
} }

View File

@ -37,12 +37,14 @@ public class VersionUtil {
} catch (Exception ignored2) { } catch (Exception ignored2) {
} }
} }
if(StringUtils.equals(listA.get(i), listB.get(i))) if(StringUtils.equals(listA.get(i), listB.get(i))) {
continue; continue;
if(intA != null && intB == null) }
if(intA != null && intB == null) {
return 1; return 1;
else if(intA == null) } else if(intA == null) {
return -1; return -1;
}
return listA.get(i).compareTo(listB.get(i)); return listA.get(i).compareTo(listB.get(i));
} }
return Integer.compare(listA.size(), listB.size()); return Integer.compare(listA.size(), listB.size());
@ -83,9 +85,10 @@ public class VersionUtil {
return 1; return 1;
} }
int compare = compareVersionSection(versionSectionsA.get(i), versionSectionsB.get(i)); int compare = compareVersionSection(versionSectionsA.get(i), versionSectionsB.get(i));
if(compare != 0) if(compare != 0) {
return compare; return compare;
} }
}
if(versionSectionsA.size() < versionSectionsB.size()) { if(versionSectionsA.size() < versionSectionsB.size()) {
if(isZero(versionSectionsB.subList(versionSectionsA.size(), versionSectionsB.size()))) { if(isZero(versionSectionsB.subList(versionSectionsA.size(), versionSectionsB.size()))) {
return 0; return 0;

View File

@ -6,10 +6,52 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.config.ConfigFragment"> tools:context=".ui.config.ConfigFragment">
<EditText
android:id="@+id/button_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/guideline_v1"
app:layout_constraintEnd_toStartOf="@id/guideline_v2"
app:layout_constraintTop_toBottomOf="@id/guideline_h1"
android:hint="@android:string/search_go"
android:inputType="text"
android:importantForAutofill="no"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/view_mod_list" android:id="@+id/view_mod_list"
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent"> android:layout_height="0dp"
</androidx.recyclerview.widget.RecyclerView> app:layout_constraintStart_toEndOf="@id/guideline_v1"
app:layout_constraintEnd_toStartOf="@id/guideline_v2"
app:layout_constraintTop_toBottomOf="@id/button_search"
app:layout_constraintBottom_toTopOf="@id/guideline_h2"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_h1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="10dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_h2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="60dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_v1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="10dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_v2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="10dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -32,7 +32,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/view_help_list" android:id="@+id/view_help_list"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
app:layout_constraintStart_toEndOf="@id/guideline_v1" app:layout_constraintStart_toEndOf="@id/guideline_v1"

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="abort">Terminación</string>
<string name="android_version_confirm">La versión de su sistema es demasiado antigua, lo que puede hacer que 0Harmony no sea válido. Se recomienda actualizar a Android 6 y superior</string>
<string name="app_name">Instalador SMAPI</string>
<string name="button_compat">Compatibilidad</string>
<string name="button_donation_text">Donar</string>
<string name="button_gplay">Detalles de la tienda de Google</string>
<string name="button_install">Instalar</string>
<string name="button_logs">Registro</string>
<string name="button_nexus">Nexus</string>
<string name="button_qq_group_1_text">QQ group①: 860453392</string>
<string name="button_qq_group_2_text">QQ group②: 1078428449</string>
<string name="button_release"> Sitio web oficial</string>
<string name="cancel">Cancelar</string>
<string name="confirm">Confirmar</string>
<string name="confirm_delete_content">¿Estás seguro de que deseas eliminar este contenido?</string>
<string name="confirm_disable_mod"> ¿Seguro que quieresdeshabilitar este contenido?</string>
<string name="continue_text">Continuar</string>
<string name="download_unpack_success">Descarga e instalación completadas</string>
<string name="downloading">Descargando:%1$dKB /%2$dKB</string>
<string name="duplicate_mod_found">Se han encontrado varias copias de este mod. Elimina los mods duplicados de:%s</string>
<string name="error">Mal</string>
<string name="error_depends_on_mod">%1$s depende de la interfaz de%2$s, instálelo primero</string>
<string name="error_depends_on_mod_version">%1$s depende de la versión%2$s%3$s, actualícela primero</string>
<string name="error_failed_to_create_file">No se pudieron crear los siguientes archivos:%s</string>
<string name="error_failed_to_download">No se puede descargar el recurso de destino</string>
<string name="error_failed_to_repair"> No se puede reparar el entorno SMAPI</string>
<string name="error_game_not_found">Incapaz de encontrar el cuerpo del juego, ¿instalaste Stardew Valley?</string>
<string name="error_illegal_path">Por favor ingrese una ruta válida</string>
<string name="error_no_supported_game_version">La versión del juego no es compatible, actualice laversión o descargue el paquete compatible</string>
<string name="error_smapi_not_installed">SMAPI no está instalado, primero haga clic en el botón instalar</string>
<string name="extracting_package">Extrayendo paquete de instalación</string>
<string name="failed_to_patch_game"> No se puede modificar el paquete de instalación, póngase en contacto con el desarrollador para obtener ayuda</string>
<string name="failed_to_process_manifest">No se puede procesar el archivo AndroidManifest.xml</string>
<string name="failed_to_sign_game">No se puede firmar el paquete de instalación, póngase en contacto con el desarrollador para obtener ayuda</string>
<string name="failed_to_unpack_smapi_files">No se puede descomprimir el entorno SMAPI</string>
<string name="info">Pista</string>
<string name="input">Entrar</string>
<string name="input_mods_path">Por favor, introduzca la ruta de Mods</string>
<string name="install_progress_title">Progreso de la instalación</string>
<string name="installing_package">Instalando</string>
<string name="locale_pack">Paquete de idiomas</string>
<string name="menu_about">Acerca de</string>
<string name="menu_config">Config</string>
<string name="menu_config_edit">Editar</string>
<string name="menu_download">Descargar</string>
<string name="menu_help">Ayuda</string>
<string name="menu_install">Instalar</string>
<string name="nav_header_subtitle">ZaneYork@qq.com</string>
<string name="nav_header_title">Instalador SMAPI</string>
<string name="ok">OK</string>
<string name="patching_package">Instalar el parche SMAPI</string>
<string name="progress">Progreso</string>
<string name="save">Guardar</string>
<string name="settings_check_for_updates">Buscar actualizaciones</string>
<string name="settings_developer_mode">Modo desarrollador</string>
<string name="settings_set_language">Idioma</string>
<string name="settings_set_mod_path">Establecer la posición de Mods</string>
<string name="settings_translation_service">Servicios Traducción</string>
<string name="settings_verbose_logging">Registro detallado</string>
<string name="signing_package">Firmando paquete deinstalación</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">Versión SMAPI: 3.3.2.3</string>
<string name="text_install_tip1">Nota: Requiere la versión del juego 1.4.5.138 o superior</string>
<string name="text_install_tip2">El cuerpo del juego debe instalarse durante la actualización o instalación</string>
<string name="unpacking_smapi_files">Desempacando</string>
</resources>

View File

@ -16,6 +16,7 @@
<item>Español</item> <item>Español</item>
<item>Français</item> <item>Français</item>
<item>Português</item> <item>Português</item>
<item>Bahasa Indonesia</item>
</string-array> </string-array>
<string-array name="translators"> <string-array name="translators">
<item>關閉</item> <item>關閉</item>

View File

@ -16,6 +16,7 @@
<item>Español</item> <item>Español</item>
<item>Français</item> <item>Français</item>
<item>Português</item> <item>Português</item>
<item>Bahasa Indonesia</item>
</string-array> </string-array>
<string-array name="translators"> <string-array name="translators">
<item>關閉</item> <item>關閉</item>

View File

@ -16,6 +16,7 @@
<item>Español</item> <item>Español</item>
<item>Français</item> <item>Français</item>
<item>Português</item> <item>Português</item>
<item>Bahasa Indonesia</item>
</string-array> </string-array>
<string-array name="translators"> <string-array name="translators">
<item>关闭</item> <item>关闭</item>

View File

@ -16,6 +16,7 @@
<item>Español</item> <item>Español</item>
<item>Français</item> <item>Français</item>
<item>Português</item> <item>Português</item>
<item>Bahasa Indonesia</item>
</string-array> </string-array>
<string-array name="translators"> <string-array name="translators">
<item>OFF</item> <item>OFF</item>