Migrate to coroutines from AsyncTask for saving of custom artist images

This commit is contained in:
Prathamesh More 2022-05-05 10:48:21 +05:30
parent 612b492aee
commit ee300722af
4 changed files with 80 additions and 186 deletions

View file

@ -173,7 +173,7 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
private fun loadBiography(
name: String,
lang: String? = Locale.getDefault().language
lang: String? = Locale.getDefault().language,
) {
biography = null
this.lang = lang
@ -274,12 +274,16 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
R.id.action_set_artist_image -> {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
selectImageLauncher.launch(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)))
selectImageLauncher.launch(Intent.createChooser(intent,
getString(R.string.pick_from_local_storage)))
return true
}
R.id.action_reset_artist_image -> {
showToast(resources.getString(R.string.updating))
CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist)
lifecycleScope.launch {
CustomArtistImageUtil.getInstance(requireContext())
.resetCustomArtistImage(artist)
}
forceDownload = true
return true
}
@ -335,12 +339,16 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
}
}
private val selectImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {result->
private val selectImageLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let {
lifecycleScope.launch {
CustomArtistImageUtil.getInstance(requireContext())
.setCustomArtistImage(artist, it)
}
}
}
}

View file

@ -1,104 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.misc;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
public abstract class DialogAsyncTask<Params, Progress, Result>
extends WeakContextAsyncTask<Params, Progress, Result> {
private final int delay;
private WeakReference<Dialog> dialogWeakReference;
private boolean supposedToBeDismissed;
public DialogAsyncTask(Context context) {
this(context, 0);
}
public DialogAsyncTask(Context context, int showDelay) {
super(context);
this.delay = showDelay;
dialogWeakReference = new WeakReference<>(null);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (delay > 0) {
new Handler().postDelayed(this::initAndShowDialog, delay);
} else {
initAndShowDialog();
}
}
private void initAndShowDialog() {
Context context = getContext();
if (!supposedToBeDismissed && context != null) {
Dialog dialog = createDialog(context);
dialogWeakReference = new WeakReference<>(dialog);
dialog.show();
}
}
@SuppressWarnings("unchecked")
@Override
protected void onProgressUpdate(Progress... values) {
super.onProgressUpdate(values);
Dialog dialog = getDialog();
if (dialog != null) {
onProgressUpdate(dialog, values);
}
}
@SuppressWarnings("unchecked")
protected void onProgressUpdate(@NonNull Dialog dialog, Progress... values) {}
@Nullable
protected Dialog getDialog() {
return dialogWeakReference.get();
}
@Override
protected void onCancelled(Result result) {
super.onCancelled(result);
tryToDismiss();
}
@Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
tryToDismiss();
}
private void tryToDismiss() {
supposedToBeDismissed = true;
try {
Dialog dialog = getDialog();
if (dialog != null) dialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}
protected abstract Dialog createDialog(@NonNull Context context);
}

View file

@ -1 +0,0 @@
/* * Copyright (c) 2019 Hemanth Savarala. * * Licensed under the GNU General Public License v3 * * This is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by * the Free Software Foundation either version 3 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. */ package code.name.monkey.retromusic.misc import android.content.Context import android.os.AsyncTask import java.lang.ref.WeakReference abstract class WeakContextAsyncTask<Params, Progress, Result>(context: Context) : AsyncTask<Params, Progress, Result>() { private val contextWeakReference: WeakReference<Context> = WeakReference(context) protected val context: Context? get() = contextWeakReference.get() }

View file

@ -14,23 +14,20 @@
package code.name.monkey.retromusic.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.AsyncTask
import android.provider.MediaStore
import android.widget.Toast
import androidx.core.content.edit
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.model.Artist
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException
import java.util.*
@ -43,62 +40,60 @@ class CustomArtistImageUtil private constructor(context: Context) {
Context.MODE_PRIVATE
)
fun setCustomArtistImage(artist: Artist, uri: Uri) {
Glide.with(App.getContext())
suspend fun setCustomArtistImage(artist: Artist, uri: Uri) {
val context = App.getContext()
withContext(IO) {
runCatching {
GlideApp.with(context)
.asBitmap()
.load(uri)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(object : SimpleTarget<Bitmap>() {
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
App.getContext().showToast("Load Failed")
.submit()
.get()
}
.onSuccess {
saveImage(context, artist, it)
}
.onFailure {
context.showToast("Load Failed")
}
}
}
@SuppressLint("StaticFieldLeak")
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
object : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void): Void? {
val dir = File(App.getContext().filesDir, FOLDER_NAME)
private fun saveImage(context: Context, artist: Artist, bitmap: Bitmap) {
val dir = File(context.filesDir, FOLDER_NAME)
if (!dir.exists()) {
if (!dir.mkdirs()) { // create the folder
return null
return
}
}
val file = File(dir, getFileName(artist))
var succesful = false
var successful = false
try {
file.outputStream().buffered().use { bos ->
succesful = ImageUtil.resizeBitmap(resource, 2048)
successful = ImageUtil.resizeBitmap(bitmap, 2048)
.compress(Bitmap.CompressFormat.JPEG, 100, bos)
}
} catch (e: IOException) {
App.getContext().showToast(e.toString(), Toast.LENGTH_LONG)
context.showToast(e.toString(), Toast.LENGTH_LONG)
}
if (succesful) {
if (successful) {
mPreferences.edit { putBoolean(getFileName(artist), true) }
ArtistSignatureUtil.getInstance(App.getContext())
ArtistSignatureUtil.getInstance(context)
.updateArtistSignature(artist.name)
App.getContext().contentResolver.notifyChange(
context.contentResolver.notifyChange(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
null
) // trigger media store changed to force artist image reload
}
return null
}
}.execute()
}
})
}
@SuppressLint("StaticFieldLeak")
fun resetCustomArtistImage(artist: Artist) {
object : AsyncTask<Void, Void, Void>() {
@SuppressLint("ApplySharedPref")
override fun doInBackground(vararg params: Void): Void? {
mPreferences.edit(commit = true) { putBoolean(getFileName(artist), false) }
suspend fun resetCustomArtistImage(artist: Artist) {
withContext(IO) {
mPreferences.edit { putBoolean(getFileName(artist), false) }
ArtistSignatureUtil.getInstance(App.getContext()).updateArtistSignature(artist.name)
App.getContext().contentResolver.notifyChange(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
@ -106,14 +101,10 @@ class CustomArtistImageUtil private constructor(context: Context) {
) // trigger media store changed to force artist image reload
val file = getFile(artist)
if (!file.exists()) {
return null
} else {
if (file.exists()) {
file.delete()
}
return null
}
}.execute()
}
// shared prefs saves us many IO operations