1. Update to SMAPI 3.7.3.3

2. Remove BSPatch and ButterKnife lib
This commit is contained in:
ZaneYork 2020-09-30 10:17:40 +08:00
parent 8b801247a4
commit 327e2520c5
34 changed files with 385 additions and 458 deletions

View File

@ -12,8 +12,8 @@ android {
applicationId "com.zane.smapiinstaller"
minSdkVersion 19
targetSdkVersion 28
versionCode 53
versionName "1.6.1"
versionCode 55
versionName "1.6.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
@ -52,6 +52,10 @@ android {
enableSplit = false
}
}
viewBinding {
enabled = true
}
}
greendao {
@ -61,10 +65,10 @@ greendao {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation "androidx.navigation:navigation-fragment:2.3.0"
implementation "androidx.navigation:navigation-ui:2.3.0"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
@ -74,16 +78,13 @@ dependencies {
implementation 'com.afollestad.material-dialogs:input:3.3.0'
implementation 'com.afollestad.material-dialogs:lifecycle:3.3.0'
implementation 'com.lmntrx.android.library.livin.missme:missme:0.1.5'
// https://mvnrepository.com/artifact/com.jakewharton/butterknife
implementation 'com.jakewharton:butterknife:10.2.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
// https://mvnrepository.com/artifact/com.google.guava/guava
implementation group: 'com.google.guava', name: 'guava', version: '28.2-android'
// https://mvnrepository.com/artifact/org.zeroturnaround/zt-zip
implementation group: 'org.zeroturnaround', name: 'zt-zip', version: '1.14'
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.10'
// https://mvnrepository.com/artifact/commons-io/commons-io
implementation group: 'commons-io', name: 'commons-io', version: '2.7'
implementation 'com.lzy.net:okgo:3.0.4'
@ -99,8 +100,8 @@ dependencies {
api 'org.greenrobot:greendao-generator:3.3.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.android.support:multidex:1.0.3'
def appCenterSdkVersion = '3.0.0'
@ -110,6 +111,4 @@ dependencies {
compileOnly 'org.projectlombok:lombok:1.18.12'
annotationProcessor 'org.projectlombok:lombok:1.18.12'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
api 'com.smart.library.util:bspatch:0.0.2'
}

View File

@ -1,29 +1,29 @@
{
"version": 27,
"version": 28,
"contents": [
{
"type": "COMPAT",
"name": "SMAPI for 1.4.5.137",
"assetPath": "compat/137/",
"description": "SMAPI compat package for game 1.4.4.128 - 1.4.5.137, SMAPI 3.3.2.0",
"url": "http://zaneyork.cn/dl/compat/smapi_137.zip",
"hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc"
"description": "SMAPI compat package for game 1.4.4.128 - 1.4.5.137, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_137_2.zip",
"hash": "3607a1f0647d49963a9803cc0445bba23a19b3c92c3b747eb12569fdb5d9b95e"
},
{
"type": "COMPAT",
"name": "SMAPI for Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_21.zip",
"hash": "a77e8d35e0d1118bd03acda038ba00c4e13adc5eb73ebf8c200dfacdf85ef0a1"
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_22.zip",
"hash": "e17f369ed84b2d30e6f552d27d7a06c7026a5ddf1de4552c86f31bbff6088ed9"
},
{
"type": "COMPAT",
"name": "SMAPI for Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_11.zip",
"hash": "f792ea943d2892b9c6c1116eec05916577f5f0385730a61a3d80d5d5b65b6c7d"
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_12.zip",
"hash": "a7746738ee384d2142c2d3075a400d93f95c84bae2d56ce0e84894f24307d7a2"
},
{
"type": "LOCALE",

View File

@ -5,25 +5,25 @@
"type": "COMPAT",
"name": "SMAPI for 1.4.5.137",
"assetPath": "compat/137/",
"description": "SMAPI compat package for game 1.4.4.128 - 1.4.5.137, SMAPI 3.3.2.0",
"url": "http://zaneyork.cn/dl/compat/smapi_137.zip",
"hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc"
"description": "SMAPI compat package for game 1.4.4.128 - 1.4.5.137, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_137_2.zip",
"hash": "3607a1f0647d49963a9803cc0445bba23a19b3c92c3b747eb12569fdb5d9b95e"
},
{
"type": "COMPAT",
"name": "SMAPI for Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_21.zip",
"hash": "a77e8d35e0d1118bd03acda038ba00c4e13adc5eb73ebf8c200dfacdf85ef0a1"
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_22.zip",
"hash": "e17f369ed84b2d30e6f552d27d7a06c7026a5ddf1de4552c86f31bbff6088ed9"
},
{
"type": "COMPAT",
"name": "SMAPI for Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_11.zip",
"hash": "f792ea943d2892b9c6c1116eec05916577f5f0385730a61a3d80d5d5b65b6c7d"
"description": "SMAPI compat package for game 1.4.4.138 - latest, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_12.zip",
"hash": "a7746738ee384d2142c2d3075a400d93f95c84bae2d56ce0e84894f24307d7a2"
},
{
"type": "LOCALE",

View File

@ -5,25 +5,25 @@
"type": "COMPAT",
"name": "SMAPI untuk 1.4.5.137",
"assetPath": "compat/137/",
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.128 - 1.4.5.137, SMAPI 3.3.2.0",
"url": "http://zaneyork.cn/dl/compat/smapi_137.zip",
"hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc"
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.128 - 1.4.5.137, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_137_2.zip",
"hash": "3607a1f0647d49963a9803cc0445bba23a19b3c92c3b747eb12569fdb5d9b95e"
},
{
"type": "COMPAT",
"name": "SMAPI untuk Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.138 - terbaru, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_21.zip",
"hash": "a77e8d35e0d1118bd03acda038ba00c4e13adc5eb73ebf8c200dfacdf85ef0a1"
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.138 - terbaru, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_22.zip",
"hash": "e17f369ed84b2d30e6f552d27d7a06c7026a5ddf1de4552c86f31bbff6088ed9"
},
{
"type": "COMPAT",
"name": "SMAPI untuk Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.138 - terbaru, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_11.zip",
"hash": "f792ea943d2892b9c6c1116eec05916577f5f0385730a61a3d80d5d5b65b6c7d"
"description": "Paket kompatibilitas SMAPI untuk versi 1.4.4.138 - terbaru, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_12.zip",
"hash": "a7746738ee384d2142c2d3075a400d93f95c84bae2d56ce0e84894f24307d7a2"
},
{
"type": "LOCAL",

View File

@ -5,25 +5,25 @@
"type": "COMPAT",
"name": "SMAPI สำหรับ 1.4.5.137",
"assetPath": "compat/137/",
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.128 - 1.4.5.137, SMAPI 3.3.2.0",
"url": "http://zaneyork.cn/dl/compat/smapi_137.zip",
"hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc"
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.128 - 1.4.5.137, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_137_2.zip",
"hash": "3607a1f0647d49963a9803cc0445bba23a19b3c92c3b747eb12569fdb5d9b95e"
},
{
"type": "COMPAT",
"name": "SMAPI สำหรับ Galaxy Store",
"assetPath": "compat/samsung_138/",
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.138 - ล่าสุด, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_21.zip",
"hash": "a77e8d35e0d1118bd03acda038ba00c4e13adc5eb73ebf8c200dfacdf85ef0a1"
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.138 - ล่าสุด, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_22.zip",
"hash": "e17f369ed84b2d30e6f552d27d7a06c7026a5ddf1de4552c86f31bbff6088ed9"
},
{
"type": "COMPAT",
"name": "SMAPI สำหรับ Amazon Store",
"assetPath": "compat/amazon_138/",
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.138 - ล่าสุด, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_11.zip",
"hash": "f792ea943d2892b9c6c1116eec05916577f5f0385730a61a3d80d5d5b65b6c7d"
"description": "แพคเกจแอพลิเคชั่น SMAPI สำหรับเกม Stardew Valley เวอร์ชั่น 1.4.4.138 - ล่าสุด, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_12.zip",
"hash": "a7746738ee384d2142c2d3075a400d93f95c84bae2d56ce0e84894f24307d7a2"
},
{
"type": "LOCALE",

View File

@ -5,25 +5,25 @@
"type": "COMPAT",
"name": "SMAPI兼容包 1.4.5.137",
"assetPath": "compat/137/",
"description": "SMAPI兼容包, 适用版本1.4.4.128 - 1.4.5.137, SMAPI 3.3.2.0",
"url": "http://zaneyork.cn/dl/compat/smapi_137.zip",
"hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc"
"description": "SMAPI兼容包, 适用版本1.4.4.128 - 1.4.5.137, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_137_2.zip",
"hash": "3607a1f0647d49963a9803cc0445bba23a19b3c92c3b747eb12569fdb5d9b95e"
},
{
"type": "COMPAT",
"name": "SMAPI三星商店兼容包",
"assetPath": "compat/samsung_138/",
"description": "SMAPI三星商店兼容包 适用版本1.4.4.138至今, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_21.zip",
"hash": "a77e8d35e0d1118bd03acda038ba00c4e13adc5eb73ebf8c200dfacdf85ef0a1"
"description": "SMAPI三星商店兼容包 适用版本1.4.4.138至今, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_samsung_138_22.zip",
"hash": "e17f369ed84b2d30e6f552d27d7a06c7026a5ddf1de4552c86f31bbff6088ed9"
},
{
"type": "COMPAT",
"name": "SMAPI亚马逊商店兼容包",
"assetPath": "compat/amazon_138/",
"description": "SMAPI亚马逊商店兼容包 适用版本1.4.4.138至今, SMAPI 3.7.3.2",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_11.zip",
"hash": "f792ea943d2892b9c6c1116eec05916577f5f0385730a61a3d80d5d5b65b6c7d"
"description": "SMAPI亚马逊商店兼容包 适用版本1.4.4.138至今, SMAPI 3.7.3.3",
"url": "http://zaneyork.cn/dl/compat/smapi_amazon_138_12.zip",
"hash": "a7746738ee384d2142c2d3075a400d93f95c84bae2d56ce0e84894f24307d7a2"
},
{
"type": "LOCALE",

View File

@ -11,8 +11,6 @@ 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;
import com.lzy.okgo.OkGo;
@ -23,6 +21,7 @@ import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.constant.AppConfigKeyConstants;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.databinding.ActivityMainBinding;
import com.zane.smapiinstaller.dto.AppUpdateCheckResultDto;
import com.zane.smapiinstaller.entity.AppConfig;
import com.zane.smapiinstaller.entity.FrameworkConfig;
@ -45,17 +44,12 @@ import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* @author Zane
@ -64,21 +58,10 @@ public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
@BindView(R.id.launch)
public FloatingActionButton buttonLaunch;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.drawer_layout)
DrawerLayout drawer;
@BindView(R.id.nav_view)
NavigationView navigationView;
private int currentFragment = R.id.nav_main;
public static MainActivity instance;
private ActivityMainBinding binding;
private void requestPermissions() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
@ -109,13 +92,13 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
AppConfig appConfig = ConfigUtils.getConfig((MainApplication) this.getApplication(), AppConfigKeyConstants.PRIVACY_POLICY_CONFIRM, false);
if (Boolean.parseBoolean(appConfig.getValue())) {
requestPermissions();
} else {
CommonLogic.showPrivacyPolicy(toolbar, (dialog, action) -> {
CommonLogic.showPrivacyPolicy(binding.appBarMain.toolbar, (dialog, action) -> {
if (action == DialogAction.POSITIVE) {
appConfig.setValue(String.valueOf(true));
ConfigUtils.saveConfig((MainApplication) this.getApplication(), appConfig);
@ -125,31 +108,32 @@ public class MainActivity extends AppCompatActivity {
}
});
}
binding.appBarMain.launch.setOnClickListener(v -> launchButtonClick());
}
private void initView() {
AppCenter.start(getApplication(), Constants.APP_CENTER_SECRET, Analytics.class, Crashes.class);
setSupportActionBar(toolbar);
setSupportActionBar(binding.appBarMain.toolbar);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_main, R.id.nav_config, R.id.nav_help, R.id.nav_download, R.id.nav_about)
.setOpenableLayout(drawer)
.setOpenableLayout(binding.drawerLayout)
.build();
final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
NavigationUI.setupWithNavController(binding.navView, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
currentFragment = destination.getId();
this.invalidateOptionsMenu();
switch (currentFragment){
switch (currentFragment) {
case R.id.nav_about:
case R.id.nav_help:
case R.id.config_edit_fragment:
buttonLaunch.setVisibility(View.INVISIBLE);
binding.appBarMain.launch.setVisibility(View.INVISIBLE);
break;
default:
buttonLaunch.setVisibility(View.VISIBLE);
binding.appBarMain.launch.setVisibility(View.VISIBLE);
}
});
AppConfig appConfig = ConfigUtils.getConfig((MainApplication) this.getApplication(), AppConfigKeyConstants.IGNORE_UPDATE_VERSION_CODE, Constants.PATCHED_APP_NAME);
@ -168,7 +152,7 @@ public class MainActivity extends AppCompatActivity {
if (StringUtils.equals(appConfig.getValue(), String.valueOf(dto.getVersionCode()))) {
return;
}
DialogUtils.showConfirmDialog(toolbar, R.string.settings_check_for_updates,
DialogUtils.showConfirmDialog(binding.appBarMain.toolbar, R.string.settings_check_for_updates,
MainActivity.this.getString(R.string.app_update_detected, dto.getVersionName()), (dialog, which) -> {
if (which == DialogAction.POSITIVE) {
CommonLogic.openUrl(MainActivity.this, Constants.RELEASE_URL);
@ -181,9 +165,8 @@ public class MainActivity extends AppCompatActivity {
});
}
@OnClick(R.id.launch)
void launchButtonClick() {
new GameLauncher(navigationView).launch();
new GameLauncher(binding.navView).launch();
}
@Override
@ -199,10 +182,9 @@ 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) {
if (currentFragment != R.id.nav_config) {
menu.findItem(R.id.toolbar_update_check).setVisible(false);
}
else {
} else {
menu.findItem(R.id.toolbar_update_check).setVisible(true);
}
menu.findItem(R.id.settings_developer_mode).setChecked(config.isDeveloperMode());
@ -241,7 +223,7 @@ public class MainActivity extends AppCompatActivity {
config.setRewriteMissing(item.isChecked());
break;
case R.id.settings_set_app_name:
DialogUtils.showInputDialog(toolbar, R.string.input, R.string.settings_set_app_name, Constants.PATCHED_APP_NAME, Constants.PATCHED_APP_NAME, true, (dialog, input) -> {
DialogUtils.showInputDialog(binding.appBarMain.toolbar, R.string.input, R.string.settings_set_app_name, Constants.PATCHED_APP_NAME, Constants.PATCHED_APP_NAME, true, (dialog, input) -> {
String appName = input.toString();
AppConfig appConfig = ConfigUtils.getConfig((MainApplication) getApplication(), AppConfigKeyConstants.IGNORE_UPDATE_VERSION_CODE, appName);
appConfig.setValue(appName);
@ -250,7 +232,7 @@ public class MainActivity extends AppCompatActivity {
});
return true;
case R.id.settings_set_mod_path:
DialogUtils.showInputDialog(toolbar, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
DialogUtils.showInputDialog(binding.appBarMain.toolbar, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
if (StringUtils.isNoneBlank(input)) {
String pathString = input.toString();
File file = new File(Environment.getExternalStorageDirectory(), pathString);
@ -259,7 +241,7 @@ public class MainActivity extends AppCompatActivity {
config.setModsPath(pathString);
manager.flushConfig();
} else {
DialogUtils.showAlertDialog(drawer, R.string.error, R.string.error_illegal_path);
DialogUtils.showAlertDialog(binding.drawerLayout, R.string.error, R.string.error_illegal_path);
}
}
});
@ -303,7 +285,7 @@ public class MainActivity extends AppCompatActivity {
MainApplication application = (MainApplication) this.getApplication();
AppConfig activeTranslator = ConfigUtils.getConfig(application, AppConfigKeyConstants.ACTIVE_TRANSLATOR, TranslateUtil.NONE);
int index = getTranslateServiceIndex(activeTranslator);
DialogUtils.showSingleChoiceDialog(toolbar, R.string.settings_translation_service, R.array.translators, index, (dialog, position) -> {
DialogUtils.showSingleChoiceDialog(binding.appBarMain.toolbar, R.string.settings_translation_service, R.array.translators, index, (dialog, position) -> {
switch (position) {
case 0:
activeTranslator.setValue(TranslateUtil.NONE);
@ -323,7 +305,7 @@ public class MainActivity extends AppCompatActivity {
}
private void selectLanguageLogic() {
DialogUtils.showListItemsDialog(toolbar, R.string.settings_set_language, R.array.languages, (dialog, position) -> {
DialogUtils.showListItemsDialog(binding.appBarMain.toolbar, R.string.settings_set_language, R.array.languages, (dialog, position) -> {
boolean restart;
switch (position) {
case 0:
@ -369,7 +351,7 @@ public class MainActivity extends AppCompatActivity {
}
private void checkModUpdateLogic() {
ModAssetsManager modAssetsManager = new ModAssetsManager(toolbar);
ModAssetsManager modAssetsManager = new ModAssetsManager(binding.appBarMain.toolbar);
modAssetsManager.checkModUpdate((list) -> {
if (list.isEmpty()) {
CommonLogic.runOnUiThread(this, (activity) -> Toast.makeText(activity, R.string.no_update_text, Toast.LENGTH_SHORT).show());
@ -418,7 +400,7 @@ public class MainActivity extends AppCompatActivity {
}
public void setFloatingBarVisibility(boolean value) {
buttonLaunch.setVisibility(value ? View.VISIBLE: View.INVISIBLE);
binding.appBarMain.launch.setVisibility(value ? View.VISIBLE : View.INVISIBLE);
}
}

View File

@ -67,7 +67,7 @@ public class Constants {
/**
* SMAPI版本
*/
public static final String SMAPI_VERSION = "3.7.2";
public static final String SMAPI_VERSION = "3.7.3.3";
/**
* 应用名称

View File

@ -6,6 +6,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
/**
* @author Zane
*/
public class ActivityResultHandler {
public static int REQUEST_CODE_APP_INSTALL = 1001;

View File

@ -33,7 +33,9 @@ import com.zane.smapiinstaller.utils.ZipUtils;
import net.fornwall.apksigner.KeyStoreFileManager;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.zeroturnaround.zip.ZipUtil;
import java.io.File;
@ -52,6 +54,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.Deflater;
import androidx.core.content.FileProvider;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -166,7 +169,7 @@ public class ApkPatcher {
return patch(apkPath, false);
}
public boolean patch(String apkPath, boolean advanced) {
public boolean patch(String apkPath, boolean isAdvanced) {
if (apkPath == null) {
return false;
}
@ -191,38 +194,9 @@ public class ApkPatcher {
ApkFilesManifest apkFilesManifest = apkFilesManifests.get(0);
List<ManifestEntry> manifestEntries = apkFilesManifest.getManifestEntries();
errorMessage.set(null);
List<ZipUtils.ZipEntrySource> entries = manifestEntries.stream().map(entry -> {
if (entry.isAdvanced() && !advanced) {
return null;
}
byte[] bytes;
if (entry.isExternal()) {
bytes = FileUtils.getAssetBytes(context, apkFilesManifest.getBasePath() + entry.getAssetPath());
} else {
bytes = FileUtils.getAssetBytes(context, entry.getAssetPath());
}
if (StringUtils.isNoneBlank(entry.getPatchCrc())) {
byte[] originBytes = ZipUtil.unpackEntry(file, entry.getTargetPath());
if (originBytes != null) {
String crc = Integer.toHexString(Hashing.crc32().hashBytes(originBytes).hashCode());
if (StringUtils.equals(crc, entry.getPatchCrc())) {
bytes = FileUtils.patchFile(originBytes, bytes);
if (bytes == null) {
String errorMsg = context.getString(R.string.error_patch_crc_incorrect, entry.getTargetPath(), crc);
errorMessage.set(StringUtils.stripToEmpty(errorMessage.get()) + "\n" + errorMsg);
return null;
}
} else if (StringUtils.equals(crc, entry.getPatchedCrc())) {
bytes = originBytes;
} else {
String errorMsg = context.getString(R.string.error_patch_crc_incorrect, entry.getTargetPath(), crc);
errorMessage.set(StringUtils.stripToEmpty(errorMessage.get()) + "\n" + errorMsg);
return null;
}
}
}
return new ZipUtils.ZipEntrySource(entry.getTargetPath(), bytes, entry.getCompression());
}).filter(Objects::nonNull).collect(Collectors.toList());
List<ZipUtils.ZipEntrySource> entries = manifestEntries.stream()
.map(entry -> processfileentry(file, apkFilesManifest, entry, isAdvanced))
.filter(Objects::nonNull).collect(Collectors.toList());
if (errorMessage.get() != null) {
return false;
}
@ -248,6 +222,23 @@ public class ApkPatcher {
return false;
}
@Nullable
private ZipUtils.ZipEntrySource processfileentry(File file, ApkFilesManifest apkFilesManifest, ManifestEntry entry, boolean isAdvanced) {
if (entry.isAdvanced() && !isAdvanced) {
return null;
}
byte[] bytes;
if (entry.isExternal()) {
bytes = FileUtils.getAssetBytes(context, apkFilesManifest.getBasePath() + entry.getAssetPath());
} else {
bytes = FileUtils.getAssetBytes(context, entry.getAssetPath());
}
if (StringUtils.isNoneBlank(entry.getPatchCrc())) {
throw new NotImplementedException("bs patch mode is not supported anymore.");
}
return new ZipUtils.ZipEntrySource(entry.getTargetPath(), bytes, entry.getCompression());
}
/**
* 扫描全部兼容包寻找匹配的版本修改AndroidManifest.xml文件
*
@ -279,10 +270,9 @@ public class ApkPatcher {
break;
case "label":
if (strObj.contains(ManifestPatchConstants.APP_NAME)) {
if(StringUtils.isBlank(Constants.PATCHED_APP_NAME)) {
if (StringUtils.isBlank(Constants.PATCHED_APP_NAME)) {
attr.obj = context.getString(R.string.smapi_game_name);
}
else {
} else {
attr.obj = Constants.PATCHED_APP_NAME;
}
}
@ -412,7 +402,7 @@ public class ApkPatcher {
boolean haveInstallPermission = context.getPackageManager().canRequestPackageInstalls();
if (!haveInstallPermission) {
DialogUtils.showConfirmDialog(MainActivity.instance, R.string.confirm, R.string.request_unknown_source_permission, ((dialog, dialogAction) -> {
if(dialogAction == DialogAction.POSITIVE) {
if (dialogAction == DialogAction.POSITIVE) {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_APP_INSTALL, (resultCode, data) -> this.install(apkPath));
MainActivity.instance.startActivityForResult(intent, ActivityResultHandler.REQUEST_CODE_APP_INSTALL);

View File

@ -8,51 +8,49 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.databinding.FragmentAboutBinding;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.utils.DialogUtils;
import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* @author Zane
*/
public class AboutFragment extends Fragment {
@BindView(R.id.img_heart)
ImageView imgHeart;
private FragmentAboutBinding binding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_about, container, false);
ButterKnife.bind(this, root);
return root;
binding = FragmentAboutBinding.inflate(inflater, container, false);
binding.buttonRelease.setOnClickListener(v -> release());
binding.buttonQqGroup1.setOnClickListener(v -> joinQQ());
binding.buttonDonation.setOnClickListener(v -> donation());
binding.buttonPrivacyPolicy.setOnClickListener(v -> privacyPolicy());
return binding.getRoot();
}
@OnClick(R.id.button_release)
void release() {
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void release() {
CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, Constants.RELEASE_URL));
}
// @OnClick(R.id.button_gplay)
void gplay() {
CommonLogic.openInPlayStore(this.getActivity());
}
@OnClick(R.id.button_qq_group_1)
void joinQQ() {
private void joinQQ() {
String baseUrl = "mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D";
DialogUtils.showListItemsDialog(imgHeart, R.string.button_qq_group_text, R.array.qq_group_list, (dialog, position) -> {
switch (position){
DialogUtils.showListItemsDialog(binding.imgHeart, R.string.button_qq_group_text, R.array.qq_group_list, (dialog, position) -> {
switch (position) {
case 0:
CommonLogic.doOnNonNull(this.getContext(), (context) -> CommonLogic.openUrl(context, baseUrl + "AAflCLHiWw1haM1obu_f-CpGsETxXc6b"));
break;
@ -66,16 +64,14 @@ public class AboutFragment extends Fragment {
});
}
@OnClick(R.id.button_donation)
void donation() {
DialogUtils.showListItemsDialog(imgHeart, R.string.button_donation_text, R.array.donation_methods, (dialog, position) ->
CommonLogic.showAnimation(imgHeart, R.anim.heart_beat, (animation) ->
DialogUtils.showListItemsDialog(binding.imgHeart, R.string.button_donation_text, R.array.donation_methods, (dialog, position) ->
CommonLogic.showAnimation(binding.imgHeart, R.anim.heart_beat, (animation) ->
CommonLogic.doOnNonNull(this.getActivity(), (activity) -> listSelectLogic(activity, position))));
}
@OnClick(R.id.button_privacy_policy)
void privacyPolicy() {
CommonLogic.showPrivacyPolicy(imgHeart, (dialog, dialogAction) -> {
private void privacyPolicy() {
CommonLogic.showPrivacyPolicy(binding.imgHeart, (dialog, dialogAction) -> {
});
}

View File

@ -7,13 +7,12 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import com.zane.smapiinstaller.BuildConfig;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.databinding.FragmentConfigEditBinding;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
@ -27,50 +26,40 @@ import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* @author Zane
*/
public class ConfigEditFragment extends Fragment {
@BindView(R.id.edit_text_config_edit)
EditText editText;
private Boolean editable;
private String configPath;
@BindView(R.id.button_config_save)
Button buttonConfigSave;
@BindView(R.id.button_config_cancel)
Button buttonConfigCancel;
@BindView(R.id.button_log_parser)
Button buttonLogParser;
private FragmentConfigEditBinding binding;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config_edit, container, false);
ButterKnife.bind(this, root);
binding = FragmentConfigEditBinding.inflate(inflater, container, false);
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
ConfigEditFragmentArgs args = ConfigEditFragmentArgs.fromBundle(arguments);
editable = args.getEditable();
if (!editable) {
editText.setKeyListener(null);
buttonConfigSave.setVisibility(View.INVISIBLE);
buttonConfigCancel.setVisibility(View.INVISIBLE);
buttonLogParser.setVisibility(View.VISIBLE);
binding.editTextConfigEdit.setKeyListener(null);
binding.buttonConfigSave.setVisibility(View.INVISIBLE);
binding.buttonConfigCancel.setVisibility(View.INVISIBLE);
binding.buttonLogParser.setVisibility(View.VISIBLE);
}
configPath = args.getConfigPath();
File file = new File(configPath);
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
String fileText = FileUtils.getFileText(file);
if (fileText != null) {
editText.setText(fileText);
binding.editTextConfigEdit.setText(fileText);
}
} else {
editText.setText("");
editText.setKeyListener(null);
DialogUtils.showConfirmDialog(root, R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
binding.editTextConfigEdit.setText("");
binding.editTextConfigEdit.setKeyListener(null);
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
@ -90,16 +79,23 @@ public class ConfigEditFragment extends Fragment {
}));
}
});
return root;
binding.buttonConfigCancel.setOnClickListener(v -> onConfigCancel());
binding.buttonConfigSave.setOnClickListener(v -> onConfigSave());
return binding.getRoot();
}
@OnClick(R.id.button_config_save)
void onConfigSave() {
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void onConfigSave() {
try {
JsonUtil.checkJson(editText.getText().toString());
JsonUtil.checkJson(binding.editTextConfigEdit.getText().toString());
FileOutputStream outputStream = new FileOutputStream(configPath);
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
outputStreamWriter.write(editText.getText().toString());
outputStreamWriter.write(binding.editTextConfigEdit.getText().toString());
outputStreamWriter.flush();
}
} catch (Exception e) {
@ -107,13 +103,11 @@ public class ConfigEditFragment extends Fragment {
}
}
@OnClick(R.id.button_config_cancel)
void onConfigCancel() {
private void onConfigCancel() {
CommonLogic.doOnNonNull(getView(), view -> Navigation.findNavController(view).popBackStack());
}
@OnClick(R.id.button_log_parser)
void onLogParser() {
private void onLogParser() {
CommonLogic.doOnNonNull(getContext(), context -> CommonLogic.openUrl(context, "https://smapi.io/log"));
}
}

View File

@ -6,7 +6,9 @@ import android.view.View;
import android.view.ViewGroup;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.databinding.FragmentConfigBinding;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.function.TextChangedWatcher;
import java.util.ArrayList;
@ -14,44 +16,40 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
/**
* @author Zane
*/
public class ConfigFragment extends Fragment {
@BindView(R.id.view_mod_list)
RecyclerView recyclerView;
private ConfigViewModel configViewModel;
private FragmentConfigBinding binding;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_config, container, false);
ButterKnife.bind(this, root);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
configViewModel = new ConfigViewModel(root);
binding = FragmentConfigBinding.inflate(inflater, container, false);
binding.viewModList.setLayoutManager(new LinearLayoutManager(this.getContext()));
configViewModel = new ConfigViewModel(binding.getRoot());
ModManifestAdapter modManifestAdapter = new ModManifestAdapter(configViewModel, new ArrayList<>(configViewModel.getModList()));
recyclerView.setAdapter(modManifestAdapter);
binding.viewModList.setAdapter(modManifestAdapter);
configViewModel.registerOnChangeListener((list) -> {
modManifestAdapter.setList(new ArrayList<>(list));
return false;
});
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
return root;
binding.viewModList.addItemDecoration(new DividerItemDecoration(binding.viewModList.getContext(), DividerItemDecoration.VERTICAL));
binding.buttonSearch.addTextChangedListener((TextChangedWatcher) (s, start, before, count) -> configViewModel.filter(s));
binding.buttonSortBy.setOnClickListener(v -> onSortByClick());
return binding.getRoot();
}
@OnTextChanged(R.id.button_search)
void onSearchMod(CharSequence text) {
configViewModel.filter(text);
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
@OnClick(R.id.button_sort_by)
void onSortByClick() {
int index = 0;
switch (configViewModel.getSortBy()) {
@ -69,7 +67,7 @@ public class ConfigFragment extends Fragment {
break;
default:
}
DialogUtils.showSingleChoiceDialog(recyclerView, R.string.sort_by, R.array.mod_list_sort_by, index, (dialog, position) -> {
DialogUtils.showSingleChoiceDialog(binding.viewModList, R.string.sort_by, R.array.mod_list_sort_by, index, (dialog, position) -> {
switch (position) {
case 0:
configViewModel.switchSortBy("Name asc");

View File

@ -4,13 +4,12 @@ import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.zane.smapiinstaller.MobileNavigationDirections;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.databinding.ModListItemBinding;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.utils.DialogUtils;
import com.zane.smapiinstaller.utils.FileUtils;
@ -21,15 +20,12 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import androidx.annotation.NonNull;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import java.util.function.Predicate;
/**
* @author Zane
@ -38,23 +34,23 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
private ConfigViewModel model;
private List<ModManifestEntry> modList;
public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList){
this.model=model;
public ModManifestAdapter(ConfigViewModel model, List<ModManifestEntry> modList) {
this.model = model;
this.modList = modList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.mod_list_item,parent, false);
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.mod_list_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ModManifestEntry mod = modList.get(position);
holder.modName.setText(mod.getName());
holder.modDescription.setText(StringUtils.firstNonBlank(mod.getTranslatedDescription(), mod.getDescription()));
holder.binding.textViewModName.setText(mod.getName());
holder.binding.textViewModDescription.setText(StringUtils.firstNonBlank(mod.getTranslatedDescription(), mod.getDescription()));
holder.setModPath(mod.getAssetPath());
}
@ -68,37 +64,35 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder{
class ViewHolder extends RecyclerView.ViewHolder {
private ModListItemBinding binding;
private String modPath;
void setModPath(String modPath) {
this.modPath = modPath;
File file = new File(modPath, "config.json");
if(!file.exists()) {
configModButton.setVisibility(View.INVISIBLE);
}
else {
configModButton.setVisibility(View.VISIBLE);
if (!file.exists()) {
binding.buttonConfigMod.setVisibility(View.INVISIBLE);
} else {
binding.buttonConfigMod.setVisibility(View.VISIBLE);
}
setStrike();
}
@BindView(R.id.button_config_mod)
Button configModButton;
@BindView(R.id.text_view_mod_name)
TextView modName;
@BindView(R.id.text_view_mod_description)
TextView modDescription;
ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
ViewHolder(@NonNull View view) {
super(view);
binding = ModListItemBinding.bind(view);
binding.buttonRemoveMod.setOnClickListener(v -> removeMod());
binding.buttonDisableMod.setOnClickListener(v -> disableMod());
binding.buttonConfigMod.setOnClickListener(v -> configMod());
}
private void setStrike() {
File file = new File(modPath);
if(StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
modName.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
}
else {
modName.getPaint().setFlags(modName.getPaint().getFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
if (StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
binding.textViewModName.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
} else {
binding.textViewModName.getPaint().setFlags(binding.textViewModName.getPaint().getFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
}
}
@ -113,8 +107,8 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
return deletedId;
}
@OnClick(R.id.button_remove_mod) void removeMod() {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which)->{
void removeMod() {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_delete_content, (dialog, which) -> {
if (which == DialogAction.POSITIVE) {
File file = new File(modPath);
if (file.exists()) {
@ -132,16 +126,16 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
}
});
}
@OnClick(R.id.button_disable_mod) void disableMod() {
void disableMod() {
File file = new File(modPath);
if(file.exists() && file.isDirectory()) {
if(StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
if (file.exists() && file.isDirectory()) {
if (StringUtils.startsWith(file.getName(), Constants.HIDDEN_FILE_PREFIX)) {
File newFile = new File(file.getParent(), StringUtils.stripStart(file.getName(), "."));
moveMod(file, newFile);
}
else {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_disable_mod, (dialog, which)-> {
if(which == DialogAction.POSITIVE) {
} else {
DialogUtils.showConfirmDialog(itemView, R.string.confirm, R.string.confirm_disable_mod, (dialog, which) -> {
if (which == DialogAction.POSITIVE) {
File newFile = new File(file.getParent(), "." + file.getName());
moveMod(file, newFile);
}
@ -172,9 +166,9 @@ public class ModManifestAdapter extends RecyclerView.Adapter<ModManifestAdapter.
}
}
@OnClick(R.id.button_config_mod) void configMod() {
void configMod() {
File file = new File(modPath, "config.json");
if(file.exists()) {
if (file.exists()) {
NavController controller = Navigation.findNavController(itemView);
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = ConfigFragmentDirections.actionNavAnyToConfigEditFragment(file.getAbsolutePath());
controller.navigate(action);

View File

@ -4,8 +4,6 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.lmntrx.android.library.livin.missme.ProgressDialog;
import com.lzy.okgo.OkGo;
@ -16,6 +14,7 @@ import com.microsoft.appcenter.crashes.Crashes;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.constant.DownloadableContentTypeConstants;
import com.zane.smapiinstaller.databinding.DownloadContentItemBinding;
import com.zane.smapiinstaller.entity.DownloadableContent;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.ModAssetsManager;
@ -33,9 +32,6 @@ import java.util.concurrent.atomic.AtomicReference;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* {@link RecyclerView.Adapter} that can display a {@link DownloadableContent}
@ -74,50 +70,43 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.text_item_type)
TextView typeTextView;
@BindView(R.id.text_item_name)
TextView nameTextView;
@BindView(R.id.text_item_description)
TextView descriptionTextView;
@BindView(R.id.button_remove_content)
Button buttonRemove;
@BindView(R.id.button_download_content)
Button buttonDownload;
private DownloadContentItemBinding binding;
private final AtomicBoolean downloading = new AtomicBoolean(false);
public DownloadableContent downloadableContent;
public void setDownloadableContent(DownloadableContent downloadableContent) {
this.downloadableContent = downloadableContent;
typeTextView.setText(downloadableContent.getType());
nameTextView.setText(downloadableContent.getName());
descriptionTextView.setText(downloadableContent.getDescription());
binding.textItemType.setText(downloadableContent.getType());
binding.textItemName.setText(downloadableContent.getName());
binding.textItemDescription.setText(downloadableContent.getDescription());
if (StringUtils.isNoneBlank(downloadableContent.getAssetPath())) {
File contentFile = new File(itemView.getContext().getFilesDir(), downloadableContent.getAssetPath());
if (contentFile.exists()) {
Context context = itemView.getContext();
File file = new File(context.getCacheDir(), downloadableContent.getName() + ".zip");
if (!file.exists() || !StringUtils.equalsIgnoreCase(FileUtils.getFileHash(file), downloadableContent.getHash())) {
buttonRemove.setVisibility(View.VISIBLE);
buttonDownload.setVisibility(View.VISIBLE);
binding.buttonRemoveContent.setVisibility(View.VISIBLE);
binding.buttonDownloadContent.setVisibility(View.VISIBLE);
return;
}
buttonRemove.setVisibility(View.VISIBLE);
buttonDownload.setVisibility(View.INVISIBLE);
binding.buttonRemoveContent.setVisibility(View.VISIBLE);
binding.buttonDownloadContent.setVisibility(View.INVISIBLE);
return;
}
}
buttonRemove.setVisibility(View.INVISIBLE);
buttonDownload.setVisibility(View.VISIBLE);
binding.buttonRemoveContent.setVisibility(View.INVISIBLE);
binding.buttonDownloadContent.setVisibility(View.VISIBLE);
}
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, itemView);
binding = DownloadContentItemBinding.bind(view);
binding.buttonRemoveContent.setOnClickListener(v -> removeContent());
binding.buttonDownloadContent.setOnClickListener(v -> downloadContent());
}
@OnClick(R.id.button_remove_content)
void removeContent() {
if (StringUtils.isNoneBlank(downloadableContent.getAssetPath())) {
File contentFile = new File(itemView.getContext().getFilesDir(), downloadableContent.getAssetPath());
@ -126,8 +115,8 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
if (which == DialogAction.POSITIVE) {
try {
FileUtils.forceDelete(contentFile);
buttonDownload.setVisibility(View.VISIBLE);
buttonRemove.setVisibility(View.INVISIBLE);
binding.buttonDownloadContent.setVisibility(View.VISIBLE);
binding.buttonRemoveContent.setVisibility(View.INVISIBLE);
} catch (IOException e) {
DialogUtils.showAlertDialog(itemView, R.string.error, e.getLocalizedMessage());
}
@ -137,7 +126,6 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
}
}
@OnClick(R.id.button_download_content)
void downloadContent() {
Context context = itemView.getContext();
ModManifestEntry modManifestEntry = null;
@ -212,8 +200,8 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter<Downloadabl
ZipUtil.unpack(downloadedFile, new File(context.getFilesDir(), downloadableContent.getAssetPath()));
}
DialogUtils.showAlertDialog(itemView, R.string.info, R.string.download_unpack_success);
buttonDownload.setVisibility(View.INVISIBLE);
buttonRemove.setVisibility(View.VISIBLE);
binding.buttonDownloadContent.setVisibility(View.INVISIBLE);
binding.buttonRemoveContent.setVisibility(View.VISIBLE);
} catch (Exception e) {
DialogUtils.showAlertDialog(itemView, R.string.error, e.getLocalizedMessage());
}

View File

@ -7,8 +7,8 @@ import android.view.View;
import android.view.ViewGroup;
import com.zane.smapiinstaller.MobileNavigationDirections;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.databinding.FragmentHelpBinding;
import com.zane.smapiinstaller.entity.HelpItemList;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.UpdatableListManager;
@ -21,46 +21,52 @@ import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* @author Zane
*/
public class HelpFragment extends Fragment {
@BindView(R.id.view_help_list)
RecyclerView recyclerView;
private FragmentHelpBinding binding;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_help, container, false);
ButterKnife.bind(this, root);
recyclerView.setLayoutManager(new LinearLayoutManager(this.getContext()));
UpdatableListManager<HelpItemList> manager = new UpdatableListManager<>(root, "help_item_list.json", HelpItemList.class, Constants.HELP_LIST_UPDATE_URL);
binding = FragmentHelpBinding.inflate(inflater, container, false);
binding.viewHelpList.setLayoutManager(new LinearLayoutManager(this.getContext()));
UpdatableListManager<HelpItemList> manager = new UpdatableListManager<>(binding.getRoot(), "help_item_list.json", HelpItemList.class, Constants.HELP_LIST_UPDATE_URL);
HelpItemAdapter adapter = new HelpItemAdapter(manager.getList().getItems());
recyclerView.setAdapter(adapter);
binding.viewHelpList.setAdapter(adapter);
manager.registerOnChangeListener((list) -> {
adapter.setHelpItems(list.getItems());
return false;
});
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
return root;
binding.viewHelpList.addItemDecoration(new DividerItemDecoration(binding.viewHelpList.getContext(), DividerItemDecoration.VERTICAL));
binding.buttonCompat.setOnClickListener(v -> compat());
binding.buttonNexus.setOnClickListener(v -> nexus());
binding.buttonLogs.setOnClickListener(v -> showLog());
return binding.getRoot();
}
@OnClick(R.id.button_compat) void compat() {
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void compat() {
CommonLogic.doOnNonNull(this.getContext(), context -> CommonLogic.openUrl(context, "https://smapi.io/mods"));
}
@OnClick(R.id.button_nexus) void nexus() {
private void nexus() {
CommonLogic.doOnNonNull(this.getContext(), context -> CommonLogic.openUrl(context, "https://www.nexusmods.com/stardewvalley/mods/"));
}
@OnClick({R.id.button_logs}) void showLog() {
private void showLog() {
CommonLogic.doOnNonNull(this.getView(), view -> {
NavController controller = Navigation.findNavController(view);
File logFile = new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH);
if(logFile.exists()) {
if (logFile.exists()) {
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = HelpFragmentDirections.actionNavAnyToConfigEditFragment(logFile.getAbsolutePath());
action.setEditable(false);
controller.navigate(action);

View File

@ -5,17 +5,15 @@ import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.databinding.HelpListItemBinding;
import com.zane.smapiinstaller.entity.HelpItem;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @author Zane
@ -52,26 +50,21 @@ public class HelpItemAdapter extends RecyclerView.Adapter<HelpItemAdapter.ViewHo
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.text_item_title)
TextView textTitle;
@BindView(R.id.text_item_author)
TextView textAuthor;
@BindView(R.id.text_item_content)
TextView textContent;
private HelpListItemBinding binding;
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, itemView);
binding = HelpListItemBinding.bind(view);
}
void setHelpItem(HelpItem item) {
textTitle.setText(item.getTitle());
textAuthor.setText(item.getAuthor());
binding.textItemTitle.setText(item.getTitle());
binding.textItemAuthor.setText(item.getAuthor());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
textContent.setText(Html.fromHtml(item.getContent(), Html.FROM_HTML_MODE_COMPACT));
binding.textItemContent.setText(Html.fromHtml(item.getContent(), Html.FROM_HTML_MODE_COMPACT));
} else {
textContent.setText(Html.fromHtml(item.getContent()));
binding.textItemContent.setText(Html.fromHtml(item.getContent()));
}
textContent.setMovementMethod(LinkMovementMethod.getInstance());
binding.textItemContent.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}

View File

@ -7,9 +7,6 @@ import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.common.io.Files;
import com.zane.smapiinstaller.MainApplication;
@ -17,6 +14,7 @@ import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.constant.AppConfigKeyConstants;
import com.zane.smapiinstaller.constant.Constants;
import com.zane.smapiinstaller.constant.DialogAction;
import com.zane.smapiinstaller.databinding.FragmentInstallBinding;
import com.zane.smapiinstaller.logic.ApkPatcher;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.logic.ModAssetsManager;
@ -35,9 +33,6 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* @author Zane
@ -48,44 +43,42 @@ public class InstallFragment extends Fragment {
private Thread task;
private View root;
@BindView(R.id.button_install)
Button installButton;
@BindView(R.id.text_latest_running)
TextView textLatestRunning;
@BindView(R.id.layout_adv_install)
LinearLayout layoutAdvInstall;
private FragmentInstallBinding binding;
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_install, container, false);
ButterKnife.bind(this, root);
binding = FragmentInstallBinding.inflate(inflater, container, false);
context = this.getActivity();
if (Boolean.parseBoolean(ConfigUtils.getConfig((MainApplication) context.getApplication(), AppConfigKeyConstants.ADVANCED_MODE, "false").getValue())) {
installButton.setVisibility(View.INVISIBLE);
layoutAdvInstall.setVisibility(View.VISIBLE);
binding.buttonInstall.setVisibility(View.INVISIBLE);
binding.layoutAdvInstall.setVisibility(View.VISIBLE);
}
try {
String firstLine = Files.asCharSource(new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine();
if (StringUtils.isNoneBlank(firstLine)) {
String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+");
versionString = RegExUtils.removePattern(versionString, "\\s+with.+");
textLatestRunning.setText(context.getString(R.string.smapi_version_runing, versionString));
textLatestRunning.setVisibility(View.VISIBLE);
binding.textLatestRunning.setText(context.getString(R.string.smapi_version_runing, versionString));
binding.textLatestRunning.setVisibility(View.VISIBLE);
}
} catch (IOException ignored) {
}
return root;
binding.buttonInstall.setOnClickListener(v -> InstallFragment.this.install());
binding.buttonAdvInitial.setOnClickListener(v -> InstallFragment.this.initialLogic());
binding.buttonAdvInstall.setOnClickListener(v -> InstallFragment.this.installLogic(true));
return binding.getRoot();
}
@OnClick(R.id.button_install)
void install() {
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void install() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
DialogUtils.showConfirmDialog(root, R.string.confirm, R.string.android_version_confirm, ((dialog, which) -> {
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.confirm, R.string.android_version_confirm, ((dialog, which) -> {
if (which == DialogAction.POSITIVE) {
installLogic(false);
}
@ -95,16 +88,6 @@ public class InstallFragment extends Fragment {
}
}
@OnClick(R.id.button_adv_initial)
void advInitial() {
initialLogic();
}
@OnClick(R.id.button_adv_install)
void advInstall() {
installLogic(true);
}
/**
* 初始化逻辑
*/
@ -112,13 +95,13 @@ public class InstallFragment extends Fragment {
if (task != null) {
task.interrupt();
}
task = new Thread(() -> CommonLogic.showProgressDialog(root, context, (dialog)->{
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog)->{
ApkPatcher patcher = new ApkPatcher(context);
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(root, dialog, null, progress));
DialogUtils.setProgressDialogState(root, dialog, R.string.extracting_package, null);
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(binding.getRoot(), dialog, null, progress));
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.extracting_package, null);
String path = patcher.extract(0);
if (path == null) {
DialogUtils.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
}
}));
task.start();
@ -131,48 +114,47 @@ public class InstallFragment extends Fragment {
if (task != null) {
task.interrupt();
}
task = new Thread(() -> CommonLogic.showProgressDialog(root, context, (dialog)-> {
task = new Thread(() -> CommonLogic.showProgressDialog(binding.getRoot(), context, (dialog)-> {
ApkPatcher patcher = new ApkPatcher(context);
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(root, dialog, null, progress));
DialogUtils.setProgressDialogState(root, dialog, R.string.extracting_package, null);
patcher.registerProgressListener((progress) -> DialogUtils.setProgressDialogState(binding.getRoot(), dialog, null, progress));
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.extracting_package, null);
String path = patcher.extract(isAdv ? 1 : -1);
if (path == null) {
DialogUtils.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.error_game_not_found)));
return;
}
DialogUtils.setProgressDialogState(root, dialog, R.string.unpacking_smapi_files, null);
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, null);
if (!CommonLogic.unpackSmapiFiles(context, path, false)) {
DialogUtils.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_unpack_smapi_files)));
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_unpack_smapi_files)));
return;
}
ModAssetsManager modAssetsManager = new ModAssetsManager(root);
DialogUtils.setProgressDialogState(root, dialog, R.string.unpacking_smapi_files, 6);
ModAssetsManager modAssetsManager = new ModAssetsManager(binding.getRoot());
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.unpacking_smapi_files, 6);
modAssetsManager.installDefaultMods();
DialogUtils.setProgressDialogState(root, dialog, R.string.patching_package, 8);
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.patching_package, 8);
if (!patcher.patch(path, isAdv)) {
int target = patcher.getSwitchAction().getAndSet(0);
if (target == R.string.menu_download) {
DialogUtils.showConfirmDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)), R.string.menu_download, R.string.cancel, (d, which) -> {
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)), R.string.menu_download, R.string.cancel, (d, which) -> {
if (which == DialogAction.POSITIVE) {
NavController controller = Navigation.findNavController(installButton);
NavController controller = Navigation.findNavController(binding.buttonInstall);
controller.navigate(MainTabsFragmentDirections.actionNavMainToNavDownload());
}
});
} else {
DialogUtils.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)));
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_patch_game)));
}
return;
}
DialogUtils.setProgressDialogState(root, dialog, R.string.signing_package, null);
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.signing_package, null);
String signPath = patcher.sign(path);
if (signPath == null) {
DialogUtils.showAlertDialog(root, R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_sign_game)));
DialogUtils.showAlertDialog(binding.getRoot(), R.string.error, StringUtils.firstNonBlank(patcher.getErrorMessage().get(), context.getString(R.string.failed_to_sign_game)));
return;
}
DialogUtils.setProgressDialogState(root, dialog, R.string.installing_package, null);
DialogUtils.setProgressDialogState(binding.getRoot(), dialog, R.string.installing_package, null);
patcher.install(signPath);
}));
task.start();
}
}

View File

@ -5,46 +5,45 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.zane.smapiinstaller.MainActivity;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.databinding.FragmentMainBinding;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @author Zane
*/
public class MainTabsFragment extends Fragment {
@BindView(R.id.main_tab_layout)
TabLayout tabLayout;
@BindView(R.id.main_pager)
ViewPager2 viewPager;
private FragmentMainBinding binding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.bind(this, view);
return view;
binding = FragmentMainBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
MainTabPagerAdapter pagerAdapter = new MainTabPagerAdapter(this);
viewPager.setAdapter(pagerAdapter);
new TabLayoutMediator(tabLayout, viewPager,
binding.mainPager.setAdapter(pagerAdapter);
new TabLayoutMediator(binding.mainTabLayout, binding.mainPager,
(tab, position) -> tab.setText(pagerAdapter.getTitle(position))
).attach();
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
binding.mainPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);

View File

@ -3,11 +3,11 @@ package com.zane.smapiinstaller.ui.update;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.databinding.UpdatableModListItemBinding;
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
import com.zane.smapiinstaller.entity.ModManifestEntry;
import com.zane.smapiinstaller.logic.CommonLogic;
@ -15,13 +15,10 @@ import com.zane.smapiinstaller.logic.ModAssetsManager;
import com.zane.smapiinstaller.utils.VersionUtil;
import java.util.List;
import java.util.Optional;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import java.util.Optional;
/**
* {@link RecyclerView.Adapter} that can display a {@link ModUpdateCheckResponseDto.UpdateInfo}
@ -59,20 +56,17 @@ public class ModUpdateAdapter extends RecyclerView.Adapter<ModUpdateAdapter.View
class ViewHolder extends RecyclerView.ViewHolder {
public ModUpdateCheckResponseDto updateInfo;
@BindView(R.id.text_view_mod_name)
TextView textModName;
@BindView(R.id.text_view_mod_version)
TextView textModVersion;
private UpdatableModListItemBinding binding;
public void setUpdateInfo(ModUpdateCheckResponseDto updateInfo) {
this.updateInfo = updateInfo;
String id = updateInfo.getId();
Optional<ModManifestEntry> mod = installedModMap.get(id).stream().sorted((a, b) -> VersionUtil.compareVersion(a.getVersion(), b.getVersion())).findFirst();
Optional<ModManifestEntry> mod = installedModMap.get(id).stream().min((a, b) -> VersionUtil.compareVersion(a.getVersion(), b.getVersion()));
if (mod.isPresent()) {
ModManifestEntry modManifestEntry = mod.get();
textModName.setText(modManifestEntry.getName());
CommonLogic.doOnNonNull(CommonLogic.getActivityFromView(textModName),
activity -> textModVersion.setText(
binding.textViewModName.setText(modManifestEntry.getName());
CommonLogic.doOnNonNull(CommonLogic.getActivityFromView(binding.textViewModName),
activity -> binding.textViewModVersion.setText(
activity.getString(R.string.mod_version_update_text, modManifestEntry.getVersion(), updateInfo.getSuggestedUpdate().getVersion())
));
}
@ -80,12 +74,12 @@ public class ModUpdateAdapter extends RecyclerView.Adapter<ModUpdateAdapter.View
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, itemView);
binding = UpdatableModListItemBinding.bind(view);
binding.buttonUpdateMod.setOnClickListener(v -> onUpdateClick());
}
@OnClick(R.id.button_update_mod)
void onUpdateClick() {
CommonLogic.doOnNonNull(CommonLogic.getActivityFromView(textModName), context -> CommonLogic.openUrl(context, updateInfo.getSuggestedUpdate().getUrl()));
CommonLogic.doOnNonNull(CommonLogic.getActivityFromView(binding.textViewModName), context -> CommonLogic.openUrl(context, updateInfo.getSuggestedUpdate().getUrl()));
}
}
}

View File

@ -7,7 +7,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.fasterxml.jackson.core.type.TypeReference;
import com.zane.smapiinstaller.R;
import com.zane.smapiinstaller.databinding.FragmentModUpdateListBinding;
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
import com.zane.smapiinstaller.logic.CommonLogic;
import com.zane.smapiinstaller.utils.JsonUtil;
@ -25,6 +25,8 @@ import androidx.recyclerview.widget.RecyclerView;
*/
public class ModUpdateFragment extends Fragment {
private FragmentModUpdateListBinding binding;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
@ -35,23 +37,20 @@ public class ModUpdateFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_mod_update_list, container, false);
binding = FragmentModUpdateListBinding.inflate(inflater, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
Context context = binding.getRoot().getContext();
RecyclerView recyclerView = binding.getRoot();
recyclerView.setLayoutManager(new LinearLayoutManager(context));
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
String updateInfoListJson = ModUpdateFragmentArgs.fromBundle(arguments).getUpdateInfoListJson();
List<ModUpdateCheckResponseDto> updateInfos = JsonUtil.fromJson(updateInfoListJson, new TypeReference<List<ModUpdateCheckResponseDto>>() {
});
ModUpdateAdapter adapter = new ModUpdateAdapter(updateInfos);
recyclerView.setAdapter(adapter);
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
String updateInfoListJson = ModUpdateFragmentArgs.fromBundle(arguments).getUpdateInfoListJson();
List<ModUpdateCheckResponseDto> updateInfos = JsonUtil.fromJson(updateInfoListJson, new TypeReference<List<ModUpdateCheckResponseDto>>() {
});
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
}
return view;
ModUpdateAdapter adapter = new ModUpdateAdapter(updateInfos);
recyclerView.setAdapter(adapter);
});
recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL));
return binding.getRoot();
}
}

View File

@ -8,10 +8,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.hjq.language.LanguagesManager;
import com.microsoft.appcenter.crashes.Crashes;
import com.smart.library.util.bspatch.BSPatchUtil;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang3.StringUtils;
@ -313,27 +310,4 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
}
return null;
}
public static byte[] patchFile(byte[] originBytes, byte[] patchBytes) {
File patch = null;
File origin = null;
File patched = null;
try {
patch = File.createTempFile("patch", null);
Files.write(patchBytes, patch);
origin = File.createTempFile("origin", null);
Files.write(originBytes, origin);
patched = File.createTempFile("patched", null);
if (BSPatchUtil.bspatch(origin.getAbsolutePath(), patched.getAbsolutePath(), patch.getAbsolutePath()) == 0) {
return Files.asByteSource(patched).read();
}
} catch (Exception e) {
Crashes.trackError(e);
} finally {
FileUtils.deleteQuietly(patch);
FileUtils.deleteQuietly(origin);
FileUtils.deleteQuietly(patched);
}
return null;
}
}

View File

@ -0,0 +1,35 @@
package com.zane.smapiinstaller.utils.function;
import android.text.Editable;
/**
* @author Zane
*/
@FunctionalInterface
public interface TextChangedWatcher extends android.text.TextWatcher {
/**
* Do nothing
* @param s origin string
* @param start modify position
* @param count modify count
* @param after modified string
*/
@Override
default void beforeTextChanged(CharSequence s, int start, int count, int after) {}
/**
* Text changed event
* @param s modified string
* @param start modify position
* @param count modify count
*/
@Override
void onTextChanged(CharSequence s, int start, int before, int count);
/**
* Do nothing
* @param s target view
*/
@Override
default void afterTextChanged(Editable s) {}
}

View File

@ -10,6 +10,7 @@
<include
layout="@layout/app_bar_main"
android:id="@+id/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -61,7 +61,7 @@
<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.7.3.2</string>
<string name="smapi_version">Versión SMAPI: 3.7.3.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>

View File

@ -61,7 +61,7 @@
<string name="settings_verbose_logging">Journalisation détaillée</string>
<string name="signing_package">Signature</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">Version SMAPI: 3.7.3.2</string>
<string name="smapi_version">Version SMAPI: 3.7.3.3</string>
<string name="text_install_tip1">Remarques: La version du jeu 1.4.5.138 ou ultérieure est requise.</string>
<string name="text_install_tip2">Le jeu de base est requis lors de la mise à jour / installation.</string>
<string name="unpacking_smapi_files">Déballage</string>

View File

@ -61,7 +61,7 @@
<string name="settings_verbose_logging">Catatan Terperinci</string>
<string name="signing_package">Menandatangani</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">Versi SMAPI: 3.7.3.2</string>
<string name="smapi_version">Versi SMAPI: 3.7.3.3</string>
<string name="text_install_tip1">Catatan: Dibutuhkan Stardew Valley versi 1.4.5.138 atau yang lebih baru.</string>
<string name="text_install_tip2">Permainan dasar diperlukan saat memperbarui/menginstal.</string>
<string name="unpacking_smapi_files">Membongkar</string>

View File

@ -61,7 +61,7 @@
<string name="settings_verbose_logging">자세한 로그</string>
<string name="signing_package">설치 패키지 서명</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">SMAPI버전: 3.7.3.2</string>
<string name="smapi_version">SMAPI버전: 3.7.3.3</string>
<string name="text_install_tip1">참고 : 게임 버전 1.4.5.138 이상이 필요합니다</string>
<string name="text_install_tip2">업데이트 또는 설치 중에 게임 본체를 설치해야합니다</string>
<string name="unpacking_smapi_files">포장 풀기</string>

View File

@ -61,7 +61,7 @@
<string name="settings_verbose_logging">Log detalhado</string>
<string name="signing_package">Assinatura</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">Versão SMAPI: 3.7.3.2</string>
<string name="smapi_version">Versão SMAPI: 3.7.3.3</string>
<string name="text_install_tip1">Notas: É necessária a versão do jogo 1.4.5.138 ou posterior.</string>
<string name="text_install_tip2">O jogo base é necessário ao atualizar / instalar.</string>
<string name="unpacking_smapi_files">Desembalar</string>

View File

@ -61,7 +61,7 @@
<string name="settings_verbose_logging">บันทึกอย่างละเอียด</string>
<string name="signing_package">กำลังลงทะเบียนแอป</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">เวอร์ชั่น SMAPI: 3.7.3.2</string>
<string name="smapi_version">เวอร์ชั่น SMAPI: 3.7.3.3</string>
<string name="text_install_tip1">หมายเหตุ: ต้องการเกมเวอร์ชั่น 1.4.5.138 หรือใหม่กว่า</string>
<string name="text_install_tip2">ต้องการเกมหลักเมื่อทำการอัปเดต / ติดตั้ง</string>
<string name="unpacking_smapi_files">กำลังแกะกล่อง</string>

View File

@ -60,7 +60,7 @@
<string name="settings_verbose_logging">詳細日誌</string>
<string name="signing_package">正在簽名安裝包</string>
<string name="smapi_game_name">SMAPI 星露谷物語</string>
<string name="smapi_version">SMAPI版本: 3.7.3.2</string>
<string name="smapi_version">SMAPI版本: 3.7.3.3</string>
<string name="text_install_tip1">注意:需要 1.4.5.138 以上遊戲版本</string>
<string name="text_install_tip2">更新或安裝期間需要安裝遊戲</string>
<string name="unpacking_smapi_files">正在解包</string>

View File

@ -60,7 +60,7 @@
<string name="settings_verbose_logging">详细日志</string>
<string name="signing_package">正在签名安装包</string>
<string name="smapi_game_name">SMAPI星露谷物语</string>
<string name="smapi_version">SMAPI版本: 3.7.3.2</string>
<string name="smapi_version">SMAPI版本: 3.7.3.3</string>
<string name="text_install_tip1">注意需要不低于1.4.5.138版本的游戏本体</string>
<string name="text_install_tip2">更新或安装期间需要安装游戏本体</string>
<string name="unpacking_smapi_files">正在解包</string>

View File

@ -60,7 +60,7 @@
<string name="settings_verbose_logging">Verbose Logging</string>
<string name="signing_package">Signing</string>
<string name="smapi_game_name">SMAPI Stardew Valley</string>
<string name="smapi_version">SMAPI Version: 3.7.3.2</string>
<string name="smapi_version">SMAPI Version: 3.7.3.3</string>
<string name="text_install_tip1">Notes: Game version 1.4.5.138 or later is required.</string>
<string name="text_install_tip2">The base game is required when updating/installing.</string>
<string name="unpacking_smapi_files">Unpacking</string>