Made the TagEditor more reliable.

This commit is contained in:
Karim Abou Zeid 2016-03-27 23:51:51 +02:00
commit ffa329d978
7 changed files with 324 additions and 250 deletions

View file

@ -0,0 +1,104 @@
package com.kabouzeid.gramophone.misc;
import android.app.Dialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.ref.WeakReference;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class DialogAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private final int delay;
private WeakReference<Context> contextWeakReference;
private WeakReference<Dialog> dialogWeakReference;
private boolean supposedToBeDismissed;
public DialogAsyncTask(Context context) {
this(context, 0);
}
public DialogAsyncTask(Context context, int showDelay) {
this.delay = showDelay;
contextWeakReference = new WeakReference<>(context);
dialogWeakReference = new WeakReference<>(null);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (delay > 0) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
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 Context getContext() {
return contextWeakReference.get();
}
@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

@ -0,0 +1,59 @@
package com.kabouzeid.gramophone.misc;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
import java.lang.ref.WeakReference;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class UpdateToastMediaScannerCompletionListener implements MediaScannerConnection.OnScanCompletedListener {
int scanned = 0;
int failed = 0;
private final String[] toBeScanned;
private final String scannedFiles;
private final String couldNotScanFiles;
private final WeakReference<Toast> toastWeakReference;
private final WeakReference<Activity> activityWeakReference;
@SuppressLint("ShowToast")
public UpdateToastMediaScannerCompletionListener(Activity activity, String[] toBeScanned) {
this.toBeScanned = toBeScanned;
scannedFiles = activity.getString(R.string.scanned_files);
couldNotScanFiles = activity.getString(R.string.could_not_scan_files);
toastWeakReference = new WeakReference<>(Toast.makeText(activity, "", Toast.LENGTH_SHORT));
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void onScanCompleted(final String path, final Uri uri) {
Activity activity = activityWeakReference.get();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast toast = toastWeakReference.get();
if (toast != null) {
if (uri == null) {
failed++;
} else {
scanned++;
}
String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : "");
toast.setText(text);
toast.show();
}
}
});
}
}
}

View file

