From 636da3daa8293a5b3438742aa8ad6d4e3a9eaf15 Mon Sep 17 00:00:00 2001 From: h4h13 Date: Fri, 24 Jan 2020 22:57:43 +0530 Subject: [PATCH] Removed sliding panel for now --- .../retromusic/CustomBottomSheetBehavior.java | 49 --- .../monkey/retromusic/MultiSheetView.java | 184 ---------- .../base/AbsSlidingMusicPanelActivity.kt | 18 +- .../dialogs/DeleteSongsAsyncTask.java | 73 ++-- .../retromusic/dialogs/DeleteSongsDialog.kt | 4 +- .../monkey/retromusic/util/MusicUtil.java | 339 +++++++++--------- .../res/layout/sliding_music_panel_layout.xml | 98 +---- 7 files changed, 223 insertions(+), 542 deletions(-) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/MultiSheetView.java diff --git a/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java b/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java deleted file mode 100644 index 47492772..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/CustomBottomSheetBehavior.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.bottomsheet.BottomSheetBehavior; - -public class CustomBottomSheetBehavior extends BottomSheetBehavior { - - private static final String TAG = "CustomBottomSheetBehavi"; - - private boolean allowDragging = true; - - public CustomBottomSheetBehavior() { - } - - public CustomBottomSheetBehavior(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { - if (!allowDragging) { - return false; - } - - return super.onInterceptTouchEvent(parent, child, event); - } - - public void setAllowDragging(boolean allowDragging) { - this.allowDragging = allowDragging; - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/MultiSheetView.java b/app/src/main/java/code/name/monkey/retromusic/MultiSheetView.java deleted file mode 100644 index 2e7edc86..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/MultiSheetView.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.bottomsheet.BottomSheetBehavior; - -public class MultiSheetView extends CoordinatorLayout { - - @interface Sheet { - - int NONE = 0; - int FIRST = 1; - int SECOND = 2; - } - - private static final String TAG = "MultiSheetView"; - - private CustomBottomSheetBehavior bottomSheetBehavior1; - - private CustomBottomSheetBehavior bottomSheetBehavior2; - - public MultiSheetView(@NonNull Context context) { - this(context, null); - } - - public MultiSheetView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - public MultiSheetView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public boolean consumeBackPress() { - switch (getCurrentSheet()) { - case Sheet.SECOND: - showSheet(Sheet.FIRST); - return true; - case Sheet.FIRST: - showSheet(Sheet.NONE); - return true; - case Sheet.NONE: - break; - } - return false; - } - - @Sheet - public int getCurrentSheet() { - if (bottomSheetBehavior2.getState() == BottomSheetBehavior.STATE_EXPANDED) { - return Sheet.SECOND; - } else if (bottomSheetBehavior1.getState() == BottomSheetBehavior.STATE_EXPANDED) { - return Sheet.FIRST; - } else { - return Sheet.NONE; - } - } - - @IdRes - public int getMainContainerResId() { - return R.id.mainContentFrame; - } - - @IdRes - public int getSheet1ContainerResId() { - return R.id.playerFragmentContainer; - } - - @IdRes - public int getSheet1PeekViewResId() { - return R.id.miniPlayerFragment; - } - - @IdRes - public int getSheet2ContainerResId() { - return R.id.sheet2Container; - } - - @IdRes - public int getSheet2PeekViewResId() { - return R.id.sheet2PeekView; - } - - public void showSheet(@Sheet int sheet) { - - // if we are already at our target panel, then don't do anything - if (sheet == getCurrentSheet()) { - return; - } - - switch (sheet) { - case Sheet.NONE: - bottomSheetBehavior2.setState(BottomSheetBehavior.STATE_COLLAPSED); - bottomSheetBehavior1.setState(BottomSheetBehavior.STATE_COLLAPSED); - break; - case Sheet.FIRST: - bottomSheetBehavior2.setState(BottomSheetBehavior.STATE_COLLAPSED); - bottomSheetBehavior1.setState(BottomSheetBehavior.STATE_EXPANDED); - break; - case Sheet.SECOND: - bottomSheetBehavior2.setState(BottomSheetBehavior.STATE_EXPANDED); - bottomSheetBehavior1.setState(BottomSheetBehavior.STATE_EXPANDED); - break; - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - View sheet1 = findViewById(R.id.slidingPanel); - bottomSheetBehavior1 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet1); - - View sheet2 = findViewById(R.id.sheet2); - View thump = findViewById(R.id.thumb); - View extendedToolbar = findViewById(R.id.extendedToolbar); - bottomSheetBehavior2 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet2); - bottomSheetBehavior2.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { - @Override - public void onSlide(@NonNull View bottomSheet, float slideOffset) { - bottomSheetBehavior1.setAllowDragging(false); - thump.setRotation(slideOffset * 180); - extendedToolbar.setAlpha(slideOffset); - } - - @Override - public void onStateChanged(@NonNull View bottomSheet, int newState) { - if (newState == BottomSheetBehavior.STATE_EXPANDED - || newState == BottomSheetBehavior.STATE_DRAGGING) { - bottomSheetBehavior1.setAllowDragging(false); - } else { - bottomSheetBehavior1.setAllowDragging(true); - } - } - }); - - //First sheet view click listener - findViewById(getSheet1PeekViewResId()) - .setOnClickListener(v -> bottomSheetBehavior1.setState(BottomSheetBehavior.STATE_EXPANDED)); - - // FIXME: - // This click listener (combined with a nested RecyclerView in Sheet 2's container), causes - // the second peek view to stop responding to drag events. - // See `Sheet2Controller`. Remove this ClickListener here to see things working as they should. - - //Second sheet view click listener - findViewById(getSheet2PeekViewResId()) - .setOnClickListener(v -> bottomSheetBehavior2.setState(BottomSheetBehavior.STATE_EXPANDED)); - - // FIXED: - // issue was bottomSheetBehavior1 is taking drag event when getSheet2PeekView is dragging - // so detect touch event getSheet2PeekView set bottomSheetBehavior1 dragging false and bottomSheetBehavior2 true - findViewById(getSheet2PeekViewResId()).setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - Log.e(TAG, "onTouch: "); - bottomSheetBehavior1.setAllowDragging(false); - bottomSheetBehavior2.setAllowDragging(true); - return false; - } - }); - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index 11f22af6..7cec24c4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -9,11 +9,9 @@ import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.MiniPlayerFragment @@ -57,8 +55,6 @@ import code.name.monkey.retromusic.views.BottomNavigationBarTinted import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.android.synthetic.main.sliding_music_panel_layout.bottomNavigationView import kotlinx.android.synthetic.main.sliding_music_panel_layout.dimBackground -import kotlinx.android.synthetic.main.sliding_music_panel_layout.mainContent -import kotlinx.android.synthetic.main.sliding_music_panel_layout.sheet2Container import kotlinx.android.synthetic.main.sliding_music_panel_layout.slidingPanel abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlayerFragment.Callbacks { @@ -75,7 +71,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay private var lightStatusBar: Boolean = false private var lightNavigationBar: Boolean = false private var navigationBarColorAnimator: ValueAnimator? = null - private lateinit var queueAdapter: PlayingQueueAdapter protected abstract fun createContentView(): View private val panelState: Int get() = bottomSheetBehavior.state @@ -117,10 +112,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay val themeColor = ATHUtil.resolveColor(this, android.R.attr.windowBackground, Color.GRAY) dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f)) - - queueAdapter = PlayingQueueAdapter(this, ArrayList(), MusicPlayerRemote.position, R.layout.item_queue) - sheet2Container.adapter = queueAdapter - sheet2Container.layoutManager = LinearLayoutManager(this) } override fun onResume() { @@ -279,7 +270,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay override fun onGlobalLayout() { slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this) hideBottomBar(false) - queueAdapter.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) } }) } // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout @@ -288,7 +278,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay override fun onQueueChanged() { super.onQueueChanged() hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) - queueAdapter.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) } override fun onBackPressed() { @@ -296,14 +285,11 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay } open fun handleBackPress(): Boolean { - /*if (bottomSheetBehavior.peekHeight != 0 && playerFragment!!.onBackPressed()) return true + if (bottomSheetBehavior.peekHeight != 0 && playerFragment!!.onBackPressed()) return true if (panelState == BottomSheetBehavior.STATE_EXPANDED) { collapsePanel() return true - }*/ - - if (mainContent.consumeBackPress()) - return true + } return false } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsAsyncTask.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsAsyncTask.java index 27b307d7..70b70780 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsAsyncTask.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsAsyncTask.java @@ -21,30 +21,55 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.List; - import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.activities.saf.SAFGuideActivity; import code.name.monkey.retromusic.misc.DialogAsyncTask; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.util.SAFUtil; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; /** * Created by hemanths on 2019-07-31. */ public class DeleteSongsAsyncTask extends DialogAsyncTask { - private WeakReference dialogReference; + + public static class LoadingInfo { + + public Intent intent; + + public boolean isIntent; + + public int requestCode; + + public int resultCode; + + public List safUris; + + public List songs; + + 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; + } + } + private WeakReference activityWeakReference; + private WeakReference dialogReference; public DeleteSongsAsyncTask(@NonNull DeleteSongsDialog dialog) { super(dialog.getActivity()); @@ -82,7 +107,8 @@ public class DeleteSongsAsyncTask extends DialogAsyncTask 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; - } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt index d06b269f..267993db 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt @@ -31,7 +31,6 @@ import com.afollestad.materialdialogs.LayoutMode import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.bottomsheets.BottomSheet - class DeleteSongsDialog : DialogFragment() { @JvmField var currentSong: Song? = null @@ -40,7 +39,6 @@ class DeleteSongsDialog : DialogFragment() { private var deleteSongsAsyncTask: DeleteSongsAsyncTask? = null - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val songs: ArrayList? = arguments?.getParcelableArrayList("songs") var title = 0 @@ -95,7 +93,7 @@ class DeleteSongsDialog : DialogFragment() { } fun deleteSongs(songs: List, safUris: List?) { - MusicUtil.deleteTracks(activity!!, songs, safUris) { this.dismiss() } + MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() } } companion object { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java index f8d21d58..63c2d1b4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java @@ -27,21 +27,9 @@ import android.provider.BaseColumns; import android.provider.MediaStore; import android.text.TextUtils; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; - -import org.jaudiotagger.audio.AudioFileIO; -import org.jaudiotagger.tag.FieldKey; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Pattern; - import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.loaders.PlaylistLoader; @@ -51,76 +39,22 @@ import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics; import code.name.monkey.retromusic.service.MusicService; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; +import org.jaudiotagger.audio.AudioFileIO; +import org.jaudiotagger.tag.FieldKey; public class MusicUtil { public static final String TAG = MusicUtil.class.getSimpleName(); + private static Playlist playlist; - @NonNull - public static Uri getMediaStoreAlbumCoverUri(int albumId) { - final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); - return ContentUris.withAppendedId(sArtworkUri, albumId); - } - - public static Uri getSongFileUri(int songId) { - return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); - } - - @NonNull - public static Intent createShareSongFileIntent(@NonNull final Song song, @NonNull Context context) { - try { - return new Intent() - .setAction(Intent.ACTION_SEND) - .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName(), new File(song.getData()))) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setType("audio/*"); - } catch (IllegalArgumentException e) { - // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... - e.printStackTrace(); - Toast.makeText(context, "Could not share this file, I'm aware of the issue.", Toast.LENGTH_SHORT).show(); - return new Intent(); - } - } - - @NonNull - public static String getSongCountString(@NonNull final Context context, int songCount) { - final String songString = songCount == 1 ? context.getResources().getString(R.string.song) : context.getResources().getString(R.string.songs); - return songCount + " " + songString; - } - - @NonNull - public static String getSongInfoString(@NonNull Song song) { - return MusicUtil.buildInfoString( - song.getArtistName(), - song.getAlbumName() - ); - } - - @NonNull - public static String getArtistInfoString(@NonNull final Context context, - @NonNull final Artist artist) { - int albumCount = artist.getAlbumCount(); - int songCount = artist.getSongCount(); - String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) - : context.getResources().getString(R.string.albums); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return albumCount + " " + albumString + " • " + songCount + " " + songString; - } - - @NonNull - public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List songs) { - final long duration = getTotalDuration(songs); - - return MusicUtil.buildInfoString( - MusicUtil.getSongCountString(context, songs.size()), - MusicUtil.getReadableDurationString(duration) - ); - } - - /** * Build a concatenated string from the provided arguments * The intended purpose is to show extra annotations @@ -142,61 +76,40 @@ public class MusicUtil { return string1 + " • " + string2; } - public static String getReadableDurationString(long songDurationMillis) { - long minutes = (songDurationMillis / 1000) / 60; - long seconds = (songDurationMillis / 1000) % 60; - if (minutes < 60) { - return String.format(Locale.getDefault(), "%01d:%02d", minutes, seconds); - } else { - long hours = minutes / 60; - minutes = minutes % 60; - return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); - } - } - - //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. - //this method converts those values to normal tracknumbers - public static int getFixedTrackNumber(int trackNumberToFix) { - return trackNumberToFix % 1000; - } - - public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { - ContentResolver contentResolver = context.getContentResolver(); - - Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); - - ContentValues values = new ContentValues(); - values.put("album_id", albumId); - values.put("_data", path); - - contentResolver.insert(artworkUri, values); - } - @NonNull public static File createAlbumArtFile() { return new File(createAlbumArtDir(), String.valueOf(System.currentTimeMillis())); } @NonNull - @SuppressWarnings("ResultOfMethodCallIgnored") - private static File createAlbumArtDir() { - File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); - if (!albumArtDir.exists()) { - albumArtDir.mkdirs(); - try { - new File(albumArtDir, ".nomedia").createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } + public static Intent createShareSongFileIntent(@NonNull final Song song, @NonNull Context context) { + try { + return new Intent() + .setAction(Intent.ACTION_SEND) + .putExtra(Intent.EXTRA_STREAM, FileProvider + .getUriForFile(context, context.getApplicationContext().getPackageName(), + new File(song.getData()))) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .setType("audio/*"); + } catch (IllegalArgumentException e) { + // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... + e.printStackTrace(); + Toast.makeText(context, "Could not share this file, I'm aware of the issue.", Toast.LENGTH_SHORT).show(); + return new Intent(); } - return albumArtDir; } - public static void deleteTracks(@NonNull final Activity activity, - @NonNull final List songs, - @Nullable final List safUris, - @Nullable final Runnable callback) { + public static void deleteAlbumArt(@NonNull Context context, int albumId) { + ContentResolver contentResolver = context.getContentResolver(); + Uri localUri = Uri.parse("content://media/external/audio/albumart"); + contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); + } + + public static void deleteTracks( + @NonNull final Activity activity, + @NonNull final List songs, + @Nullable final List safUris, + @Nullable final Runnable callback) { final String[] projection = new String[]{ BaseColumns._ID, MediaStore.MediaColumns.DATA }; @@ -204,7 +117,8 @@ public class MusicUtil { // Split the query into multiple batches, and merge the resulting cursors int batchStart = 0; int batchEnd = 0; - final int batchSize = 1000000 / 10; // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID + final int batchSize = 1000000 + / 10; // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID final int songCount = songs.size(); while (batchEnd < songCount) { @@ -263,19 +177,31 @@ public class MusicUtil { activity.getContentResolver().notifyChange(Uri.parse("content://media"), null); activity.runOnUiThread(() -> { - Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songCount), Toast.LENGTH_SHORT).show(); + Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songCount), Toast.LENGTH_SHORT) + .show(); if (callback != null) { callback.run(); } }); } - public static void deleteAlbumArt(@NonNull Context context, int albumId) { - ContentResolver contentResolver = context.getContentResolver(); - Uri localUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); + @NonNull + public static String getArtistInfoString(@NonNull final Context context, + @NonNull final Artist artist) { + int albumCount = artist.getAlbumCount(); + int songCount = artist.getSongCount(); + String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) + : context.getResources().getString(R.string.albums); + String songString = songCount == 1 ? context.getResources().getString(R.string.song) + : context.getResources().getString(R.string.songs); + return albumCount + " " + albumString + " • " + songCount + " " + songString; } + //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. + //this method converts those values to normal tracknumbers + public static int getFixedTrackNumber(int trackNumberToFix) { + return trackNumberToFix % 1000; + } @Nullable public static String getLyrics(@NonNull Song song) { @@ -334,44 +260,41 @@ public class MusicUtil { return lyrics; } - public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { - if (isFavorite(context, song)) { - PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id); + @NonNull + public static Uri getMediaStoreAlbumCoverUri(int albumId) { + final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); + return ContentUris.withAppendedId(sArtworkUri, albumId); + } + + @NonNull + public static Playlist getPlaylist() { + return playlist; + } + + public static void setPlaylist(@NonNull Playlist playlist) { + MusicUtil.playlist = playlist; + } + + @NonNull + public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List songs) { + final long duration = getTotalDuration(songs); + + return MusicUtil.buildInfoString( + MusicUtil.getSongCountString(context, songs.size()), + MusicUtil.getReadableDurationString(duration) + ); + } + + public static String getReadableDurationString(long songDurationMillis) { + long minutes = (songDurationMillis / 1000) / 60; + long seconds = (songDurationMillis / 1000) % 60; + if (minutes < 60) { + return String.format(Locale.getDefault(), "%01d:%02d", minutes, seconds); } else { - PlaylistsUtil.addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).id, - false); + long hours = minutes / 60; + minutes = minutes % 60; + return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); } - context.sendBroadcast(new Intent(MusicService.FAVORITE_STATE_CHANGED)); - } - - public static boolean isFavoritePlaylist(@NonNull final Context context, - @NonNull final Playlist playlist) { - return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); - } - - private static Playlist getFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, context.getString(R.string.favorites)); - } - - private static Playlist getOrCreateFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, - PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); - } - - public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { - return PlaylistsUtil - .doPlaylistContains(context, getFavoritesPlaylist(context).id, song.getId()); - } - - public static boolean isArtistNameUnknown(@Nullable String artistName) { - if (TextUtils.isEmpty(artistName)) { - return false; - } - if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { - return true; - } - artistName = artistName.trim().toLowerCase(); - return artistName.equals("unknown") || artistName.equals(""); } @NonNull @@ -392,12 +315,22 @@ public class MusicUtil { } @NonNull - public static Playlist getPlaylist() { - return playlist; + public static String getSongCountString(@NonNull final Context context, int songCount) { + final String songString = songCount == 1 ? context.getResources().getString(R.string.song) + : context.getResources().getString(R.string.songs); + return songCount + " " + songString; } - public static void setPlaylist(@NonNull Playlist playlist) { - MusicUtil.playlist = playlist; + public static Uri getSongFileUri(int songId) { + return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); + } + + @NonNull + public static String getSongInfoString(@NonNull Song song) { + return MusicUtil.buildInfoString( + song.getArtistName(), + song.getAlbumName() + ); } public static long getTotalDuration(@NonNull List songs) { @@ -408,6 +341,11 @@ public class MusicUtil { return duration; } + @NonNull + public static String getYearString(int year) { + return year > 0 ? String.valueOf(year) : "-"; + } + public static int indexOfSongInList(@NonNull List songs, int songId) { for (int i = 0; i < songs.size(); i++) { if (songs.get(i).getId() == songId) { @@ -417,8 +355,71 @@ public class MusicUtil { return -1; } + public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { + ContentResolver contentResolver = context.getContentResolver(); + + Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); + contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); + + ContentValues values = new ContentValues(); + values.put("album_id", albumId); + values.put("_data", path); + + contentResolver.insert(artworkUri, values); + } + + public static boolean isArtistNameUnknown(@Nullable String artistName) { + if (TextUtils.isEmpty(artistName)) { + return false; + } + if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { + return true; + } + artistName = artistName.trim().toLowerCase(); + return artistName.equals("unknown") || artistName.equals(""); + } + + public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { + return PlaylistsUtil + .doPlaylistContains(context, getFavoritesPlaylist(context).id, song.getId()); + } + + public static boolean isFavoritePlaylist(@NonNull final Context context, + @NonNull final Playlist playlist) { + return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); + } + + public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { + if (isFavorite(context, song)) { + PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id); + } else { + PlaylistsUtil.addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).id, + false); + } + context.sendBroadcast(new Intent(MusicService.FAVORITE_STATE_CHANGED)); + } + @NonNull - public static String getYearString(int year) { - return year > 0 ? String.valueOf(year) : "-"; + @SuppressWarnings("ResultOfMethodCallIgnored") + private static File createAlbumArtDir() { + File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); + if (!albumArtDir.exists()) { + albumArtDir.mkdirs(); + try { + new File(albumArtDir, ".nomedia").createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return albumArtDir; + } + + private static Playlist getFavoritesPlaylist(@NonNull final Context context) { + return PlaylistLoader.INSTANCE.getPlaylist(context, context.getString(R.string.favorites)); + } + + private static Playlist getOrCreateFavoritesPlaylist(@NonNull final Context context) { + return PlaylistLoader.INSTANCE.getPlaylist(context, + PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); } } diff --git a/app/src/main/res/layout/sliding_music_panel_layout.xml b/app/src/main/res/layout/sliding_music_panel_layout.xml index 4e94a8e0..b8d3d257 100644 --- a/app/src/main/res/layout/sliding_music_panel_layout.xml +++ b/app/src/main/res/layout/sliding_music_panel_layout.xml @@ -1,5 +1,5 @@ - - + + android:layout_height="match_parent" /> - + - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file