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

View File

@ -22,7 +22,8 @@ public class MainApplication extends Application {
public void onCreate() {
super.onCreate();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new GzipRequestInterceptor())//开启Gzip压缩
//开启Gzip压缩
.addInterceptor(new GzipRequestInterceptor())
.build();
OkGo.getInstance().setOkHttpClient(okHttpClient).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 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的配置
*/
@SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
@Data
@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility= JsonAutoDetect.Visibility.NONE)
public class FrameworkConfig {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,6 +33,7 @@ public class ConfigEditFragment extends Fragment {
@BindView(R.id.button_config_cancel)
Button buttonConfigCancel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
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 butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import com.zane.smapiinstaller.R;
public class ConfigFragment extends Fragment {
@BindView(R.id.view_mod_list)
RecyclerView recyclerView;
private ConfigViewModel configViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config, container, false);
ButterKnife.bind(this, root);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
ConfigViewModel configViewModel = new ConfigViewModel(root);
ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel);
configViewModel = new ConfigViewModel(root);
ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel, configViewModel.getModList());
recyclerView.setAdapter(modManifestAdapter);
configViewModel.registerListChangeListener((list) -> {
modManifestAdapter.setList(list);
@ -36,4 +39,8 @@ public class ConfigFragment extends Fragment {
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
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.utils.TranslateUtil;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.greendao.query.Query;
import java.util.ArrayList;
@ -34,10 +35,24 @@ class ConfigViewModel extends ViewModel {
@NonNull
private List<ModManifestEntry> modList;
private List<ModManifestEntry> filteredModList;
private List<Predicate<List<ModManifestEntry>>> onChangedListener = new ArrayList<>();
ConfigViewModel(View root) {
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);
if (null != app) {
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
@ -94,24 +101,12 @@ 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<>();
public void removeAll(Predicate<ModManifestEntry> predicate) {
for (int i = modList.size() - 1; i >= 0; i--) {
if (predicate.apply(modList.get(i))) {
modList.remove(i);
deletedId.add(i);
}
}
return deletedId;
}
/**
@ -122,4 +117,26 @@ class ConfigViewModel extends ViewModel {
public void registerListChangeListener(Predicate<List<ModManifestEntry>> 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 com.afollestad.materialdialogs.DialogAction;
import com.google.common.base.Predicate;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
@ -17,6 +19,7 @@ import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
@ -29,9 +32,11 @@ import butterknife.OnClick;
public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.ViewHolder> {
private ConfigViewModel model;
private List<ModManifestEntry> modList;
public ModManifestAdapter(ConfigViewModel model){
public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList){
this.model=model;
this.modList = modList;
}
@NonNull
@ -43,7 +48,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
@Override
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.modDescription.setText(StringUtils.firstNonBlank(mod.getTranslatedDescription(), mod.getDescription()));
holder.setModPath(mod.getAssetPath());
@ -51,10 +56,11 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
@Override
public int getItemCount() {
return model.getModList().size();
return modList.size();
}
public void setList(List<ModManifestEntry> list) {
this.modList = list;
notifyDataSetChanged();
}
@ -84,7 +90,7 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
private void setStrike() {
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);
}
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() {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which)->{
if (which == DialogAction.POSITIVE) {
@ -99,7 +116,8 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
if (file.exists()) {
try {
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) {
notifyItemRemoved(idx);
}
@ -113,7 +131,7 @@ 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(), ".")) {
if(StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
File newFile = new File(file.getParent(), StringUtils.stripStart(file.getName(), "."));
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) {
try {
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) {
model.getModList().get(idx).setAssetPath(newFile.getAbsolutePath());
modList.get(idx).setAssetPath(newFile.getAbsolutePath());
notifyItemChanged(idx);
}
} 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.Response;
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.ModManifestEntry;
import com.zane.smapiinstaller.logic.ModAssetsManager;
@ -126,7 +128,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
void downloadContent() {
Context context = itemView.getContext();
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"));
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"));
@ -142,8 +144,9 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
return;
}
}
if (downloading.get())
if (downloading.get()) {
return;
}
downloading.set(true);
ModManifestEntry finalModManifestEntry = modManifestEntry;
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) {
if (StringUtils.equals(downloadableContent.getType(), "LOCALE")) {
if (StringUtils.equals(downloadableContent.getType(), DownloadableContentTypes.LOCALE)) {
if (finalModManifestEntry != null) {
ZipUtil.unpack(downloadedFile, new File(finalModManifestEntry.getAssetPath()));
}

View File

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

View File

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

View File

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

View File

@ -12,16 +12,16 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* JSON工具类
*/
public class JSONUtil {
private static final ObjectMapper mapper = new ObjectMapper();
private static final ObjectMapper MAPPER = new ObjectMapper();
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注释
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 异常
*/
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 异常
*/
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) {
try {
return mapper.readValue(jsonString, cls);
return MAPPER.readValue(jsonString, cls);
} catch (JsonProcessingException e) {
Log.e("JSON", "Deserialize error", e);
}
@ -68,7 +68,7 @@ public class JSONUtil {
*/
public static <T> T fromJson(String jsonString, TypeReference<T> type) {
try {
return mapper.readValue(jsonString, type);
return MAPPER.readValue(jsonString, type);
} catch (JsonProcessingException 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.List;
import java.util.Locale;
public class TranslateUtil {
@ -26,23 +27,25 @@ public class TranslateUtil {
public static final String YOU_DAO = "YouDao";
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;
}
textList = Lists.newArrayList(Iterables.filter(textList, item -> StringUtils.isNoneBlank(item) && !item.contains("\n")));
if(textList.size() == 0) {
return;
}
String queryText = Joiner.on("%0A").join(textList);
if(queryText.length() > 4096) {
if(textList.size() == 1)
if(queryText.length() > Constants.URL_LENGTH_LIMIT) {
if(textList.size() == 1) {
return;
}
List<String> subListA = textList.subList(0, textList.size() / 2);
translateText(subListA, translator, locale, resultCallback);
List<String> subListB = textList.subList(textList.size() / 2, textList.size());
translateText(subListB, translator, locale, resultCallback);
}
if(StringUtils.equalsIgnoreCase(translator, YOU_DAO)) {
if (!StringUtils.equalsAnyIgnoreCase(locale, "zh")) {
if (!StringUtils.equalsAnyIgnoreCase(locale, Locale.CHINA.getLanguage())) {
return;
}
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.setCreateTime(System.currentTimeMillis());
for (YouDaoTranslationDto.Entry entry : list) {
if (entry == null)
if (entry == null) {
continue;
}
result.setOrigin(result.getOrigin() + entry.getSrc());
result.setTranslation(result.getTranslation() + entry.getTgt());
}

View File

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

View File

@ -6,10 +6,52 @@
android:layout_height="match_parent"
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
android:id="@+id/view_mod_list"
app:layoutManager="LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
android:layout_width="0dp"
android:layout_height="0dp"
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>

View File

@ -32,7 +32,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/view_help_list"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="10dp"
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>Français</item>
<item>Português</item>
<item>Bahasa Indonesia</item>
</string-array>
<string-array name="translators">
<item>關閉</item>

View File

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

View File

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

View File

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