@ -1,13 +1,14 @@
package com.kabouzeid.gramophone.ui.activities.tageditor; package com.kabouzeid.gramophone.ui.activities.tageditor;
import android.app.Activity;
import android.app.Dialog;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -26,7 +27,9 @@ import com.kabouzeid.appthemehelper.ThemeStore;
import com.kabouzeid.appthemehelper.util.ColorUtil; import com.kabouzeid.appthemehelper.util.ColorUtil;
import com.kabouzeid.appthemehelper.util.TintHelper; import com.kabouzeid.appthemehelper.util.TintHelper;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.misc.DialogAsyncTask;
import com.kabouzeid.gramophone.misc.SimpleObservableScrollViewCallbacks; import com.kabouzeid.gramophone.misc.SimpleObservableScrollViewCallbacks;
import com.kabouzeid.gramophone.misc.UpdateToastMediaScannerCompletionListener;
import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity; import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.Util; import com.kabouzeid.gramophone.util.Util;
@ -41,10 +44,12 @@ import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag; import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException; import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.images.Artwork; import org.jaudiotagger.tag.images.Artwork;
import org.jaudiotagger.tag.images.ArtworkFactory;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -89,7 +94,6 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
} }
}; };
private List<String> songPaths; private List<String> songPaths;
private MaterialDialog progressDialog;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -271,133 +275,145 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
setTaskDescriptionColor(paletteColorPrimary); setTaskDescriptionColor(paletteColorPrimary);
} }
protected void writeValuesToFiles(@NonNull final Map<FieldKey, String> fieldKeyValueMap) { protected void writeValuesToFiles(@NonNull final Map<FieldKey, String> fieldKeyValueMap, @Nullable final ArtworkInfo artworkInfo) {
writeValuesToFiles(fieldKeyValueMap, null, false); Util.hideSoftKeyboard(this);
new WriteTagsAsyncTask(this).execute(new WriteTagsAsyncTask.LoadingInfo(getSongPaths(), fieldKeyValueMap, artworkInfo));
} }
protected void writeValuesToFiles(@NonNull final Map<FieldKey, String> fieldKeyValueMap, @Nullable final Artwork artwork, final boolean deleteArtwork) { private static class WriteTagsAsyncTask extends DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
Util.hideSoftKeyboard(this); Context applicationContext;
final String writingFileStr = getResources().getString(R.string.writing_file_number);
progressDialog = new MaterialDialog.Builder(AbsTagEditorActivity.this) public WriteTagsAsyncTask(Context context) {
.title(R.string.saving_changes) super(context);
.cancelable(false) applicationContext = context;
.progress(true, 0) }
.build();
if (Build.VERSION.SDK_INT >= 18) @Override
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); protected String[] doInBackground(LoadingInfo... params) {
progressDialog.show(); try {
new Thread(new Runnable() { LoadingInfo info = params[0];
@Override
public void run() { Artwork artwork = null;
for (int i = 0; i < songPaths.size(); i++) { File albumArtFile = null;
String songPath = songPaths.get(i); if (info.artworkInfo != null && info.artworkInfo.artwork != null) {
final int finalI = i;
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.setContent(writingFileStr + " " + ((finalI + 1) + "/" + songPaths.size()));
}
});
try { try {
AudioFile audioFile = AudioFileIO.read(new File(songPath)); albumArtFile = MusicUtil.createAlbumArtFile(String.valueOf(info.artworkInfo.albumId)).getCanonicalFile();
info.artworkInfo.artwork.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
} catch (IOException e) {
e.printStackTrace();
}
}
int counter = 0;
boolean wroteArtwork = false;
boolean deletedArtwork = false;
for (String filePath : info.filePaths) {
publishProgress(++counter, info.filePaths.size());
try {
AudioFile audioFile = AudioFileIO.read(new File(filePath));
Tag tag = audioFile.getTagOrCreateAndSetDefault(); Tag tag = audioFile.getTagOrCreateAndSetDefault();
for (Map.Entry<FieldKey, String> entry : fieldKeyValueMap.entrySet()) {
try { if (info.fieldKeyValueMap != null) {
tag.setField(entry.getKey(), entry.getValue()); for (Map.Entry<FieldKey, String> entry : info.fieldKeyValueMap.entrySet()) {
} catch (NumberFormatException e) { try {
tag.deleteField(entry.getKey()); tag.setField(entry.getKey(), entry.getValue());
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
if (deleteArtwork) {
tag.deleteArtworkField(); if (info.artworkInfo != null) {
} else if (artwork != null) { if (info.artworkInfo.artwork == null) {
tag.deleteArtworkField(); tag.deleteArtworkField();
tag.setField(artwork); deletedArtwork = true;
} else if (artwork != null) {
tag.deleteArtworkField();
tag.setField(artwork);
wroteArtwork = true;
}
} }
audioFile.commit(); audioFile.commit();
} catch (@NonNull CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { } catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
Log.e(TAG, "Error while reading audio file.", e); e.printStackTrace();
} catch (CannotWriteException e) {
Log.e(TAG, "Error while writing audio file.", e);
} }
} }
runOnUiThread(new Runnable() {
@Override Context context = getContext();
public void run() { if (context != null) {
progressDialog.setContent(getString(R.string.rescanning_media)); if (wroteArtwork) {
MusicUtil.insertAlbumArt(context, info.artworkInfo.albumId, albumArtFile.getPath());
} else if (deletedArtwork) {
MusicUtil.deleteAlbumArt(context, info.artworkInfo.albumId);
} }
});
if (deleteArtwork) {
MusicUtil.deleteAlbumArt(AbsTagEditorActivity.this, getId());
} else if (artwork != null) {
// AlbumTagEditorActivity already inserts the album cover for us
} }
rescanMediaAndQuitOnFinish();
return info.filePaths.toArray(new String[info.filePaths.size()]);
} catch (Exception e) {
e.printStackTrace();
return null;
} }
}).start();
}
private static class FinishOnCompletedMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
private WeakReference<AbsTagEditorActivity> activityWeakReference;
private MediaScannerConnection connection;
private String[] toBeScanned;
private int position;
private FinishOnCompletedMediaScanner(AbsTagEditorActivity activity, String[] toBeScanned) {
activityWeakReference = new WeakReference<>(activity);
this.toBeScanned = toBeScanned;
connection = new MediaScannerConnection(activity.getApplicationContext(), this);
}
public void scan() {
connection.connect();
} }
@Override @Override
public void onMediaScannerConnected() { protected void onPostExecute(String[] toBeScanned) {
scanNextPath(); super.onPostExecute(toBeScanned);
scan(toBeScanned);
} }
@Override @Override
public void onScanCompleted(String path, Uri uri) { protected void onCancelled(String[] toBeScanned) {
scanNextPath(); super.onCancelled(toBeScanned);
scan(toBeScanned);
} }
private void scanNextPath() { private void scan(String[] toBeScanned) {
if (position < toBeScanned.length) { Context context = getContext();
connection.scanFile(toBeScanned[position], null); MediaScannerConnection.scanFile(applicationContext, toBeScanned, null, context instanceof Activity ? new UpdateToastMediaScannerCompletionListener((Activity) context, toBeScanned) : null);
}
checkIsDone();
position++;
} }
private void checkIsDone() { @Override
if (position >= toBeScanned.length - 1) { protected Dialog createDialog(@NonNull Context context) {
connection.disconnect(); return new MaterialDialog.Builder(context)
AbsTagEditorActivity activity = activityWeakReference.get(); .title(R.string.writing_files)
if (activity != null) { .cancelable(false)
activity.dismissDialogAndFinish(); .progress(false, 0)
} .build();
}
@Override
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
super.onProgressUpdate(dialog, values);
((MaterialDialog) dialog).setMaxProgress(values[1]);
((MaterialDialog) dialog).setProgress(values[0]);
}
public static class LoadingInfo {
public final Collection<String> filePaths;
@Nullable
public final Map<FieldKey, String> fieldKeyValueMap;
@Nullable
private ArtworkInfo artworkInfo;
private LoadingInfo(Collection<String> filePaths, @Nullable Map<FieldKey, String> fieldKeyValueMap, @Nullable ArtworkInfo artworkInfo) {
this.filePaths = filePaths;
this.fieldKeyValueMap = fieldKeyValueMap;
this.artworkInfo = artworkInfo;
} }
} }
} }
public void dismissDialogAndFinish() { public static class ArtworkInfo {
runOnUiThread(new Runnable() { public final int albumId;
@Override public final Bitmap artwork;
public void run() {
progressDialog.dismiss();
finish();
}
});
}
private void rescanMediaAndQuitOnFinish() { public ArtworkInfo(int albumId, Bitmap artwork) {
String[] toBeScanned = new String[songPaths.size()]; this.albumId = albumId;
toBeScanned = songPaths.toArray(toBeScanned); this.artwork = artwork;
new FinishOnCompletedMediaScanner(this, toBeScanned).scan(); }
} }
protected int getId() { protected int getId() {

View file

@ -10,7 +10,6 @@ import android.support.annotation.NonNull;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
@ -28,16 +27,10 @@ import com.kabouzeid.gramophone.lastfm.rest.model.LastFmAlbum;
import com.kabouzeid.gramophone.loader.AlbumLoader; import com.kabouzeid.gramophone.loader.AlbumLoader;
import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.util.LastFMUtil; import com.kabouzeid.gramophone.util.LastFMUtil;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.PhonographColorUtil; import com.kabouzeid.gramophone.util.PhonographColorUtil;
import org.jaudiotagger.tag.FieldKey; import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.images.Artwork;
import org.jaudiotagger.tag.images.ArtworkFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List; import java.util.List;
@ -45,6 +38,7 @@ import java.util.Map;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import hugo.weaving.DebugLog;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
@ -169,7 +163,6 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text
@Override @Override
protected void save() { protected void save() {
Artwork artwork = null;
Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class); Map<FieldKey, String> fieldKeyValueMap = new EnumMap<>(FieldKey.class);
fieldKeyValueMap.put(FieldKey.ALBUM, albumTitle.getText().toString()); fieldKeyValueMap.put(FieldKey.ALBUM, albumTitle.getText().toString());
//android seems not to recognize album_artist field so we additionally write the normal artist field //android seems not to recognize album_artist field so we additionally write the normal artist field
@ -178,18 +171,7 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text
fieldKeyValueMap.put(FieldKey.GENRE, genre.getText().toString()); fieldKeyValueMap.put(FieldKey.GENRE, genre.getText().toString());
fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString()); fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString());
File albumArtFile = MusicUtil.createAlbumArtFile(String.valueOf(getId())); writeValuesToFiles(fieldKeyValueMap, deleteAlbumArt ? new ArtworkInfo(getId(), null) : albumArtBitmap == null ? null : new ArtworkInfo(getId(), albumArtBitmap));
if (albumArtBitmap != null) {
try {
albumArtBitmap.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
MusicUtil.insertAlbumArt(this, getId(), albumArtFile.getAbsolutePath());
} catch (IOException e) {
Log.e(TAG, "error while trying to create the artwork from file", e);
}
}
writeValuesToFiles(fieldKeyValueMap, artwork, deleteAlbumArt);
} }
@Override @Override
@ -216,7 +198,7 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text
.transcode(new BitmapPaletteTranscoder(AlbumTagEditorActivity.this), BitmapPaletteWrapper.class) .transcode(new BitmapPaletteTranscoder(AlbumTagEditorActivity.this), BitmapPaletteWrapper.class)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true) .skipMemoryCache(true)
.into(new SimpleTarget<BitmapPaletteWrapper>(4097, 4097) { .into(new SimpleTarget<BitmapPaletteWrapper>() {
@Override @Override
public void onLoadFailed(Exception e, Drawable errorDrawable) { public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable); super.onLoadFailed(e, errorDrawable);
@ -251,6 +233,7 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text
dataChanged(); dataChanged();
} }
@DebugLog
private static Bitmap getResizedAlbumCover(@NonNull Bitmap src, int maxForSmallerSize) { private static Bitmap getResizedAlbumCover(@NonNull Bitmap src, int maxForSmallerSize) {
int width = src.getWidth(); int width = src.getWidth();
int height = src.getHeight(); int height = src.getHeight();
@ -274,11 +257,7 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text
dstHeight = maxForSmallerSize; dstHeight = maxForSmallerSize;
} }
Bitmap scaledBitmap = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false); return Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false);
if (scaledBitmap != src) {
src.recycle();
}
return scaledBitmap;
} }
@Override @Override

View file

@ -98,7 +98,7 @@ public class SongTagEditorActivity extends AbsTagEditorActivity implements TextW
fieldKeyValueMap.put(FieldKey.GENRE, genre.getText().toString()); fieldKeyValueMap.put(FieldKey.GENRE, genre.getText().toString());
fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString()); fieldKeyValueMap.put(FieldKey.YEAR, year.getText().toString());
fieldKeyValueMap.put(FieldKey.TRACK, trackNumber.getText().toString()); fieldKeyValueMap.put(FieldKey.TRACK, trackNumber.getText().toString());
writeValuesToFiles(fieldKeyValueMap); writeValuesToFiles(fieldKeyValueMap, null);
} }
@Override @Override

View file

@ -1,20 +1,14 @@
package com.kabouzeid.gramophone.ui.fragments.mainactivity.folders; package com.kabouzeid.gramophone.ui.fragments.mainactivity.folders;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -46,6 +40,8 @@ import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper; import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper; import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.misc.DialogAsyncTask;
import com.kabouzeid.gramophone.misc.UpdateToastMediaScannerCompletionListener;
import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader; import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader;
import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.ui.activities.MainActivity;
@ -485,52 +481,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
if (toBeScanned == null || toBeScanned.length < 1) { if (toBeScanned == null || toBeScanned.length < 1) {
Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show();
} else { } else {
MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null, new UpdateToastCompletionListener(getActivity(), toBeScanned)); MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null, new UpdateToastMediaScannerCompletionListener(getActivity(), toBeScanned));
}
}
private static class UpdateToastCompletionListener implements MediaScannerConnection.OnScanCompletedListener {
int scanned = 0;
int failed = 0;
private final String[] toBeScanned;
private final String scannedFiles;
private final String couldNotScanFiles;
private final WeakReference<Toast> toastWeakReference;
private final WeakReference<Activity> activityWeakReference;
@SuppressLint("ShowToast")
public UpdateToastCompletionListener(Activity activity, String[] toBeScanned) {
this.toBeScanned = toBeScanned;
scannedFiles = activity.getString(R.string.scanned_files);
couldNotScanFiles = activity.getString(R.string.could_not_scan_files);
toastWeakReference = new WeakReference<>(Toast.makeText(activity, "", Toast.LENGTH_SHORT));
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void onScanCompleted(final String path, final Uri uri) {
Activity activity = activityWeakReference.get();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast toast = toastWeakReference.get();
if (toast != null) {
if (uri == null) {
failed++;
} else {
scanned++;
}
String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : "");
toast.setText(text);
toast.show();
}
}
});
}
} }
} }
@ -585,13 +536,13 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
} }
} }
private static class ListSongsAsyncTask extends DialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> { private static class ListSongsAsyncTask extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
private WeakReference<Context> contextWeakReference; private WeakReference<Context> contextWeakReference;
private WeakReference<OnSongsListedCallback> callbackWeakReference; private WeakReference<OnSongsListedCallback> callbackWeakReference;
private final Object extra; private final Object extra;
public ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) { public ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
super(context, R.string.listing_files); super(context);
this.extra = extra; this.extra = extra;
contextWeakReference = new WeakReference<>(context); contextWeakReference = new WeakReference<>(context);
callbackWeakReference = new WeakReference<>(callback); callbackWeakReference = new WeakReference<>(callback);
@ -668,11 +619,11 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
} }
} }
private static class ListPathsAsyncTask extends DialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> { private static class ListPathsAsyncTask extends ListingFilesDialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference; private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) { public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
super(context, R.string.listing_files); super(context);
onPathsListedCallbackWeakReference = new WeakReference<>(callback); onPathsListedCallbackWeakReference = new WeakReference<>(callback);
} }
@ -753,76 +704,41 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
} }
} }
private static abstract class DialogAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { private static abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> extends DialogAsyncTask<Params, Progress, Result> {
private final int title; public ListingFilesDialogAsyncTask(Context context) {
private WeakReference<Context> contextWeakReference; super(context);
private WeakReference<Dialog> dialogWeakReference; }
private boolean supposedToBeDismissed; public ListingFilesDialogAsyncTask(Context context, int showDelay) {
super(context, showDelay);
public DialogAsyncTask(Context context, @StringRes int title) {
contextWeakReference = new WeakReference<>(context);
dialogWeakReference = new WeakReference<>(null);
this.title = title;
} }
@Override @Override
protected void onPreExecute() { protected Dialog createDialog(@NonNull Context context) {
super.onPreExecute(); return new MaterialDialog.Builder(context)
new Handler().postDelayed(new Runnable() { .title(R.string.listing_files)
@Override .progress(true, 0)
public void run() { .progressIndeterminateStyle(true)
if (!supposedToBeDismissed && contextWeakReference.get() != null) { .cancelListener(new DialogInterface.OnCancelListener() {
Dialog dialog = new MaterialDialog.Builder(contextWeakReference.get()) @Override
.title(title) public void onCancel(DialogInterface dialog) {
.progress(true, 0) cancel(false);
.progressIndeterminateStyle(true) }
.cancelListener(new DialogInterface.OnCancelListener() { })
@Override .dismissListener(new DialogInterface.OnDismissListener() {
public void onCancel(DialogInterface dialog) { @Override
cancel(false); public void onDismiss(DialogInterface dialog) {
} cancel(false);
}) }
.dismissListener(new DialogInterface.OnDismissListener() { })
@Override .negativeText(android.R.string.cancel)
public void onDismiss(DialogInterface dialog) { .onNegative(new MaterialDialog.SingleButtonCallback() {
cancel(false); @Override
} public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
}) cancel(false);
.negativeText(android.R.string.cancel) }
.onNegative(new MaterialDialog.SingleButtonCallback() { })
@Override .show();
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
cancel(false);
}
})
.show();
dialogWeakReference = new WeakReference<>(dialog);
}
}
}, 200);
}
@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 {
if (dialogWeakReference.get() != null)
dialogWeakReference.get().dismiss();
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
} }

View file

@ -46,7 +46,7 @@
<string name="track">Track</string> <string name="track">Track</string>
<string name="track_hint">"Track (2 for track 2 or 3004 for CD3 track 4)"</string> <string name="track_hint">"Track (2 for track 2 or 3004 for CD3 track 4)"</string>
<string name="album_or_artist_empty">The title or artist is empty.</string> <string name="album_or_artist_empty">The title or artist is empty.</string>
<string name="writing_file_number">Writing file</string> <string name="writing_files">Writing files</string>
<string name="saving_changes">Saving changes…</string> <string name="saving_changes">Saving changes…</string>
<string name="saving_to_file">Saving to file…</string> <string name="saving_to_file">Saving to file…</string>
<string name="label_details">Details</string> <string name="label_details">Details</string>