1.Mod list search feature
2.Indonesia translate file 3.Code clean up
This commit is contained in:
parent
4761d3d998
commit
36a64e4ad3
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 有道翻译服务
|
* 有道翻译服务
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.zane.smapiinstaller.constant;
|
||||||
|
|
||||||
|
public class DownloadableContentTypes {
|
||||||
|
public static final String LOCALE = "LOCALE";
|
||||||
|
|
||||||
|
public static final String COMPAT = "COMPAT";
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取报错内容
|
* 获取报错内容
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue