Made the TagEditor more reliable.
This commit is contained in:
parent
fc4b3846c5
commit
ffa329d978
7 changed files with 324 additions and 250 deletions
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue