Removed sliding panel for now

This commit is contained in:
h4h13 2020-01-24 22:57:43 +05:30
parent 2af26548ee
commit 636da3daa8
7 changed files with 223 additions and 542 deletions

View file

@ -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<V extends View> extends BottomSheetBehavior<V> {
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;
}
}

View file

@ -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;
}
});
}
}

View file

@ -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
}

View file

@ -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<DeleteSongsAsyncTask.LoadingInfo, Integer, Void> {
private WeakReference<DeleteSongsDialog> dialogReference;
public static class LoadingInfo {
public Intent intent;
public boolean isIntent;
public int requestCode;
public int resultCode;
public List<Uri> safUris;
public List<Song> songs;
public LoadingInfo(List<Song> songs, List<Uri> 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<FragmentActivity> activityWeakReference;
private WeakReference<DeleteSongsDialog> dialogReference;
public DeleteSongsAsyncTask(@NonNull DeleteSongsDialog dialog) {
super(dialog.getActivity());
@ -82,7 +107,8 @@ public class DeleteSongsAsyncTask extends DialogAsyncTask<DeleteSongsAsyncTask.L
if (SAFUtil.isSDCardAccessGranted(fragmentActivity)) {
dialog.deleteSongs(info.songs, null);
} else {
dialog.startActivityForResult(new Intent(fragmentActivity, SAFGuideActivity.class), SAFGuideActivity.REQUEST_CODE_SAF_GUIDE);
dialog.startActivityForResult(new Intent(fragmentActivity, SAFGuideActivity.class),
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE);
}
} else {
Log.i("Hmm", "doInBackground: kitkat delete songs");
@ -100,7 +126,8 @@ public class DeleteSongsAsyncTask extends DialogAsyncTask<DeleteSongsAsyncTask.L
break;
case SAFUtil.REQUEST_SAF_PICK_FILE:
if (info.resultCode == Activity.RESULT_OK) {
dialog.deleteSongs(Collections.singletonList(dialog.currentSong), Collections.singletonList(info.intent.getData()));
dialog.deleteSongs(Collections.singletonList(dialog.currentSong),
Collections.singletonList(info.intent.getData()));
}
break;
}
@ -111,28 +138,4 @@ public class DeleteSongsAsyncTask extends DialogAsyncTask<DeleteSongsAsyncTask.L
return null;
}
public static class LoadingInfo {
public boolean isIntent;
public List<Song> songs;
public List<Uri> safUris;
public int requestCode;
public int resultCode;
public Intent intent;
public LoadingInfo(List<Song> songs, List<Uri> 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;
}
}
}

View file

@ -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<Song>? = arguments?.getParcelableArrayList("songs")
var title = 0
@ -95,7 +93,7 @@ class DeleteSongsDialog : DialogFragment() {
}
fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) {
MusicUtil.deleteTracks(activity!!, songs, safUris) { this.dismiss() }
MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() }
}
companion object {

View file

@ -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<Song> 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<Song> songs,
@Nullable final List<Uri> 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<Song> songs,
@Nullable final List<Uri> 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<Song> 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("<unknown>");
}
@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<Song> 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<Song> 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("<unknown>");
}
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)));
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<code.name.monkey.retromusic.MultiSheetView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainContent"
@ -28,95 +28,21 @@
app:cardBackgroundColor="?attr/colorSurface"
app:cardCornerRadius="0dp"
app:cardUseCompatPadding="false"
app:layout_behavior="code.name.monkey.retromusic.CustomBottomSheetBehavior"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:strokeWidth="0dp">
<FrameLayout
android:id="@+id/playerFragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="2dp"
app:behavior_peekHeight="@dimen/bottom_sheet_peek_1_height"
app:layout_behavior="code.name.monkey.retromusic.CustomBottomSheetBehavior">
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/playerFragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/bottom_sheet_peek_2_height" />
<fragment
android:id="@+id/miniPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.MiniPlayerFragment"
android:layout_width="match_parent"
android:layout_height="48dp"
tools:layout="@layout/fragment_mini_player" />
<fragment
android:id="@+id/miniPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.MiniPlayerFragment"
android:layout_width="match_parent"
android:layout_height="48dp"
tools:layout="@layout/fragment_mini_player" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/sheet1Coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sheet2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:elevation="2dp"
android:orientation="vertical"
app:behavior_peekHeight="@dimen/bottom_sheet_peek_2_height"
app:layout_behavior="code.name.monkey.retromusic.CustomBottomSheetBehavior">
<FrameLayout
android:id="@+id/sheet2PeekView"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_peek_2_height"
android:elevation="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<code.name.monkey.retromusic.views.StatusBarView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/thumb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_keyboard_arrow_up_24dp"
app:tint="?attr/colorControlNormal" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/sheet2Container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/extendedToolbar" />
<FrameLayout
android:id="@+id/extendedToolbar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:elevation="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sheet2PeekView">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/collapseBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="14dp"
app:srcCompat="@drawable/ic_keyboard_backspace_black_24dp"
app:tint="?attr/colorControlNormal" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
<code.name.monkey.retromusic.views.BottomNavigationBarTinted
@ -134,4 +60,4 @@
app:labelVisibilityMode="labeled"
app:menu="@menu/bottom_navigation_main"
tools:layout_height="56dp" />
</code.name.monkey.retromusic.MultiSheetView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>