diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4ed887e6..d0c73348 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -113,9 +113,6 @@
android:name=".ui.activities.intro.AppIntroActivity"
android:label="@string/intro_label"
android:theme="@style/Theme.Intro" />
-
diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java
index 00b5eb84..da7ef178 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java
@@ -1,41 +1,24 @@
package com.kabouzeid.gramophone.dialogs;
-import android.annotation.TargetApi;
-import android.app.Activity;
import android.app.Dialog;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
-import android.support.v4.app.FragmentActivity;
import android.text.Html;
-import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.gramophone.R;
-import com.kabouzeid.gramophone.misc.DialogAsyncTask;
import com.kabouzeid.gramophone.model.Song;
-import com.kabouzeid.gramophone.ui.activities.saf.SAFGuideActivity;
import com.kabouzeid.gramophone.util.MusicUtil;
-import com.kabouzeid.gramophone.util.SAFUtil;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
* @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad)
*/
public class DeleteSongsDialog extends DialogFragment {
- private ArrayList songsToRemove;
- private Song currentSong;
-
@NonNull
public static DeleteSongsDialog create(Song song) {
ArrayList list = new ArrayList<>();
@@ -71,171 +54,14 @@ public class DeleteSongsDialog extends DialogFragment {
.content(content)
.positiveText(R.string.delete_action)
.negativeText(android.R.string.cancel)
- .autoDismiss(false)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
- songsToRemove = songs;
- new DeleteSongsAsyncTask(DeleteSongsDialog.this).execute(new DeleteSongsAsyncTask.LoadingInfo(songs, null));
- }
- })
- .onNegative(new MaterialDialog.SingleButtonCallback() {
- @Override
- public void onClick(@NonNull MaterialDialog materialDialog, @NonNull DialogAction dialogAction) {
- dismiss();
+ if (getActivity() == null)
+ return;
+ MusicUtil.deleteTracks(getActivity(), songs);
}
})
.build();
}
-
- private void deleteSongs(List songs, List safUris) {
- MusicUtil.deleteTracks(getActivity(), songs, safUris);
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private void deleteSongsKitkat() {
- if (songsToRemove.size() < 1) {
- dismiss();
- return;
- }
-
- currentSong = songsToRemove.remove(0);
-
- if (!SAFUtil.isSAFRequired(currentSong)) {
- deleteSongs(Collections.singletonList(currentSong), null);
- deleteSongsKitkat();
- } else {
- Toast.makeText(getActivity(), String.format(getString(R.string.saf_pick_file), currentSong.data), Toast.LENGTH_LONG).show();
- SAFUtil.openFilePicker(this);
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
- switch (requestCode) {
- case SAFGuideActivity.REQUEST_CODE_SAF_GUIDE:
- SAFUtil.openTreePicker(this);
- break;
-
- case SAFUtil.REQUEST_SAF_PICK_TREE:
- case SAFUtil.REQUEST_SAF_PICK_FILE:
- new DeleteSongsAsyncTask(this).execute(new DeleteSongsAsyncTask.LoadingInfo(requestCode, resultCode, intent));
- break;
- }
- }
-
- private static class DeleteSongsAsyncTask extends DialogAsyncTask {
- private WeakReference dialog;
- private WeakReference activity;
-
- public DeleteSongsAsyncTask(DeleteSongsDialog dialog) {
- super(dialog.getActivity());
- this.dialog = new WeakReference<>(dialog);
- this.activity = new WeakReference<>(dialog.getActivity());
- }
-
- @Override
- protected Void doInBackground(LoadingInfo... params) {
- try {
- LoadingInfo info = params[0];
-
- DeleteSongsDialog dialog = this.dialog.get();
- FragmentActivity activity = this.activity.get();
-
- if (dialog == null || activity == null)
- return null;
-
- if (!info.isIntent) {
- if (!SAFUtil.isSAFRequiredForSongs(info.songs)) {
- dialog.deleteSongs(info.songs, null);
- } else {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- if (SAFUtil.isSDCardAccessGranted(activity)) {
- dialog.deleteSongs(info.songs, null);
- } else {
- dialog.startActivityForResult(new Intent(activity, SAFGuideActivity.class), SAFGuideActivity.REQUEST_CODE_SAF_GUIDE);
- }
- } else {
- dialog.deleteSongsKitkat();
- }
- }
- } else {
- switch (info.requestCode) {
- case SAFUtil.REQUEST_SAF_PICK_TREE:
- if (info.resultCode == Activity.RESULT_OK) {
- SAFUtil.saveTreeUri(activity, info.intent);
- dialog.deleteSongs(dialog.songsToRemove, null);
- }
- break;
-
- case SAFUtil.REQUEST_SAF_PICK_FILE:
- if (info.resultCode == Activity.RESULT_OK) {
- dialog.deleteSongs(Collections.singletonList(dialog.currentSong), Collections.singletonList(info.intent.getData()));
- }
- break;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Void v) {
- super.onPostExecute(v);
-
- DeleteSongsDialog dialog = this.dialog.get();
- FragmentActivity activity = this.activity.get();
- if (dialog != null && activity != null && !activity.isFinishing()) {
- dialog.dismiss();
- }
- }
-
- @Override
- protected void onCancelled() {
- super.onCancelled();
-
- DeleteSongsDialog dialog = this.dialog.get();
- FragmentActivity activity = this.activity.get();
- if (dialog != null && activity != null && !activity.isFinishing()) {
- dialog.dismiss();
- }
- }
-
- @Override
- protected Dialog createDialog(@NonNull Context context) {
- return new MaterialDialog.Builder(context)
- .title(R.string.deleting_songs)
- .cancelable(false)
- .progress(true, 0)
- .build();
- }
-
- public static class LoadingInfo {
- public boolean isIntent;
-
- public List songs;
- public List safUris;
-
- public int requestCode;
- public int resultCode;
- public Intent intent;
-
- public LoadingInfo(List songs, List safUris) {
- this.isIntent = false;
- this.songs = songs;
- this.safUris = safUris;
- }
-
- public LoadingInfo(int requestCode, int resultCode, Intent intent) {
- this.isIntent = true;
- this.requestCode = requestCode;
- this.resultCode = resultCode;
- this.intent = intent;
- }
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/saf/SAFGuideActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/saf/SAFGuideActivity.java
deleted file mode 100644
index aff54327..00000000
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/saf/SAFGuideActivity.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.kabouzeid.gramophone.ui.activities.saf;
-
-import android.os.Build;
-import android.os.Bundle;
-
-import com.heinrichreimersoftware.materialintro.app.IntroActivity;
-import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
-import com.kabouzeid.gramophone.R;
-
-
-public class SAFGuideActivity extends IntroActivity {
-
- public static final int REQUEST_CODE_SAF_GUIDE = 98;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setButtonCtaVisible(false);
- setButtonNextVisible(false);
- setButtonBackVisible(false);
-
- setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
-
- String title = String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
-
- addSlide(new SimpleSlide.Builder()
- .title(title)
- .description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 ? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description)
- .image(R.drawable.saf_guide_1)
- .background(R.color.md_indigo_300)
- .backgroundDark(R.color.md_indigo_400)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- addSlide(new SimpleSlide.Builder()
- .title(R.string.saf_guide_slide2_title)
- .description(R.string.saf_guide_slide2_description)
- .image(R.drawable.saf_guide_2)
- .background(R.color.md_indigo_500)
- .backgroundDark(R.color.md_indigo_600)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- addSlide(new SimpleSlide.Builder()
- .title(R.string.saf_guide_slide3_title)
- .description(R.string.saf_guide_slide3_description)
- .image(R.drawable.saf_guide_3)
- .background(R.color.md_indigo_700)
- .backgroundDark(R.color.md_indigo_800)
- .layout(R.layout.fragment_simple_slide_large_image)
- .build());
- }
-}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java
index 06dfcada..cf9ab563 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java
@@ -1,6 +1,5 @@
package com.kabouzeid.gramophone.ui.activities.tageditor;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.app.SearchManager;
@@ -10,7 +9,6 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -22,7 +20,6 @@ import android.view.View;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.github.ksoichiro.android.observablescrollview.ObservableScrollView;
@@ -34,25 +31,25 @@ import com.kabouzeid.gramophone.misc.DialogAsyncTask;
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.saf.SAFGuideActivity;
import com.kabouzeid.gramophone.util.MusicUtil;
-import com.kabouzeid.gramophone.util.SAFUtil;
import com.kabouzeid.gramophone.util.Util;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
+import org.jaudiotagger.audio.exceptions.CannotReadException;
+import org.jaudiotagger.audio.exceptions.CannotWriteException;
+import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
+import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
+import org.jaudiotagger.tag.TagException;
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.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -98,11 +95,6 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
};
private List songPaths;
- private List savedSongPaths;
- private String currentSongPath;
- private Map savedTags;
- private ArtworkInfo savedArtworkInfo;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -265,16 +257,6 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
fab.setEnabled(true);
}
- private void hideFab() {
- fab.animate()
- .setDuration(500)
- .setInterpolator(new OvershootInterpolator())
- .scaleX(0)
- .scaleY(0)
- .start();
- fab.setEnabled(false);
- }
-
protected void setImageBitmap(@Nullable final Bitmap bitmap, int bgColor) {
if (bitmap == null) {
image.setImageResource(R.drawable.default_album_art);
@@ -296,52 +278,15 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
protected void writeValuesToFiles(@NonNull final Map fieldKeyValueMap, @Nullable final ArtworkInfo artworkInfo) {
Util.hideSoftKeyboard(this);
- hideFab();
-
- savedSongPaths = getSongPaths();
- savedTags = fieldKeyValueMap;
- savedArtworkInfo = artworkInfo;
-
- if (!SAFUtil.isSAFRequired(savedSongPaths)) {
- writeTags(savedSongPaths);
- } else {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- if (SAFUtil.isSDCardAccessGranted(this)) {
- writeTags(savedSongPaths);
- } else {
- startActivityForResult(new Intent(this, SAFGuideActivity.class), SAFGuideActivity.REQUEST_CODE_SAF_GUIDE);
- }
- } else {
- writeTagsKitkat();
- }
- }
- }
-
- private void writeTags(List paths) {
- new WriteTagsAsyncTask(this).execute(new WriteTagsAsyncTask.LoadingInfo(paths, savedTags, savedArtworkInfo));
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private void writeTagsKitkat() {
- if (savedSongPaths.size() < 1) return;
-
- currentSongPath = savedSongPaths.remove(0);
-
- if (!SAFUtil.isSAFRequired(currentSongPath)) {
- writeTags(Collections.singletonList(currentSongPath));
- writeTagsKitkat();
- } else {
- Toast.makeText(this, String.format(getString(R.string.saf_pick_file), currentSongPath), Toast.LENGTH_LONG).show();
- SAFUtil.openFilePicker(this);
- }
+ new WriteTagsAsyncTask(this).execute(new WriteTagsAsyncTask.LoadingInfo(getSongPaths(), fieldKeyValueMap, artworkInfo));
}
private static class WriteTagsAsyncTask extends DialogAsyncTask {
- private WeakReference activity;
+ Context applicationContext;
- public WriteTagsAsyncTask(Activity activity) {
- super(activity);
- this.activity = new WeakReference<>(activity);
+ public WriteTagsAsyncTask(Context context) {
+ super(context);
+ applicationContext = context;
}
@Override
@@ -367,14 +312,6 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
for (String filePath : info.filePaths) {
publishProgress(++counter, info.filePaths.size());
try {
- Uri safUri = null;
-
- if (filePath.contains(SAFUtil.SEPARATOR)) {
- String[] fragments = filePath.split(SAFUtil.SEPARATOR);
- filePath = fragments[0];
- safUri = Uri.parse(fragments[1]);
- }
-
AudioFile audioFile = AudioFileIO.read(new File(filePath));
Tag tag = audioFile.getTagOrCreateAndSetDefault();
@@ -399,10 +336,8 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
}
}
- Activity activity = this.activity.get();
-
- SAFUtil.write(activity, audioFile, safUri);
- } catch (@NonNull Exception e) {
+ audioFile.commit();
+ } catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
e.printStackTrace();
}
}
@@ -416,18 +351,7 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
}
}
- Collection paths = info.filePaths;
-
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { // remove SAF URI from paths
- paths = new ArrayList<>(info.filePaths.size());
- for (String path : info.filePaths) {
- if (path.contains(SAFUtil.SEPARATOR))
- path = path.split(SAFUtil.SEPARATOR)[0];
- paths.add(path);
- }
- }
-
- return paths.toArray(new String[paths.size()]);
+ return info.filePaths.toArray(new String[info.filePaths.size()]);
} catch (Exception e) {
e.printStackTrace();
return null;
@@ -447,10 +371,8 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
}
private void scan(String[] toBeScanned) {
- Activity activity = this.activity.get();
- if (activity != null) {
- MediaScannerConnection.scanFile(activity, toBeScanned, null, new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
- }
+ Context context = getContext();
+ MediaScannerConnection.scanFile(applicationContext, toBeScanned, null, context instanceof Activity ? new UpdateToastMediaScannerCompletionListener((Activity) context, toBeScanned) : null);
}
@Override
@@ -499,32 +421,14 @@ public abstract class AbsTagEditorActivity extends AbsBaseActivity {
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, @NonNull Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
+ protected void onActivityResult(int requestCode, int resultCode, @NonNull Intent imageReturnedIntent) {
+ super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
switch (requestCode) {
case REQUEST_CODE_SELECT_IMAGE:
if (resultCode == RESULT_OK) {
- Uri selectedImage = intent.getData();
+ Uri selectedImage = imageReturnedIntent.getData();
loadImageFromFile(selectedImage);
}
- break;
-
- case SAFGuideActivity.REQUEST_CODE_SAF_GUIDE:
- SAFUtil.openTreePicker(this);
- break;
-
- case SAFUtil.REQUEST_SAF_PICK_TREE:
- if (resultCode == RESULT_OK) {
- SAFUtil.saveTreeUri(this, intent);
- writeTags(savedSongPaths);
- }
- break;
-
- case SAFUtil.REQUEST_SAF_PICK_FILE:
- if (resultCode == RESULT_OK) {
- writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent.getDataString()));
- }
- break;
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java
index 3a5f43e6..df54e766 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java
@@ -12,7 +12,6 @@ import com.kabouzeid.gramophone.loader.SortedCursor;
import com.kabouzeid.gramophone.model.Song;
import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
@@ -191,15 +190,4 @@ public final class FileUtil {
fin.close();
return ret;
}
-
- public static byte[] readBytes(InputStream stream) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buffer = new byte[4096];
- int count;
- while ((count = stream.read(buffer)) != -1) {
- baos.write(buffer, 0, count);
- }
- stream.close();
- return baos.toByteArray();
- }
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
index 8728564c..4f299565 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
@@ -1,6 +1,5 @@
package com.kabouzeid.gramophone.util;
-import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -15,6 +14,7 @@ import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import android.util.Log;
import android.widget.Toast;
import com.kabouzeid.gramophone.R;
@@ -23,6 +23,7 @@ import com.kabouzeid.gramophone.loader.PlaylistLoader;
import com.kabouzeid.gramophone.loader.SongLoader;
import com.kabouzeid.gramophone.model.Artist;
import com.kabouzeid.gramophone.model.Playlist;
+import com.kabouzeid.gramophone.model.PlaylistSong;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.model.lyrics.AbsSynchronizedLyrics;
@@ -174,7 +175,7 @@ public class MusicUtil {
return albumArtDir;
}
- public static void deleteTracks(@NonNull final Activity activity, @NonNull final List songs, @Nullable final List safUris) {
+ public static void deleteTracks(@NonNull final Context context, @NonNull final List songs) {
final String[] projection = new String[]{
BaseColumns._ID, MediaStore.MediaColumns.DATA
};
@@ -189,7 +190,7 @@ public class MusicUtil {
selection.append(")");
try {
- final Cursor cursor = activity.getContentResolver().query(
+ final Cursor cursor = context.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(),
null, null);
if (cursor != null) {
@@ -198,35 +199,37 @@ public class MusicUtil {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
final int id = cursor.getInt(0);
- final Song song = SongLoader.getSong(activity, id);
+ final Song song = SongLoader.getSong(context, id);
MusicPlayerRemote.removeFromQueue(song);
cursor.moveToNext();
}
// Step 2: Remove selected tracks from the database
- activity.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
selection.toString(), null);
// Step 3: Remove files from card
cursor.moveToFirst();
- int i = 0;
while (!cursor.isAfterLast()) {
final String name = cursor.getString(1);
- final Uri safUri = safUris == null || safUris.size() <= i ? null : safUris.get(i);
- SAFUtil.delete(activity, name, safUri);
- i++;
- cursor.moveToNext();
+ try { // File.delete can throw a security exception
+ final File f = new File(name);
+ if (!f.delete()) {
+ // I'm not sure if we'd ever get here (deletion would
+ // have to fail, but no exception thrown)
+ Log.e("MusicUtils", "Failed to delete file " + name);
+ }
+ cursor.moveToNext();
+ } catch (@NonNull final SecurityException ex) {
+ cursor.moveToNext();
+ } catch (NullPointerException e) {
+ Log.e("MusicUtils", "Failed to find file " + name);
+ }
}
cursor.close();
}
- activity.getContentResolver().notifyChange(Uri.parse("content://media"), null);
-
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songs.size()), Toast.LENGTH_SHORT).show();
- }
- });
+ context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
+ Toast.makeText(context, context.getString(R.string.deleted_x_songs, songs.size()), Toast.LENGTH_SHORT).show();
} catch (SecurityException ignored) {
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
index 69df6d5e..565df23e 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
@@ -3,7 +3,6 @@ package com.kabouzeid.gramophone.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
-import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.StyleRes;
@@ -72,8 +71,6 @@ public final class PreferenceUtil {
public static final String SYNCHRONIZED_LYRICS_SHOW = "synchronized_lyrics_show";
- public static final String SAF_SDCARD_URI = "saf_sdcard_uri";
-
private static PreferenceUtil sInstance;
private final SharedPreferences mPreferences;
@@ -409,12 +406,4 @@ public final class PreferenceUtil {
public final boolean synchronizedLyricsShow() {
return mPreferences.getBoolean(SYNCHRONIZED_LYRICS_SHOW, true);
}
-
- public final String getSAFSDCardUri() {
- return mPreferences.getString(SAF_SDCARD_URI, "");
- }
-
- public final void setSAFSDCardUri(Uri uri) {
- mPreferences.edit().putString(SAF_SDCARD_URI, uri.toString()).apply();
- }
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/SAFUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/SAFUtil.java
deleted file mode 100644
index 80038481..00000000
--- a/app/src/main/java/com/kabouzeid/gramophone/util/SAFUtil.java
+++ /dev/null
@@ -1,291 +0,0 @@
-package com.kabouzeid.gramophone.util;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.UriPermission;
-import android.net.Uri;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.provider.DocumentFile;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.kabouzeid.gramophone.R;
-import com.kabouzeid.gramophone.model.Song;
-
-import org.jaudiotagger.audio.AudioFile;
-import org.jaudiotagger.audio.exceptions.CannotWriteException;
-import org.jaudiotagger.audio.generic.Utils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class SAFUtil {
-
- public static final String TAG = SAFUtil.class.getSimpleName();
- public static final String SEPARATOR = "###/SAF/###";
-
- public static final int REQUEST_SAF_PICK_FILE = 42;
- public static final int REQUEST_SAF_PICK_TREE = 43;
-
- public static boolean isSAFRequired(File file) {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !file.canWrite();
- }
-
- public static boolean isSAFRequired(String path) {
- return isSAFRequired(new File(path));
- }
-
- public static boolean isSAFRequired(AudioFile audio) {
- return isSAFRequired(audio.getFile());
- }
-
- public static boolean isSAFRequired(Song song) {
- return isSAFRequired(song.data);
- }
-
- public static boolean isSAFRequired(List paths) {
- for (String path : paths) {
- if (isSAFRequired(path)) return true;
- }
- return false;
- }
-
- public static boolean isSAFRequiredForSongs(List songs) {
- for (Song song : songs) {
- if (isSAFRequired(song)) return true;
- }
- return false;
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void openFilePicker(Activity activity) {
- Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- i.addCategory(Intent.CATEGORY_OPENABLE);
- i.setType("audio/*");
- i.putExtra("android.content.extra.SHOW_ADVANCED", true);
- activity.startActivityForResult(i, SAFUtil.REQUEST_SAF_PICK_FILE);
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void openFilePicker(Fragment fragment) {
- Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- i.addCategory(Intent.CATEGORY_OPENABLE);
- i.setType("audio/*");
- i.putExtra("android.content.extra.SHOW_ADVANCED", true);
- fragment.startActivityForResult(i, SAFUtil.REQUEST_SAF_PICK_FILE);
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public static void openTreePicker(Activity activity) {
- Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
- i.putExtra("android.content.extra.SHOW_ADVANCED", true);
- activity.startActivityForResult(i, SAFUtil.REQUEST_SAF_PICK_TREE);
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public static void openTreePicker(Fragment fragment) {
- Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
- i.putExtra("android.content.extra.SHOW_ADVANCED", true);
- fragment.startActivityForResult(i, SAFUtil.REQUEST_SAF_PICK_TREE);
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void saveTreeUri(Context context, Intent data) {
- Uri uri = data.getData();
- context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- PreferenceUtil.getInstance(context).setSAFSDCardUri(uri);
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public static boolean isTreeUriSaved(Context context) {
- return !TextUtils.isEmpty(PreferenceUtil.getInstance(context).getSAFSDCardUri());
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public static boolean isSDCardAccessGranted(Context context) {
- if (!isTreeUriSaved(context)) return false;
-
- String sdcardUri = PreferenceUtil.getInstance(context).getSAFSDCardUri();
-
- List perms = context.getContentResolver().getPersistedUriPermissions();
- for (UriPermission perm : perms) {
- if (perm.getUri().toString().equals(sdcardUri) && perm.isWritePermission()) return true;
- }
-
- return false;
- }
-
- /**
- * https://github.com/vanilla-music/vanilla-music-tag-editor/commit/e00e87fef289f463b6682674aa54be834179ccf0#diff-d436417358d5dfbb06846746d43c47a5R359
- * Finds needed file through Document API for SAF. It's not optimized yet - you can still gain wrong URI on
- * files such as "/a/b/c.mp3" and "/b/a/c.mp3", but I consider it complete enough to be usable.
- *
- * @param dir - document file representing current dir of search
- * @param segments - path segments that are left to find
- * @return URI for found file. Null if nothing found.
- */
- @Nullable
- public static Uri findDocument(DocumentFile dir, List segments) {
- for (DocumentFile file : dir.listFiles()) {
- int index = segments.indexOf(file.getName());
- if (index == -1) {
- continue;
- }
-
- if (file.isDirectory()) {
- segments.remove(file.getName());
- return findDocument(file, segments);
- }
-
- if (file.isFile() && index == segments.size() - 1) {
- // got to the last part
- return file.getUri();
- }
- }
-
- return null;
- }
-
- public static void write(Context context, AudioFile audio, Uri safUri) {
- if (isSAFRequired(audio)) {
- writeSAF(context, audio, safUri);
- } else {
- try {
- writeFile(audio);
- } catch (CannotWriteException e) {
- e.printStackTrace();
- }
- }
- }
-
- public static void writeFile(AudioFile audio) throws CannotWriteException {
- audio.commit();
- }
-
- public static void writeSAF(Context context, AudioFile audio, Uri safUri) {
- Uri uri = null;
-
- if (context == null) {
- Log.e(TAG, "writeSAF: context == null");
- return;
- }
-
- if (isTreeUriSaved(context)) {
- List pathSegments = new ArrayList<>(Arrays.asList(audio.getFile().getAbsolutePath().split("/")));
- Uri sdcard = Uri.parse(PreferenceUtil.getInstance(context).getSAFSDCardUri());
- uri = findDocument(DocumentFile.fromTreeUri(context, sdcard), pathSegments);
- }
-
- if (uri == null) {
- uri = safUri;
- }
-
- if (uri == null) {
- Log.e(TAG, "writeSAF: Can't get SAF URI");
- toast(context, context.getString(R.string.saf_error_uri));
- return;
- }
-
- try {
- // copy file to app folder to use jaudiotagger
- final File original = audio.getFile();
- File temp = File.createTempFile("tmp-media", '.' + Utils.getExtension(original));
- Utils.copy(original, temp);
- temp.deleteOnExit();
- audio.setFile(temp);
- writeFile(audio);
-
- ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "rw");
- if (pfd == null) {
- Log.e(TAG, "writeSAF: SAF provided incorrect URI: " + uri);
- return;
- }
-
- // now read persisted data and write it to real FD provided by SAF
- FileInputStream fis = new FileInputStream(temp);
- byte[] audioContent = FileUtil.readBytes(fis);
- FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
- fos.write(audioContent);
- fos.close();
-
- temp.delete();
- } catch (final Exception e) {
- Log.e(TAG, "writeSAF: Failed to write to file descriptor provided by SAF", e);
-
- toast(context, String.format(context.getString(R.string.saf_write_failed), e.getLocalizedMessage()));
- }
- }
-
- public static void delete(Context context, String path, Uri safUri) {
- if (isSAFRequired(path)) {
- deleteSAF(context, path, safUri);
- } else {
- try {
- deleteFile(path);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- public static void deleteFile(String path) {
- new File(path).delete();
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void deleteSAF(Context context, String path, Uri safUri) {
- Uri uri = null;
-
- if (context == null) {
- Log.e(TAG, "deleteSAF: context == null");
- return;
- }
-
- if (isTreeUriSaved(context)) {
- List pathSegments = new ArrayList<>(Arrays.asList(path.split("/")));
- Uri sdcard = Uri.parse(PreferenceUtil.getInstance(context).getSAFSDCardUri());
- uri = findDocument(DocumentFile.fromTreeUri(context, sdcard), pathSegments);
- }
-
- if (uri == null) {
- uri = safUri;
- }
-
- if (uri == null) {
- Log.e(TAG, "deleteSAF: Can't get SAF URI");
- toast(context, context.getString(R.string.saf_error_uri));
- return;
- }
-
- try {
- DocumentsContract.deleteDocument(context.getContentResolver(), uri);
- } catch (final Exception e) {
- Log.e(TAG, "deleteSAF: Failed to delete a file descriptor provided by SAF", e);
-
- toast(context, String.format(context.getString(R.string.saf_delete_failed), e.getLocalizedMessage()));
- }
- }
-
- private static void toast(final Context context, final String message) {
- if (context instanceof Activity) {
- ((Activity) context).runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
-
-}
diff --git a/app/src/main/res/drawable-v21/saf_guide_1.png b/app/src/main/res/drawable-v21/saf_guide_1.png
deleted file mode 100644
index d31be5b5..00000000
Binary files a/app/src/main/res/drawable-v21/saf_guide_1.png and /dev/null differ
diff --git a/app/src/main/res/drawable-v21/saf_guide_2.png b/app/src/main/res/drawable-v21/saf_guide_2.png
deleted file mode 100644
index 24a9c294..00000000
Binary files a/app/src/main/res/drawable-v21/saf_guide_2.png and /dev/null differ
diff --git a/app/src/main/res/drawable-v21/saf_guide_3.png b/app/src/main/res/drawable-v21/saf_guide_3.png
deleted file mode 100644
index 2317a82c..00000000
Binary files a/app/src/main/res/drawable-v21/saf_guide_3.png and /dev/null differ
diff --git a/app/src/main/res/drawable-v26/saf_guide_1.png b/app/src/main/res/drawable-v26/saf_guide_1.png
deleted file mode 100644
index f4904601..00000000
Binary files a/app/src/main/res/drawable-v26/saf_guide_1.png and /dev/null differ
diff --git a/app/src/main/res/drawable-v26/saf_guide_2.png b/app/src/main/res/drawable-v26/saf_guide_2.png
deleted file mode 100644
index ddc84b27..00000000
Binary files a/app/src/main/res/drawable-v26/saf_guide_2.png and /dev/null differ
diff --git a/app/src/main/res/drawable-v26/saf_guide_3.png b/app/src/main/res/drawable-v26/saf_guide_3.png
deleted file mode 100644
index c3ada22a..00000000
Binary files a/app/src/main/res/drawable-v26/saf_guide_3.png and /dev/null differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0f8d58e0..050cdca7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -262,7 +262,6 @@
Copied device info to clipboard.
Your account data is only used for authentication.
You will be forwarded to the issue tracker website.
- Deleting songs
@string/action_shuffle_all
Shuffle
@@ -273,18 +272,4 @@
Playlist is empty
The playing notification provides actions for play/pause etc.
Playing notification
-
- Can\'t get SAF URI
- File write failed: %s
- File delete failed: %s
- SD card access required. Please pick root directory of SD card
- File access required. Pick %s
-
- %s needs SD card access
- Enable \'Show SD card\' in overflow menu
- Open navigation drawer
- Select your SD card in navigation drawer
- You need to select your SD card root directory
- Tap \'select\' button at the bottom of the screen
- Do not open any subfolders