diff --git a/app/build.gradle b/app/build.gradle index 3a3e91c..efb1374 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "com.zane.smapiinstaller" minSdkVersion 19 targetSdkVersion 28 - versionCode 4 - versionName "1.1.1" + versionCode 5 + versionName "1.1.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/app/src/main/assets/downloadable_content_list.json b/app/src/main/assets/downloadable_content_list.json index 3fe36b6..83ad05e 100644 --- a/app/src/main/assets/downloadable_content_list.json +++ b/app/src/main/assets/downloadable_content_list.json @@ -1,19 +1,19 @@ { - "version": 1, + "version": 2, "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", - "url": "http://dl.zaneyork.cn/compat/smapi_137.zip", + "url": "http://zaneyork.cn/download/compat/smapi_137.zip", "hash": "bd16e8e4cb52d636e24c6a2d2309b66a60e492d2b97c1b8f6a519c04ac42ebdc" }, { "type": "LOCALE", "name": "中文汉化v2.4", "description": "简体中文语言包,感谢Wabi-Sabi提供", - "url": "http://dl.zaneyork.cn/locale/locale_pack_zh_2.4.zip", + "url": "http://zaneyork.cn/download/locale/locale_pack_zh_2.4.zip", "hash": "59be19240160de61046f05619f3e448d180137e0989feb537e392a014588615f" } ] diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java index 56cb3e6..b9f36b1 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/CommonLogic.java @@ -38,6 +38,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import pxb.android.axml.AxmlReader; import pxb.android.axml.AxmlVisitor; @@ -59,7 +60,7 @@ public class CommonLogic { public static InputStream getLocalAsset(Context context, String filename) throws IOException { File file = new File(context.getFilesDir(), filename); - if(file.exists()){ + if (file.exists()) { return new FileInputStream(file); } return context.getAssets().open(filename); @@ -94,8 +95,7 @@ public class CommonLogic { FileOutputStream outputStream = new FileOutputStream(file); try (OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) { writer.write(new Gson().toJson(content)); - } - finally { + } finally { FileUtils.moveFile(file, new File(context.getFilesDir(), filename)); } } catch (Exception ignored) { @@ -136,8 +136,8 @@ public class CommonLogic { public static void setProgressDialogState(View view, MaterialDialog dialog, int message, int progress) { Activity activity = getActivityFromView(view); - if(activity != null && !activity.isFinishing() && !dialog.isCancelled()) { - activity.runOnUiThread(()->{ + if (activity != null && !activity.isFinishing() && !dialog.isCancelled()) { + activity.runOnUiThread(() -> { dialog.incrementProgress(progress - dialog.getCurrentProgress()); dialog.setContent(message); }); @@ -159,62 +159,80 @@ public class CommonLogic { public static void showAlertDialog(View view, int title, String message) { Activity activity = getActivityFromView(view); - if(activity != null && !activity.isFinishing()) { - activity.runOnUiThread(()-> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.ok).show()); + if (activity != null && !activity.isFinishing()) { + activity.runOnUiThread(() -> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.ok).show()); } } + public static void showAlertDialog(View view, int title, int message) { Activity activity = getActivityFromView(view); - if(activity != null && !activity.isFinishing()) { - activity.runOnUiThread(()-> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.ok).show()); + if (activity != null && !activity.isFinishing()) { + activity.runOnUiThread(() -> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.ok).show()); } } public static void showConfirmDialog(View view, int title, int message, MaterialDialog.SingleButtonCallback callback) { Activity activity = getActivityFromView(view); - if(activity != null && !activity.isFinishing()) { - activity.runOnUiThread(()-> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.confirm).negativeText(R.string.cancel).onAny(callback).show()); + if (activity != null && !activity.isFinishing()) { + activity.runOnUiThread(() -> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.confirm).negativeText(R.string.cancel).onAny(callback).show()); } } public static void showConfirmDialog(View view, int title, String message, MaterialDialog.SingleButtonCallback callback) { Activity activity = getActivityFromView(view); - if(activity != null && !activity.isFinishing()) { - activity.runOnUiThread(()-> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.confirm).negativeText(R.string.cancel).onAny(callback).show()); + if (activity != null && !activity.isFinishing()) { + activity.runOnUiThread(() -> new MaterialDialog.Builder(activity).title(title).content(message).positiveText(R.string.confirm).negativeText(R.string.cancel).onAny(callback).show()); } } + public static AtomicReference showProgressDialog(View view, int title, String message) { + Activity activity = getActivityFromView(view); + AtomicReference reference = new AtomicReference<>(); + if (activity != null && !activity.isFinishing()) { + activity.runOnUiThread(() -> { + MaterialDialog dialog = new MaterialDialog.Builder(activity) + .title(title) + .content(message) + .progress(false, 100, true) + .cancelable(false) + .show(); + reference.set(dialog); + }); + } + return reference; + } + public static List findAllApkFileManifest(Context context) { ApkFilesManifest apkFilesManifest = CommonLogic.getAssetJson(context, "apk_files_manifest.json", ApkFilesManifest.class); ArrayList apkFilesManifests = Lists.newArrayList(apkFilesManifest); File compatFolder = new File(context.getFilesDir(), "compat"); - if(compatFolder.exists()) { + if (compatFolder.exists()) { for (File directory : compatFolder.listFiles(File::isDirectory)) { File manifestFile = new File(directory, "apk_files_manifest.json"); - if(manifestFile.exists()) { + if (manifestFile.exists()) { ApkFilesManifest manifest = getFileJson(manifestFile, ApkFilesManifest.class); - if(manifest != null) { + if (manifest != null) { apkFilesManifests.add(manifest); } } } } - Collections.sort(apkFilesManifests, (a, b)-> Long.compare(b.getMinBuildCode(), a.getMinBuildCode())); + Collections.sort(apkFilesManifests, (a, b) -> Long.compare(b.getMinBuildCode(), a.getMinBuildCode())); return apkFilesManifests; } public static boolean unpackSmapiFiles(Context context, String apkPath, boolean checkMod) { List manifestEntries = CommonLogic.getAssetJson(context, "smapi_files_manifest.json", new TypeToken>() { }.getType()); - if(manifestEntries == null) + if (manifestEntries == null) return false; File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/"); - if(!basePath.exists()) { - if(!basePath.mkdir()) { + if (!basePath.exists()) { + if (!basePath.mkdir()) { return false; } } - File noMedia = new File(basePath,".nomedia"); + File noMedia = new File(basePath, ".nomedia"); if (!noMedia.exists()) { try { noMedia.createNewFile(); @@ -225,7 +243,7 @@ public class CommonLogic { File targetFile = new File(basePath, entry.getTargetPath()); switch (entry.getOrigin()) { case 0: - if(!checkMod || !targetFile.exists()) { + if (!checkMod || !targetFile.exists()) { try (InputStream inputStream = context.getAssets().open(entry.getAssetPath())) { if (!targetFile.getParentFile().exists()) { if (!targetFile.getParentFile().mkdirs()) { @@ -241,7 +259,7 @@ public class CommonLogic { } break; case 1: - if(!checkMod || !targetFile.exists()) { + if (!checkMod || !targetFile.exists()) { ZipUtil.unpackEntry(new File(apkPath), entry.getAssetPath(), targetFile); } break; @@ -275,7 +293,7 @@ public class CommonLogic { } public static String getFileHash(Context context, String filename) { - try(InputStream inputStream = getLocalAsset(context, filename)){ + try (InputStream inputStream = getLocalAsset(context, filename)) { return Hashing.sha256().hashBytes(ByteStreams.toByteArray(inputStream)).toString(); } catch (IOException ignored) { } @@ -283,7 +301,7 @@ public class CommonLogic { } public static String getFileHash(File file) { - try(InputStream inputStream = new FileInputStream(file)){ + try (InputStream inputStream = new FileInputStream(file)) { return Hashing.sha256().hashBytes(ByteStreams.toByteArray(inputStream)).toString(); } catch (IOException ignored) { } diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java b/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java new file mode 100644 index 0000000..062cda8 --- /dev/null +++ b/app/src/main/java/com/zane/smapiinstaller/ui/about/AboutFragment.java @@ -0,0 +1,65 @@ +package com.zane.smapiinstaller.ui.about; + +import android.os.Bundle; + +import androidx.fragment.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.zane.smapiinstaller.R; + +/** + * A simple {@link Fragment} subclass. + * Use the {@link AboutFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class AboutFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + public AboutFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment AboutFragment. + */ + // TODO: Rename and change types and number of parameters + public static AboutFragment newInstance(String param1, String param2) { + AboutFragment fragment = new AboutFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_about, container, false); + } +} diff --git a/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java index 9315e6a..f72bf4f 100644 --- a/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java +++ b/app/src/main/java/com/zane/smapiinstaller/ui/download/DownloadableContentAdapter.java @@ -17,6 +17,7 @@ import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.lzy.okgo.OkGo; import com.lzy.okgo.callback.FileCallback; +import com.lzy.okgo.model.Progress; import com.lzy.okgo.model.Response; import com.zane.smapiinstaller.R; import com.zane.smapiinstaller.entity.DownloadableContent; @@ -31,6 +32,8 @@ import org.zeroturnaround.zip.commons.FileUtils; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; /** * {@link RecyclerView.Adapter} that can display a {@link DownloadableContent} @@ -71,6 +74,7 @@ public class DownloadableContentAdapter extends RecyclerView.Adapter dialogRef = CommonLogic.showProgressDialog(itemView, R.string.progress, ""); OkGo.get(downloadableContent.getUrl()).execute(new FileCallback(file.getParentFile().getAbsolutePath(), file.getName()) { @Override public void onError(Response response) { super.onError(response); + MaterialDialog dialog = dialogRef.get(); + if (dialog != null && !dialog.isCancelled()) { + dialog.dismiss(); + } + downloading.set(false); CommonLogic.showAlertDialog(itemView, R.string.error, R.string.error_failed_to_download); } + @Override + public void downloadProgress(Progress progress) { + super.downloadProgress(progress); + MaterialDialog dialog = dialogRef.get(); + if (dialog != null && !dialog.isCancelled()) { + dialog.setContent(R.string.downloading, progress.currentSize / 1024, progress.totalSize / 1024); + dialog.setProgress((int) (progress.currentSize / progress.totalSize)); + } + } + @Override public void onSuccess(Response response) { + MaterialDialog dialog = dialogRef.get(); + if (dialog != null && !dialog.isCancelled()) { + dialog.dismiss(); + } + downloading.set(false); File downloadedFile = response.body(); String hash = CommonLogic.getFileHash(downloadedFile); if (!StringUtils.equalsIgnoreCase(hash, downloadableContent.getHash())) { diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 0000000..d6d36c4 --- /dev/null +++ b/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 02732da..cc66cf2 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -20,5 +20,9 @@ android:id="@+id/nav_help" android:icon="@android:drawable/ic_menu_help" android:title="@string/menu_help" /> + diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 929d919..3367a8a 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -44,4 +44,9 @@ android:name="com.zane.smapiinstaller.ui.download.DownloadableContentFragment" android:label="@string/menu_download" tools:layout="@layout/fragment_download_content_list" /> + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 047dd29..e3a8cba 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -49,4 +49,7 @@ 語言包 提示 已完成下載安裝 + 進度 + 正在下載: %d KB / %d KB + 關於 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 047dd29..e3a8cba 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -49,4 +49,7 @@ 語言包 提示 已完成下載安裝 + 進度 + 正在下載: %d KB / %d KB + 關於 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 58bdf87..d7c4f1b 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -49,4 +49,7 @@ 语言包 提示 已完成下载安装 + 进度 + 正在下载: %d KB / %d KB + 关于 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ef61fe..c88359a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -37,7 +37,7 @@ Compat Nexus Release - Still at test stage now, not release yet. + Still at test stage now. Logs SMAPI Stardew Valley Failed to create target file : %s @@ -49,4 +49,10 @@ Locale Pack Info Download and unpack success + Progress + Downloading: %d KB / %d KB + + + Hello blank fragment + About