From 019174d7e21f6d7380b414ff0dbd6526d3d97437 Mon Sep 17 00:00:00 2001 From: tarsin Date: Tue, 21 Feb 2023 22:11:16 +0800 Subject: [PATCH] Remove all AsyncTask usage, Resolve #631 --- .../preference/CleanRedundancyPreference.kt | 136 ++++------- .../preference/RestoreDownloadPreference.kt | 202 +++++++--------- .../ehviewer/preference/TaskPreference.kt | 215 +++--------------- 3 files changed, 160 insertions(+), 393 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.kt b/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.kt index ab2bdec0f..75bbb2d08 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.kt +++ b/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.kt @@ -13,106 +13,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.hippo.ehviewer.preference -package com.hippo.ehviewer.preference; +import android.content.Context +import android.util.AttributeSet +import com.hippo.ehviewer.EhApplication.Companion.downloadManager +import com.hippo.ehviewer.GetText +import com.hippo.ehviewer.R +import com.hippo.ehviewer.Settings +import com.hippo.unifile.UniFile +import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withUIContext +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch -import android.content.Context; -import android.util.AttributeSet; +private val NO_REDUNDANCY = + GetText.getString(R.string.settings_download_clean_redundancy_no_redundancy) +private val CLEAR_REDUNDANCY_DONE = + { cnt: Int -> GetText.getString(R.string.settings_download_clean_redundancy_done, cnt) } +private val FINAL_CLEAR_REDUNDANCY_MSG = + { cnt: Int -> if (cnt == 0) NO_REDUNDANCY else CLEAR_REDUNDANCY_DONE(cnt) } -import androidx.annotation.NonNull; - -import com.google.android.material.snackbar.Snackbar; -import com.hippo.ehviewer.EhApplication; -import com.hippo.ehviewer.R; -import com.hippo.ehviewer.Settings; -import com.hippo.ehviewer.download.DownloadManager; -import com.hippo.unifile.UniFile; -import com.hippo.yorozuya.NumberUtils; - -public class CleanRedundancyPreference extends TaskPreference { - - public CleanRedundancyPreference(Context context) { - super(context); - } - - public CleanRedundancyPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CleanRedundancyPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @NonNull - @Override - protected Task onCreateTask() { - return new ClearTask(getContext()); - } - - private static class ClearTask extends Task { - - private final DownloadManager mManager; - - public ClearTask(@NonNull Context context) { - super(context); - mManager = EhApplication.getDownloadManager(); +class CleanRedundancyPreference @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : TaskPreference(context, attrs) { + private fun clearFile(file: UniFile): Boolean { + var name = file.name ?: return false + val index = name.indexOf('-') + if (index >= 0) { + name = name.substring(0, index) } - - // True for cleared - private boolean clearFile(UniFile file) { - String name = file.getName(); - if (name == null) { - return false; - } - int index = name.indexOf('-'); - if (index >= 0) { - name = name.substring(0, index); - } - long gid = NumberUtils.parseLongSafely(name, -1L); - if (-1L == gid) { - return false; - } - if (mManager.containDownloadInfo(gid)) { - return false; - } - file.delete(); - return true; + val gid = name.toLongOrNull() ?: return false + if (downloadManager.containDownloadInfo(gid)) { + return false } + file.delete() + return true + } - @Override - protected Object doInBackground(Void... params) { - UniFile dir = Settings.getDownloadLocation(); - if (null == dir) { - return 0; - } - UniFile[] files = dir.listFiles(); - if (null == files) { - return 0; - } + private fun doRealWork(): Int { + return Settings.getDownloadLocation()?.listFiles()?.sumOf { clearFile(it).compareTo(false) } ?: 0 + } - int count = 0; - for (UniFile f : files) { - if (clearFile(f)) { - ++count; - } + override fun launchJob() { + if (singletonJob?.isActive == true) singletonJob?.invokeOnCompletion { + launchUI { + dialog.dismiss() } - - return count; } - - @Override - protected void onPostExecute(Object o) { - int count; - if (o instanceof Integer) { - count = (Integer) o; - } else { - count = 0; + else singletonJob = launch { + val cnt = doRealWork() + withUIContext { + showTip(FINAL_CLEAR_REDUNDANCY_MSG(cnt)) + dialog.dismiss() } - - showTip(0 == count ? - mApplication.getString(R.string.settings_download_clean_redundancy_no_redundancy) : - mApplication.getString(R.string.settings_download_clean_redundancy_done, count), Snackbar.LENGTH_SHORT); - super.onPostExecute(o); } } } + +private var singletonJob: Job? = null diff --git a/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.kt b/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.kt index 372577b14..d15da6e0c 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.kt +++ b/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.kt @@ -15,145 +15,111 @@ */ package com.hippo.ehviewer.preference -import android.app.Activity import android.content.Context import android.util.AttributeSet -import androidx.preference.Preference import com.hippo.ehviewer.EhApplication.Companion.downloadManager import com.hippo.ehviewer.EhDB +import com.hippo.ehviewer.GetText import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhEngine.fillGalleryListByApi import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.data.BaseGalleryInfo +import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.spider.SpiderQueen import com.hippo.ehviewer.spider.readCompatFromUniFile -import com.hippo.ehviewer.ui.scene.BaseScene import com.hippo.unifile.UniFile -import com.hippo.util.ExceptionUtils -import kotlinx.coroutines.runBlocking -import java.io.IOException -import java.util.Collections +import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withUIContext +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import moe.tarsin.coroutines.runSuspendCatching -class RestoreDownloadPreference constructor( - context: Context, attrs: AttributeSet? +private val RESTORE_NOT_FOUND = GetText.getString(R.string.settings_download_restore_not_found) +private val RESTORE_FAILED = GetText.getString(R.string.settings_download_restore_failed) +private val RESTORE_COUNT_MSG = + { cnt: Int -> if (cnt == 0) RESTORE_NOT_FOUND else GetText.getString(R.string.settings_download_restore_successfully, cnt) } + +class RestoreDownloadPreference @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null ) : TaskPreference(context, attrs) { - override fun onCreateTask(): Task { - return RestoreTask(context) - } - - private class RestoreTask(context: Context) : Task(context) { - private val mManager: DownloadManager = downloadManager - private var restoreDirCount = 0 - private fun getRestoreItem(file: UniFile?): RestoreItem? { - if (null == file || !file.isDirectory) { + private val mManager: DownloadManager = downloadManager + private var restoreDirCount = 0 + private fun getRestoreItem(file: UniFile?): RestoreItem? { + if (null == file || !file.isDirectory) return null + val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null + return runCatching { + val spiderInfo = readCompatFromUniFile(siFile) ?: return null + var gid = spiderInfo.gid + var dirname = file.name + if (mManager.containDownloadInfo(gid)) { + // Restore download dir to avoid redownload + val dbdirname = EhDB.getDownloadDirname(gid) + if (null == dbdirname || dirname != dbdirname) { + EhDB.putDownloadDirname(gid, dirname) + restoreDirCount++ + } return null } - val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null - return try { - val spiderInfo = readCompatFromUniFile(siFile) ?: return null - val gid = spiderInfo.gid - val dirname = file.name - if (mManager.containDownloadInfo(gid)) { - // Restore download dir to avoid redownload - val dbdirname = EhDB.getDownloadDirname(gid) - if (null == dbdirname || dirname != dbdirname) { - EhDB.putDownloadDirname(gid, dirname) - restoreDirCount++ - } - return null - } - val token = spiderInfo.token - val restoreItem = RestoreItem() - restoreItem.gid = gid - restoreItem.token = token - restoreItem.dirname = dirname - restoreItem - } catch (e: IOException) { - null + RestoreItem().apply { + gid = spiderInfo.gid + token = spiderInfo.token + dirname = file.name } - } + }.getOrNull() + } - override fun doInBackground(vararg params: Void): Any? { - val dir = Settings.getDownloadLocation() ?: return null - val restoreItemList: MutableList = ArrayList() - val files = dir.listFiles() ?: return null - for (file in files) { - val restoreItem = getRestoreItem(file) - if (null != restoreItem) { - restoreItemList.add(restoreItem) - } - } - return if (0 == restoreItemList.size) { - Collections.EMPTY_LIST - } else try { - runBlocking { - fillGalleryListByApi( - ArrayList(restoreItemList), - EhUrl.referer - ) - } - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - e.printStackTrace() - null - } - } - - override fun onPostExecute(o: Any) { - if (o !is List<*>) { - mActivity.showTip(R.string.settings_download_restore_failed, BaseScene.LENGTH_SHORT) - } else { - val list = o as List - if (list.isEmpty()) { - mActivity.showTip( - if (restoreDirCount == 0) - mActivity.getString(R.string.settings_download_restore_not_found) - else - mActivity.getString( - R.string.settings_download_restore_successfully, - restoreDirCount - ), - BaseScene.LENGTH_SHORT - ) - } else { - var count = 0 - var i = 0 - val n = list.size - while (i < n) { - val item = list[i] - // Avoid failed gallery info - if (null != item.title) { - // Put to download - mManager.addDownload(item, null) - // Put download dir to DB - EhDB.putDownloadDirname(item.gid, item.dirname) - count++ - } - i++ - } - showTip( - mActivity.getString( - R.string.settings_download_restore_successfully, - count + restoreDirCount - ), - BaseScene.LENGTH_SHORT - ) - val preference: Preference? = preference - if (null != preference) { - val context = preference.context - if (context is Activity) { - context.setResult(Activity.RESULT_OK) - } - } - } - } - super.onPostExecute(o) - } + private suspend fun doRealWork(): List? { + val dir = Settings.getDownloadLocation() ?: return null + val files = dir.listFiles() ?: return null + val restoreItemList = files.mapNotNull { getRestoreItem(it) } + return runSuspendCatching { + fillGalleryListByApi(restoreItemList, EhUrl.referer) + }.getOrNull() } private class RestoreItem : BaseGalleryInfo() { var dirname: String? = null } -} \ No newline at end of file + + override fun launchJob() { + if (singletonJob?.isActive == true) singletonJob?.invokeOnCompletion { + launchUI { + dialog.dismiss() + } + } + else singletonJob = launch { + val result = doRealWork() + withUIContext { + if (result == null) { + showTip(RESTORE_FAILED) + } else { + if (result.isEmpty()) { + showTip(RESTORE_COUNT_MSG(restoreDirCount)) + } else { + var count = 0 + var i = 0 + val n = result.size + while (i < n) { + val item = result[i] + // Avoid failed gallery info + if (null != item.title) { + // Put to download + mManager.addDownload(item, null) + // Put download dir to DB + EhDB.putDownloadDirname(item.gid, (item as RestoreItem).dirname) + count++ + } + i++ + } + showTip(RESTORE_COUNT_MSG(count + restoreDirCount)) + } + } + dialog.dismiss() + } + } + } +} + +private var singletonJob: Job? = null diff --git a/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.kt b/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.kt index d6e5a5483..239e24037 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.kt +++ b/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.kt @@ -13,196 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.hippo.ehviewer.preference -package com.hippo.ehviewer.preference; +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.app.AlertDialog +import com.google.android.material.snackbar.Snackbar +import com.hippo.ehviewer.R +import com.hippo.ehviewer.ui.SettingsActivity +import com.hippo.preference.DialogPreference +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.content.Context; -import android.os.AsyncTask; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.widget.Toast; - -import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.app.AlertDialog; -import androidx.customview.view.AbsSavedState; - -import com.hippo.ehviewer.EhApplication; -import com.hippo.ehviewer.R; -import com.hippo.ehviewer.ui.SettingsActivity; -import com.hippo.preference.DialogPreference; -import com.hippo.util.IoThreadPoolExecutor; -import com.hippo.yorozuya.IntIdGenerator; - -public abstract class TaskPreference extends DialogPreference { - - private Task mTask; - private int mTaskId = IntIdGenerator.INVALID_ID; - - public TaskPreference(Context context) { - super(context); +abstract class TaskPreference @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : DialogPreference(context, attrs), CoroutineScope { + override val coroutineContext = Dispatchers.IO + Job() + protected lateinit var dialog: AlertDialog + override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { + builder.setTitle(null) + builder.setView(R.layout.preference_dialog_task) + builder.setCancelable(false) } - public TaskPreference(Context context, AttributeSet attrs) { - super(context, attrs); + abstract fun launchJob() + + override fun onDialogCreated(dialog: AlertDialog) { + this.dialog = dialog + launchJob() } - public TaskPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { - builder.setTitle(null); - builder.setView(R.layout.preference_dialog_task); - builder.setCancelable(false); - } - - @Override - protected void onDialogCreated(AlertDialog dialog) { - if (null == mTask) { - mTask = onCreateTask(); - mTask.setPreference(this); - mTaskId = ((EhApplication) getContext().getApplicationContext()).putGlobalStuff(mTask); - mTask.executeOnExecutor(IoThreadPoolExecutor.getInstance()); - } - } - - protected void onTaskEnd() { - // Dismiss dialog - Dialog dialog = getDialog(); - try { - dialog.dismiss(); - } catch (Exception e) { - e.printStackTrace(); - } - // Clear async - mTask = null; - mTaskId = IntIdGenerator.INVALID_ID; - } - - @NonNull - protected abstract Task onCreateTask(); - - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - final SavedState myState = new SavedState(superState); - myState.asyncTaskId = mTaskId; - return myState; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - if (state == null || !state.getClass().equals(SavedState.class)) { - // Didn't save state for us in onSaveInstanceState - super.onRestoreInstanceState(state); - return; - } - - SavedState myState = (SavedState) state; - mTaskId = myState.asyncTaskId; - if (IntIdGenerator.INVALID_ID != mTaskId) { - Object o = ((EhApplication) getContext().getApplicationContext()).getGlobalStuff(mTaskId); - if (o instanceof Task) { - mTask = (Task) o; - mTask.setPreference(this); - } - } - if (null == mTask) { - mTaskId = IntIdGenerator.INVALID_ID; - } - - // TODO if not task, show not reopen dialog - - super.onRestoreInstanceState(myState.getSuperState()); - } - - private static class SavedState extends AbsSavedState { - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - int asyncTaskId; - - public SavedState(Parcel source) { - super(source, SavedState.class.getClassLoader()); - asyncTaskId = source.readInt(); - } - - public SavedState(Parcelable superState) { - super(superState); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(asyncTaskId); - } - } - - public abstract static class Task extends AsyncTask { - - protected final EhApplication mApplication; - @SuppressLint("StaticFieldLeak") - protected final SettingsActivity mActivity; - @Nullable - private TaskPreference mPreference; - - public Task(@NonNull Context context) { - mActivity = (SettingsActivity) context; - mApplication = (EhApplication) context.getApplicationContext(); - } - - public EhApplication getApplication() { - return mApplication; - } - - @Nullable - public TaskPreference getPreference() { - return mPreference; - } - - public void setPreference(@Nullable TaskPreference preference) { - mPreference = preference; - } - - @CallSuper - @Override - protected void onPostExecute(Object o) { - mApplication.removeGlobalStuff(this); - if (null != mPreference) { - mPreference.onTaskEnd(); - } - } - - protected void showTip(@StringRes int id, int length) { - try { - mActivity.showTip(id, length); - } catch (Exception e) { - Toast.makeText(mApplication, id, length).show(); - } - } - - protected void showTip(CharSequence message, int length) { - try { - mActivity.showTip(message, length); - } catch (Exception e) { - Toast.makeText(mApplication, message, length).show(); - } - } + protected fun showTip(msg: String) { + (context as SettingsActivity).showTip( + msg, + Snackbar.LENGTH_SHORT + ) } }