Removed sliding panel for now
This commit is contained in:
parent
2af26548ee
commit
636da3daa8
7 changed files with 223 additions and 542 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue