Remove all AsyncTask usage, Resolve #631

This commit is contained in:
tarsin committed 2023-02-21 22:11:36 +08:00
1 parent cac88cbb81
commit 019174d7e2
3 files changed
+160 -393

No files matched your search

@@ -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
@@ -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<RestoreItem> = 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<RestoreItem>
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<GalleryInfo>? {
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
}
}
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
@@ -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<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@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<Void, Void, Object> {
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
)
}
}