1.Privacy Policy

2.Layout adjust
3.Amazon Store version compat
4.Thai translation
This commit is contained in:
ZaneYork 2020-05-07 17:19:49 +08:00
parent 52b6ef6b73
commit 263649cb0b
24 changed files with 522 additions and 227 deletions

View File

@ -12,8 +12,8 @@ android {
applicationId "com.zane.smapiinstaller"
minSdkVersion 19
targetSdkVersion 28
versionCode 38
versionName "1.4.7"
versionCode 39
versionName "1.4.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View File

@ -1,5 +1,5 @@
{
"version": 15,
"version": 17,
"contents": [
{
"type": "COMPAT",
@ -14,8 +14,16 @@
"name": "SMAPI for Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_10.zip",
"hash": "46f16ab565be6a33888726a98169e4493b6784b98ace2f23fa51129a0c4c55fe"
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_11.zip",
"hash": "4dce4537952d868d40ec18dd0f0b312259cd581f6a2ffdaa5a5e3519148f63d9"
},
{
"type": "COMPAT",
"name": "SMAPI for Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_amazon_138_1.zip",
"hash": "8feac89c4b722a38408d40503cc93f8e425e06cdd27b564c03af6aeb07a384e6"
},
{
"type": "LOCALE",

View File

@ -1,5 +1,5 @@
{
"version": 15,
"version": 17,
"contents": [
{
"type": "COMPAT",
@ -14,8 +14,16 @@
"name": "SMAPI for Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_10.zip",
"hash": "46f16ab565be6a33888726a98169e4493b6784b98ace2f23fa51129a0c4c55fe"
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_11.zip",
"hash": "4dce4537952d868d40ec18dd0f0b312259cd581f6a2ffdaa5a5e3519148f63d9"
},
{
"type": "COMPAT",
"name": "SMAPI for Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_amazon_138_1.zip",
"hash": "8feac89c4b722a38408d40503cc93f8e425e06cdd27b564c03af6aeb07a384e6"
},
{
"type": "LOCALE",

View File

@ -1,5 +1,5 @@
{
"version": 15,
"version": 17,
"contents": [
{
"type": "COMPAT",
@ -14,8 +14,16 @@
"name": "SMAPI三星商店兼容包",
"assetPath": "compat/samsung_138/",
"description": "SMAPI三星商店兼容包 适用版本1.4.4.138至今, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_10.zip",
"hash": "46f16ab565be6a33888726a98169e4493b6784b98ace2f23fa51129a0c4c55fe"
"url": "http://zaneyork.cn/download/compat/smapi_samsung_138_11.zip",
"hash": "4dce4537952d868d40ec18dd0f0b312259cd581f6a2ffdaa5a5e3519148f63d9"
},
{
"type": "COMPAT",
"name": "SMAPI亚马逊商店兼容包",
"assetPath": "compat/amazon_138/",
"description": "SMAPI亚马逊商店兼容包 适用版本1.4.4.138至今, SMAPI 3.5.0",
"url": "http://zaneyork.cn/download/compat/smapi_amazon_138_1.zip",
"hash": "8feac89c4b722a38408d40503cc93f8e425e06cdd27b564c03af6aeb07a384e6"
},
{
"type": "LOCALE",

View File

@ -0,0 +1,53 @@
Zane York built the SMAPI Installer app. This app is provided by at no cost and is intended for use as is: provide the SMAPI framework for Stardew Valley.
<br>
This privacy policy is used to inform you regarding policies with the collection, use, and disclosure of Personal Information if anyone decided to use this app.
<br>
<br>
<b>1. Overview</b>
<br>
The app does not collect or share any personal information. There are no third-party ads in this app.
<br>
<br>
<b>2. Security</b>
<br>
I am striving to use commercially acceptable means of protecting your Personal Information. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.
<br>
<br>
<b>3. Links to Other Sites</b>
<br>
This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me.
Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
<br>
<br>
<b>4. Childrens Privacy</b>
<br>
These Services do not address anyone under the age of 16. I do not knowingly collect personally identifiable information from children under 16. In the case I discover that a child under 16 has provided me with personal information, I immediately delete those informations. If you are a parent or guardian and you are aware that your child has provided personal information, please contact me so that I will be able to do necessary actions.
<br>
<br>
<b>5. Distribution</b>
<br>
The only official channel for distribution of this app are:
<br>
* <a href="https://play.google.com/store/apps/details?id=com.zane.smapiinstaller">Google Play Store</a>
<br>
* <a href="https://android.myapp.com">Mobile Assistant</a>
<br>
* <a href="https://www.coolapk.com/apk/256582">Coolapk</a>
<br>
* <a href="https://github.com/ZaneYork/SMAPI-Android-Installer/releases">Github</a>
<br>
Any other mode of distribution is not official and thus, is not maintained by the developer.
<br>
This privacy policy is effective to distribution made on official channel only.
<br>
<br>
<b>6. Changes to This Privacy Policy</b>
<br>
This policy may be updated from time to time.
<br>
<br>
<b>Contact me</b>
<br>
If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me.
<br>
ZaneYork@qq.com

View File

@ -0,0 +1,53 @@
《SMAPI安装器》应用程序是由Zane York构建的。该应用免费提供旨在按原样使用提供《星露谷物语》的SMAPI框架。
<br>
如果有人决定使用此应用程序,则本隐私政策用于通知您有关收集,使用和披露个人信息的政策。
<br>
<br>
<b>1.概述</b>
<br>
该应用程序不会收集或共享任何个人信息。此应用程序中没有第三方广告。
<br>
<br>
<b>2.安全性</b>
<br>
我正在努力使用商业上可接受的方式来保护您的个人信息。但是请记住互联网上的传输方法或电子存储方法都不是100安全可靠的而且我不能保证其绝对安全。
<br>
<br>
<b>3.链接到其他网站</b>
<br>
该服务可能包含指向其他站点的链接。如果单击第三方链接,您将被定向到该站点。请注意,这些外部站点不是由我维护的。
因此,我强烈建议您阅读这些网站的隐私政策。我无法控制任何第三方站点或服务的内容,隐私政策或行为,也不承担任何责任。
<br>
<br>
<b>4.儿童的隐私权</b>
<br>
这些服务不针对16岁以下的任何人。我不会有意收集16岁以下儿童的个人身份信息。如果我发现16岁以下的儿童向我提供了个人信息我会立即删除这些信息。如果您是父母或监护人并且知道自己的孩子提供了个人信息请与我联系以便我能够采取必要的措施。
<br>
<br>
<b>5.发行</b>
<br>
分发此应用程序的唯一官方渠道是:
<br>
* <a href="https://play.google.com/store/apps/details?id=com.zane.smapiinstaller">Google Play商店</a>
<br>
* <a href="https://android.myapp.com">应用宝</a>
<br>
* <a href="https://www.coolapk.com/apk/256582">酷安</a>
<br>
* <a href="https://github.com/ZaneYork/SMAPI-Android-Installer/releases">Github</a>
<br>
任何其他分发方式都可能不是开发者官方维护的。
<br>
本隐私政策仅对在官方渠道上发布的内容有效。
<br>
<br>
<b>6.本隐私政策的变更</b>
<br>
该政策可能会不定时更新。
<br>
<br>
<b>联络方式</b>
<br>
如果您对我的隐私政策有任何疑问或建议,请随时与我联系。
<br>
ZaneYork@qq.com

View File

@ -8,8 +8,10 @@ import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.navigation.NavigationView;
import com.hjq.language.LanguagesManager;
import com.lmntrx.android.library.livin.missme.ProgressDialog;
@ -23,16 +25,15 @@ import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.dto.AppUpdateCheckResultDto;
import com.zane.smapiinstaller.entity.AppConfig;
import com.zane.smapiinstaller.entity.AppConfigDao;
import com.zane.smapiinstaller.entity.DaoSession;
import com.zane.smapiinstaller.entity.FrameworkConfig;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.ConfigManager;
import com.zane.smapiinstaller.logic.GameLauncher;
import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.ConfigUtils;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.JsonUtil;
import com.zane.smapiinstaller.utils.JsonCallback;
import com.zane.smapiinstaller.utils.JsonUtil;
import com.zane.smapiinstaller.utils.TranslateUtil;
import org.apache.commons.lang3.StringUtils;
@ -61,6 +62,9 @@ public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
@BindView(R.id.launch)
FloatingActionButton buttonLaunch;
@BindView(R.id.toolbar)
Toolbar toolbar;
@ -70,6 +74,8 @@ public class MainActivity extends AppCompatActivity {
@BindView(R.id.nav_view)
NavigationView navigationView;
private int currentFragment = R.id.nav_install;
private void requestPermissions() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
@ -94,11 +100,24 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
AppCenter.start(getApplication(), Constants.APP_CENTER_SECRET, Analytics.class, Crashes.class);
AppConfig appConfig = ConfigUtils.getConfig((MainApplication) this.getApplication(), AppConfigKey.PRIVACY_POLICY_CONFIRM, false);
if (Boolean.parseBoolean(appConfig.getValue())) {
requestPermissions();
} else {
CommonLogic.showPrivacyPolicy(toolbar, (dialog, action) -> {
if (action == DialogAction.POSITIVE) {
appConfig.setValue(String.valueOf(true));
ConfigUtils.saveConfig((MainApplication) this.getApplication(), appConfig);
requestPermissions();
} else {
this.finish();
}
});
}
}
private void initView() {
AppCenter.start(getApplication(), Constants.APP_CENTER_SECRET, Analytics.class, Crashes.class);
setSupportActionBar(toolbar);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
@ -109,19 +128,31 @@ public class MainActivity extends AppCompatActivity {
final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
currentFragment = destination.getId();
this.invalidateOptionsMenu();
switch (currentFragment){
case R.id.nav_about:
case R.id.nav_help:
case R.id.config_edit_fragment:
buttonLaunch.setVisibility(View.INVISIBLE);
break;
default:
buttonLaunch.setVisibility(View.VISIBLE);
}
});
checkAppUpdate();
}
private void checkAppUpdate() {
DaoSession daoSession = ((MainApplication) this.getApplication()).getDaoSession();
AppConfigDao appConfigDao = daoSession.getAppConfigDao();
AppConfig appConfig = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.IGNORE_UPDATE_VERSION_CODE)).build().unique();
MainApplication application = (MainApplication) this.getApplication();
OkGo.<AppUpdateCheckResultDto>get(Constants.SELF_UPDATE_CHECK_SERVICE_URL).execute(new JsonCallback<AppUpdateCheckResultDto>(AppUpdateCheckResultDto.class) {
@Override
public void onSuccess(Response<AppUpdateCheckResultDto> response) {
AppUpdateCheckResultDto dto = response.body();
if (dto != null && CommonLogic.getVersionCode(MainActivity.this) < dto.getVersionCode()) {
if (appConfig != null && StringUtils.equals(appConfig.getValue(), String.valueOf(dto.getVersionCode()))) {
AppConfig appConfig = ConfigUtils.getConfig(application, AppConfigKey.IGNORE_UPDATE_VERSION_CODE, dto.getVersionCode());
if (StringUtils.equals(appConfig.getValue(), String.valueOf(dto.getVersionCode()))) {
return;
}
DialogUtils.showConfirmDialog(toolbar, R.string.settings_check_for_updates,
@ -129,14 +160,7 @@ public class MainActivity extends AppCompatActivity {
if (which == DialogAction.POSITIVE) {
CommonLogic.openInPlayStore(MainActivity.this);
} else {
AppConfig config;
if (appConfig != null) {
config = appConfig;
config.setValue(String.valueOf(dto.getVersionCode()));
} else {
config = new AppConfig(null, AppConfigKey.IGNORE_UPDATE_VERSION_CODE, String.valueOf(dto.getVersionCode()));
}
appConfigDao.insertOrReplace(config);
ConfigUtils.saveConfig(application, appConfig);
}
});
}
@ -162,6 +186,12 @@ public class MainActivity extends AppCompatActivity {
FrameworkConfig config = manager.getConfig();
menu.findItem(R.id.settings_verbose_logging).setChecked(config.isVerboseLogging());
menu.findItem(R.id.settings_check_for_updates).setChecked(config.isCheckForUpdates());
if(currentFragment != R.id.nav_config) {
menu.findItem(R.id.toolbar_update_check).setVisible(false);
}
else {
menu.findItem(R.id.toolbar_update_check).setVisible(true);
}
menu.findItem(R.id.settings_developer_mode).setChecked(config.isDeveloperMode());
menu.findItem(R.id.settings_disable_mono_mod).setChecked(config.isDisableMonoMod());
Constants.MOD_PATH = config.getModsPath();
@ -214,12 +244,10 @@ public class MainActivity extends AppCompatActivity {
int size = Integer.parseInt(input.toString());
config.setMaxLogSize(size);
manager.flushConfig();
}
catch (Exception ignored){
} catch (Exception ignored) {
}
}
else {
} else {
config.setMaxLogSize(Integer.MAX_VALUE);
manager.flushConfig();
}
@ -242,9 +270,6 @@ public class MainActivity extends AppCompatActivity {
}
private int getTranslateServiceIndex(AppConfig selectedTranslator) {
if (selectedTranslator == null) {
return 0;
}
switch (selectedTranslator.getValue()) {
case "OFF":
return 0;
@ -256,32 +281,22 @@ public class MainActivity extends AppCompatActivity {
}
private void selectTranslateServiceLogic() {
DaoSession daoSession = ((MainApplication) this.getApplication()).getDaoSession();
AppConfigDao appConfigDao = daoSession.getAppConfigDao();
int index = getTranslateServiceIndex(appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.ACTIVE_TRANSLATOR)).build().unique());
MainApplication application = (MainApplication) this.getApplication();
AppConfig activeTranslator = ConfigUtils.getConfig(application, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.NONE);
int index = getTranslateServiceIndex(activeTranslator);
DialogUtils.showSingleChoiceDialog(toolbar, R.string.settings_translation_service, R.array.translators, index, (dialog, position) -> {
AppConfig activeTranslator = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.ACTIVE_TRANSLATOR)).build().unique();
switch (position) {
case 0:
if (activeTranslator != null) {
appConfigDao.delete(activeTranslator);
}
activeTranslator.setValue(TranslateUtil.NONE);
ConfigUtils.saveConfig(application, activeTranslator);
break;
case 1:
if (activeTranslator == null) {
activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.GOOGLE);
} else {
activeTranslator.setValue(TranslateUtil.GOOGLE);
}
appConfigDao.insertOrReplace(activeTranslator);
ConfigUtils.saveConfig(application, activeTranslator);
break;
case 2:
if (activeTranslator == null) {
activeTranslator = new AppConfig(null, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.YOU_DAO);
} else {
activeTranslator.setValue(TranslateUtil.YOU_DAO);
}
appConfigDao.insertOrReplace(activeTranslator);
ConfigUtils.saveConfig(application, activeTranslator);
break;
default:
}

View File

@ -9,4 +9,6 @@ public class AppConfigKey {
public static final String MOD_LIST_SORT_BY = "ModListSortBy";
public static final String IGNORE_UPDATE_VERSION_CODE = "UpdateIgnoreVersionCode";
public static final String PRIVACY_POLICY_CONFIRM = "PrivacyPolicyConfirm";
}

View File

@ -9,5 +9,10 @@ public class ManifestPatchConstants {
public static final String PATTERN_MAIN_ACTIVITY = ".MainActivity";
public static final String PATTERN_VERSION_CODE = "versionCode";
public static final String PATTERN_VERSION_NAME = "versionName";
public static final CharSequence APP_PACKAGE_NAME = "com.chucklefish.stardewvalley";
public static final String PATTERN_VERSION_AMAZON = "amazon";
}

View File

@ -14,6 +14,7 @@ public class AppConfig {
@Unique
private String name;
private String value;
@Generated(hash = 1859776450)
public AppConfig(Long id, String name, String value) {
this.id = id;

View File

@ -188,6 +188,7 @@ public class ApkPatcher {
*/
private byte[] modifyManifest(byte[] bytes, List<ApkFilesManifest> manifests) {
AtomicReference<String> packageName = new AtomicReference<>();
AtomicReference<String> versionName = new AtomicReference<>();
AtomicLong versionCode = new AtomicLong();
Predicate<ManifestTagVisitor.AttrArgs> processLogic = (attr) -> {
if(attr == null) {
@ -202,6 +203,11 @@ public class ApkPatcher {
attr.obj = strObj.replace(ManifestPatchConstants.APP_PACKAGE_NAME, Constants.TARGET_PACKAGE_NAME);
}
break;
case ManifestPatchConstants.PATTERN_VERSION_NAME:
if (versionName.get() == null) {
versionName.set((String) attr.obj);
}
break;
case "label":
if (strObj.contains(ManifestPatchConstants.APP_NAME)) {
attr.obj = context.getString(R.string.smapi_game_name);
@ -230,6 +236,9 @@ public class ApkPatcher {
};
try {
byte[] modifyManifest = CommonLogic.modifyManifest(bytes, processLogic);
if(StringUtils.endsWith(versionName.get(), ManifestPatchConstants.PATTERN_VERSION_AMAZON)) {
packageName.set(ManifestPatchConstants.APP_PACKAGE_NAME + ManifestPatchConstants.PATTERN_VERSION_AMAZON);
}
Iterables.removeIf(manifests, manifest -> {
if(manifest == null) {
return true;

View File

@ -18,13 +18,17 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.zane.smapiinstaller.MainApplication;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.entity.ApkFilesManifest;
import com.zane.smapiinstaller.entity.ManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
import org.zeroturnaround.zip.ZipUtil;
@ -37,6 +41,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java9.util.function.BiConsumer;
import java9.util.function.Consumer;
import pxb.android.axml.AxmlReader;
import pxb.android.axml.AxmlVisitor;
@ -309,4 +314,10 @@ public class CommonLogic {
}
});
}
public static void showPrivacyPolicy(View view, BiConsumer<MaterialDialog, DialogAction> callback) {
Context context = view.getContext();
String policy = FileUtils.getLocaledAssetText(context, "privacy_policy.txt");
DialogUtils.showConfirmDialog(view, R.string.privacy_policy, policy, R.string.confirm, R.string.cancel, true, callback);
}
}

View File

@ -13,9 +13,11 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.utils.DialogUtils;
@ -23,6 +25,7 @@ import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import java9.util.function.BiConsumer;
/**
* @author Zane
@ -75,6 +78,12 @@ public class AboutFragment extends Fragment {
CommonLogic.doOnNonNull(this.getActivity(), (activity) -> listSelectLogic(activity, position))));
}
@OnClick(R.id.button_privacy_policy)
void privacyPolicy() {
CommonLogic.showPrivacyPolicy(imgHeart, (dialog, dialogAction) -> {
});
}
private void listSelectLogic(Context context, int position) {
switch (position) {
case 0:

View File

@ -16,6 +16,7 @@ import com.zane.smapiinstaller.entity.TranslationResultDao;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.ListenableObject;
import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.ConfigUtils;
import com.zane.smapiinstaller.utils.TranslateUtil;
import org.apache.commons.lang3.StringUtils;
@ -54,13 +55,9 @@ class ConfigViewModel extends ViewModel implements ListenableObject<List<ModMani
translateLogic(root);
MainApplication app = CommonLogic.getApplicationFromView(root);
if (null != app) {
AppConfigDao appConfigDao = app.getDaoSession().getAppConfigDao();
Query<AppConfig> query = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.MOD_LIST_SORT_BY)).build();
AppConfig appConfig = query.unique();
if (null != appConfig) {
AppConfig appConfig = ConfigUtils.getConfig(app, AppConfigKey.MOD_LIST_SORT_BY, sortBy);
sortBy = appConfig.getValue();
}
}
sortLogic(sortBy);
}
@ -70,9 +67,8 @@ class ConfigViewModel extends ViewModel implements ListenableObject<List<ModMani
return;
}
this.sortBy = sortBy;
AppConfigDao appConfigDao = app.getDaoSession().getAppConfigDao();
AppConfig appConfig = new AppConfig(null, AppConfigKey.MOD_LIST_SORT_BY, sortBy);
appConfigDao.insertOrReplace(appConfig);
ConfigUtils.saveConfig(app, appConfig);
sortLogic(appConfig.getValue());
}
@ -116,8 +112,8 @@ class ConfigViewModel extends ViewModel implements ListenableObject<List<ModMani
MainApplication app = CommonLogic.getApplicationFromView(root);
if (null != app) {
DaoSession daoSession = app.getDaoSession();
AppConfig activeTranslator = daoSession.getAppConfigDao().queryBuilder().where(AppConfigDao.Properties.Name.eq(AppConfigKey.ACTIVE_TRANSLATOR)).build().unique();
if (activeTranslator != null) {
AppConfig activeTranslator = ConfigUtils.getConfig(app, AppConfigKey.ACTIVE_TRANSLATOR, TranslateUtil.NONE);
if (StringUtils.equals(activeTranslator.getValue(), TranslateUtil.NONE)) {
String translator = activeTranslator.getValue();
List<String> descriptions = StreamSupport.stream(this.modList).map(ModManifestEntry::getDescription).filter(Objects::nonNull).collect(Collectors.toList());
String language = LanguagesManager.getAppLanguage(app).getLanguage();

View File

@ -0,0 +1,26 @@
package com.zane.smapiinstaller.utils;
import com.zane.smapiinstaller.MainApplication;
import com.zane.smapiinstaller.entity.AppConfig;
import com.zane.smapiinstaller.entity.AppConfigDao;
import com.zane.smapiinstaller.entity.DaoSession;
/**
* @author Zane
*/
public class ConfigUtils {
public static <T> AppConfig getConfig(MainApplication application, String key, T defaultValue) {
DaoSession daoSession = application.getDaoSession();
AppConfigDao appConfigDao = daoSession.getAppConfigDao();
AppConfig appConfig = appConfigDao.queryBuilder().where(AppConfigDao.Properties.Name.eq(key)).build().unique();
if(appConfig == null) {
appConfig = new AppConfig(null, key, String.valueOf(defaultValue));
}
return appConfig;
}
public static void saveConfig(MainApplication application, AppConfig config){
DaoSession daoSession = application.getDaoSession();
AppConfigDao appConfigDao = daoSession.getAppConfigDao();
appConfigDao.insertOrReplace(config);
}
}

View File

@ -8,6 +8,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.input.DialogInputExtKt;
import com.afollestad.materialdialogs.list.DialogListExtKt;
import com.afollestad.materialdialogs.list.DialogSingleChoiceExtKt;
import com.afollestad.materialdialogs.message.DialogMessageSettings;
import com.lmntrx.android.library.livin.missme.ProgressDialog;
import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.R;
@ -128,14 +129,29 @@ public class DialogUtils {
* @param callback 回调
*/
public static void showConfirmDialog(View view, int title, String message, int positiveText, int negativeText, BiConsumer<MaterialDialog, DialogAction> callback) {
showConfirmDialog(view, title, message, positiveText, negativeText, false, callback);
}
public static void showConfirmDialog(View view, int title, String message, int positiveText, int negativeText, boolean isHtml, BiConsumer<MaterialDialog, DialogAction> callback) {
CommonLogic.runOnUiThread(CommonLogic.getActivityFromView(view), (activity) -> {
MaterialDialog materialDialog = new MaterialDialog(activity, MaterialDialog.getDEFAULT_BEHAVIOR()).title(title, null).message(null, message, null).positiveButton(positiveText, null, dialog -> {
MaterialDialog materialDialog = new MaterialDialog(activity, MaterialDialog.getDEFAULT_BEHAVIOR())
.title(title, null)
.positiveButton(positiveText, null, dialog -> {
callback.accept(dialog, DialogAction.POSITIVE);
return null;
}).negativeButton(negativeText, null, dialog -> {
callback.accept(dialog, DialogAction.NEGATIVE);
return null;
});
if(isHtml){
materialDialog.message(null, message, (dialogMessageSettings) -> {
dialogMessageSettings.html(null);
return null;
});
}
else {
materialDialog.message(null, message, null);
}
DialogUtils.setCurrentDialog(materialDialog);
materialDialog.show();
});
@ -184,6 +200,7 @@ public class DialogUtils {
});
}
}
/**
* 解散指定对话框
*

View File

@ -167,6 +167,40 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
}
}
/**
* 读取资源文本
* @param context context
* @param filename 文件名
* @return 文本
*/
public static String getAssetText(Context context, String filename) {
try {
InputStream inputStream = getLocalAsset(context, filename);
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
return CharStreams.toString(reader);
}
} catch (IOException ignored) {
}
return null;
}
/**
* 读取本地化后的资源文本
* @param context context
* @param filename 文件名
* @return 文本
*/
public static String getLocaledAssetText(Context context, String filename) {
try {
InputStream inputStream = getLocaledLocalAsset(context, filename);
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
return CharStreams.toString(reader);
}
} catch (IOException ignored) {
}
return null;
}
/**
* 读取JSON资源
* @param context context
@ -176,12 +210,9 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
* @return 数据
*/
public static <T> T getAssetJson(Context context, String filename, Class<T> tClass) {
try {
InputStream inputStream = getLocalAsset(context, filename);
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
return JsonUtil.fromJson(CharStreams.toString(reader), tClass);
}
} catch (IOException ignored) {
String text = getAssetText(context, filename);
if(text != null){
return JsonUtil.fromJson(text, tClass);
}
return null;
}
@ -206,12 +237,9 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
* @return 数据
*/
public static <T> T getAssetJson(Context context, String filename, TypeReference<T> type) {
try {
InputStream inputStream = getLocalAsset(context, filename);
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
return JsonUtil.fromJson(CharStreams.toString(reader), type);
}
} catch (IOException ignored) {
String text = getAssetText(context, filename);
if(text != null){
return JsonUtil.fromJson(text, type);
}
return null;
}

View File

@ -26,6 +26,7 @@ import java9.util.stream.StreamSupport;
*/
public class TranslateUtil {
public static final String NONE = "OFF";
public static final String GOOGLE = "Google";
public static final String YOU_DAO = "YouDao";

View File

@ -1,116 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:divider="@drawable/horizontal_divider"
android:padding="10dp"
android:showDividers="middle"
android:orientation="vertical"
tools:context=".ui.about.AboutFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/horizontal_divider"
android:orientation="vertical"
android:padding="10dp"
android:showDividers="middle">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_qq_group_1"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/button_qq_group_text"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/img_qq_1"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/img_qq_1"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/icon_desc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/tencentqq"
android:contentDescription="@string/icon_desc" />
<Button
android:id="@+id/button_qq_group_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/img_qq_1"
app:layout_constraintEnd_toEndOf="parent"
style="@style/Widget.AppCompat.Button.Borderless"
android:gravity="center"
android:textSize="24sp"
android:text="@string/button_qq_group_text" />
app:srcCompat="@drawable/tencentqq" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_release"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/button_release"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/img_release"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/img_release"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/icon_desc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/global"
android:contentDescription="@string/icon_desc" />
<Button
android:id="@+id/button_release"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/img_release"
app:layout_constraintEnd_toEndOf="parent"
style="@style/Widget.AppCompat.Button.Borderless"
android:gravity="center"
android:textSize="24sp"
android:text="@string/button_release" />
app:srcCompat="@drawable/global" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_gplay"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/button_gplay"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/img_gplay"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/img_gplay"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/icon_desc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/global"
android:contentDescription="@string/icon_desc" />
<Button
android:id="@+id/button_gplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/img_gplay"
app:layout_constraintEnd_toEndOf="parent"
style="@style/Widget.AppCompat.Button.Borderless"
android:gravity="center"
android:textSize="24sp"
android:text="@string/button_gplay" />
app:srcCompat="@drawable/global" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_donation"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/button_donation_text"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/img_heart"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/img_heart"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/icon_desc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/heart"
android:contentDescription="@string/icon_desc" />
<Button
android:id="@+id/button_donation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/img_heart"
app:layout_constraintEnd_toEndOf="parent"
style="@style/Widget.AppCompat.Button.Borderless"
android:gravity="center"
android:textSize="24sp"
android:text="@string/button_donation_text" />
app:srcCompat="@drawable/heart" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<TextView
android:id="@+id/button_privacy_policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/privacy_policy"
android:autoLink="all"
app:layout_constraintBottom_toBottomOf="@id/guideline_h1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
</TextView>
<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_end="20dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -52,7 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="60dp" />
app:layout_constraintGuide_end="20dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_v1"

View File

@ -1,68 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="abort">หยุด</string>
<string name="android_version_confirm">ุ่นอุปกรณ์ระบบของคุณเก่าเกินไปสำหรับ MonoMod อาจทำให้เกิดข้อผิดพลาดของเฟรมเวิร์ก 0Harmony อัปเดตเป็น Android M หรือใหม่กว่าหากเป็นไปได้</string>
<string name="app_name">โปรแกรมติดตั้ง SMAPI</string>
<string name="button_compat">ความเข้ากันได้</string>
<string name="android_version_confirm">ะบบอุปกรณ์ของคุณเก่าเกินกว่าจะใช้ MonoMod อาจจะทำให้ 0Harmony framework เกิดอาการ crash ถ้าเป็นไปได้กรุณาอัปเดตเป็น Android M หรือใหม่กว่า</string>
<string name="app_name">แอปติดตั้ง SMAPI</string>
<string name="button_compat">ใช้กับแอปนี้ได้</string>
<string name="button_donation_text">การบริจาค</string>
<string name="button_gplay">ดูใน Play Store</string>
<string name="button_install">ติดตั้ง</string>
<string name="button_logs">ท่อน</string>
<string name="button_logs">บันทึก SMAPI</string>
<string name="button_nexus">Nexus</string>
<string name="button_qq_group_1_text">กลุ่ม QQ 1: 860453392</string>
<string name="button_qq_group_2_text">กลุ่ม QQ 2: 1078428449</string>
<string name="button_release">ปล่อย</string>
<string name="button_release">โหลดเวอร์ชั่นใหม่</string>
<string name="cancel">ยกเลิก</string>
<string name="confirm">ยืนยัน</string>
<string name="confirm_delete_content">คุณแน่ใจที่จะลบหรือไม่</string>
<string name="confirm_delete_content">คุณแน่ใจนะว่าจะลบ?</string>
<string name="confirm_disable_mod">คุณแน่ใจว่าจะปิดการใช้งานหรือไม่</string>
<string name="continue_text">ต่อ</string>
<string name="download_unpack_success">ดาวน์โหลดและเปิดเครื่องสำเร็จ</string>
<string name="download_unpack_success">ดาวน์โหลดและแกะกล่องสำเร็จ</string>
<string name="downloading">กำลังดาวน์โหลด: %1$d KB / %2$d KB</string>
<string name="duplicate_mod_found">พบสำเนาตัวดัดแปลงที่ซ้ำกันโปรดลบสำเนาที่ซ้ำกัน:%s</string>
<string name="error">ความผิดพลาด</string>
<string name="error_depends_on_mod">%1$sขึ้นอยู่กับ%2$s โปรดติดตั้งก่อน</string>
<string name="error_depends_on_mod_version">%1$s ขึ้นอยู่กับรุ่น%2$s %3$s หรือใหม่กว่าโปรดอัปเดตก่อน</string>
<string name="error_failed_to_create_file">ไม่สามารถสร้างไฟล์เป้าหมาย:%s</string>
<string name="error_failed_to_download">ไม่สามารถดาวน์โหลดทรัพยากรเป้าหมาย</string>
<string name="error_failed_to_repair">ไม่สามารถซ่อมแซมสภาพแวดล้อม SMAPI</string>
<string name="error_game_not_found">ไม่พบร่างของเกมคุณติดตั้ง Stardew Valley หรือไม่</string>
<string name="duplicate_mod_found">เจอ mod ที่เหมือนกันมากกว่าหนึ่ง กรุณาลบให้เหลืออันเดียว: %s</string>
<string name="error">ผิดพลาด</string>
<string name="error_depends_on_mod">%1$s ขึ้นอยู่กับ %2$s กรุณาติดตั้งก่อน</string>
<string name="error_depends_on_mod_version">%1$s ขึ้นอยู่กับ %2$s %3$s เวอร์ชั่นหรือใหม่กว่า กรุณาอัปเดตก่อน</string>
<string name="error_failed_to_create_file">สร้างไฟล์เป้าหมายล้มเหลว: %s</string>
<string name="error_failed_to_download">ล้มเหลวในการดาวน์โหลดทรัพยากรเป้าหมาย</string>
<string name="error_failed_to_repair">ซ่อมแซม SMAPI environment ล้มเหลว</string>
<string name="error_game_not_found">ไม่พบเกม Stardew Valley คุณติดตั้งเกมแล้วหรือยัง?</string>
<string name="error_illegal_path">กรุณาใส่เส้นทางที่ถูกต้อง</string>
<string name="error_no_supported_game_version">เวอร์ชั่นเกมไม่รองรับอัปเกรดหรือดาวน์โหลดแพ็คเกจที่รองรับ</string>
<string name="error_smapi_not_installed">SMAPI ยังไม่ได้ติดตั้งให้กดติดตั้งก่อน</string>
<string name="extracting_package">แตกแพ็กเกจการติดตั้ง</string>
<string name="failed_to_patch_game">ไม่สามารถแก้ไขเกมได้โปรดขอความช่วยเหลือจากผู้พัฒนา</string>
<string name="failed_to_process_manifest">ไม่สามารถประมวลผลไฟล์ AndroidManifest.xml</string>
<string name="failed_to_sign_game">ไม่สามารถลงชื่อในเกมได้โปรดขอความช่วยเหลือจากผู้พัฒนา</string>
<string name="failed_to_unpack_smapi_files">ไม่สามารถแยกไฟล์ smapi ได้</string>
<string name="error_no_supported_game_version">เวอร์ชั่นของเกมไม่สนับสนุน อัพเกรดหรือดาวน์โหลดแพคเกจที่ใช้กับแอปนี้ได้ก่อน</string>
<string name="error_smapi_not_installed">ยังไม่ได้ติดตั้ง SMAPI กรุณาติดตั้งก่อน</string>
<string name="extracting_package">กำลังแตกไฟล์ Install Package</string>
<string name="failed_to_patch_game">แพทช์เกมไม่สำเร็จ กรุณาติดต่อผู้พัฒนาเพื่อขอความช่วยเหลือ</string>
<string name="failed_to_process_manifest">ล้มเหลวในการเข้าถึงไฟล์ AndroidManifest.xml</string>
<string name="failed_to_sign_game">ลงทะเบียนแอปเกมไม่สำเร็จ กรุณาติดต่อผู้พัฒนาเพื่อขอความช่วยเหลือ</string>
<string name="failed_to_unpack_smapi_files">แกะกล่องไฟล์ smapi ไม่สำเร็จ</string>
<string name="info">ข้อมูล</string>
<string name="input">อินพุต</string>
<string name="input_mods_path">กรุณาใส่เส้นทาง mods</string>
<string name="install_progress_title">ติดตั้งความคืบหน้า</string>
<string name="installing_package">ารติดตั้ง</string>
<string name="locale_pack">ชุดภาษา</string>
<string name="install_progress_title">ความคืบหน้าการติดตั้ง</string>
<string name="installing_package">ำลังติดตั้ง</string>
<string name="locale_pack">ชุดภาษาท้องถิ่น</string>
<string name="menu_about">เกี่ยวกับ</string>
<string name="menu_config">ารกำหนดค่า</string>
<string name="menu_config">กำหนดค่า</string>
<string name="menu_config_edit">แก้ไข</string>
<string name="menu_download">ดาวน์โหลด</string>
<string name="menu_help">ช่วยด้วย</string>
<string name="menu_help">ช่วยเหลือ</string>
<string name="menu_install">ติดตั้ง</string>
<string name="nav_header_subtitle">ZaneYork@qq.com</string>
<string name="nav_header_title">โปรแกรมติดตั้ง SMAPI</string>
<string name="nav_header_title">แอปติดตั้ง SMAPI</string>
<string name="ok">ตกลง</string>
<string name="patching_package">ารแปะ SMAPI</string>
<string name="patching_package">ำลังแพทช์ SMAPI</string>
<string name="progress">ความคืบหน้า</string>
<string name="save">บันทึก</string>
<string name="settings_check_for_updates">ตรวจสอบสำหรับการอัเดต</string>
<string name="settings_check_for_updates">ตรวจสอบสำหรับการอัเดต</string>
<string name="settings_developer_mode">โหมดนักพัฒนา</string>
<string name="settings_set_language">ภาษา</string>
<string name="settings_set_mod_path">ตั้งค่าเส้นทาง Mods</string>
<string name="settings_translation_service">บริการแปล</string>
<string name="settings_verbose_logging">การบันทึกอย่างละเอียด</string>
<string name="signing_package">ารลงชื่อ</string>
<string name="signing_package">ำลังลงทะเบียนแอป</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">เวอร์ชั่น SMAPI: 3.5.0</string>
<string name="text_install_tip1">หมายเหตุ: จำเป็นต้องใช้เวอร์ชั่นเกม 1.4.5.138 หรือใหม่กว่า</string>
<string name="text_install_tip2">จำเป็นต้องมีเกมพื้นฐานเมื่อทำการอัพเดต / ติดตั้ง</string>
<string name="unpacking_smapi_files">แกะกล่อง</string>
<string name="text_install_tip1">โน้ต: ต้องการเกมเวอร์ชั่น 1.4.5.138 หรือใหม่กว่า</string>
<string name="text_install_tip2">ต้องการเกมหลักเมื่อทำการอัปเดต / ติดตั้ง</string>
<string name="unpacking_smapi_files">กำลังแกะกล่อง</string>
<string name="mod_version_update_text">พบเวอร์ชั่นใหม่: %2$s</string>
<string name="sort_by">จัดเรียงตาม</string>
<string name="text_too_large">ไฟล์ข้อความมีขนาดใหญ่เกินไปสำหรับเครื่องมือแก้ไข</string>
<string name="open_with">เปิดด้วย</string>
<string name="no_update_text">Mods ทั้งหมดเป็นเวอร์ชั่นล่าสุด</string>
<string name="app_update_detected">ตรวจพบเวอร์ชั่นใหม่: %1$s</string>
<string name="parser">วิเคราะห์</string>
<string name="mod_version_update_checking">กำลังตรวจหาการอัปเดตของ Mod</string>
<string name="smapi_version_runing">ใช้งานล่าสุด: %s</string>
<string name="settings_disable_mono_mod">ปิดการใช้งาน MonoMod</string>
<string name="settings_set_max_log_size">ขนาดไพล์บันทึกสูงสุด</string>
<string name="button_qq_group_text">QQ Group</string>
</resources>

View File

@ -75,4 +75,5 @@
<string name="settings_disable_mono_mod">停用 MonoMod</string>
<string name="settings_set_max_log_size">最大日誌檔案大小</string>
<string name="button_qq_group_text">QQ群</string>
<string name="privacy_policy">隱私權條款</string>
</resources>

View File

@ -75,4 +75,5 @@
<string name="settings_disable_mono_mod">禁用MonoMod</string>
<string name="settings_set_max_log_size">最大日志大小</string>
<string name="button_qq_group_text">QQ群</string>
<string name="privacy_policy">隐私权政策</string>
</resources>

View File

@ -79,4 +79,5 @@
<string name="settings_disable_mono_mod">Disable MonoMod</string>
<string name="settings_set_max_log_size">Max Log Size</string>
<string name="button_qq_group_text">QQ Group</string>
<string name="privacy_policy">Privacy Policy</string>
</resources>