Conflicts:
	README.md
	app/build.gradle
	app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt
	app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt
	app/src/main/java/code/name/monkey/retromusic/fragments/player/lockscreen/LockScreenPlayerControlsFragment.kt
	app/src/main/java/code/name/monkey/retromusic/interfaces/CabHolder.kt
	app/src/main/java/code/name/monkey/retromusic/interfaces/MainActivityFragmentCallbacks.kt
	app/src/main/java/code/name/monkey/retromusic/interfaces/MusicServiceEventListener.kt
	app/src/main/java/code/name/monkey/retromusic/interfaces/PaletteColorHolder.kt
	app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt
	app/src/main/java/io/github/muntashirakon/music/App.kt
	app/src/main/java/io/github/muntashirakon/music/Constants.kt
	app/src/main/java/io/github/muntashirakon/music/HomeSection.kt
	app/src/main/java/io/github/muntashirakon/music/MainModule.kt
	app/src/main/java/io/github/muntashirakon/music/activities/DriveModeActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/LicenseActivity.java
	app/src/main/java/io/github/muntashirakon/music/activities/LockScreenActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/LyricsActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/MainActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/PermissionActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/PlayingQueueActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/SettingsActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/ShareInstagramStory.kt
	app/src/main/java/io/github/muntashirakon/music/activities/UserInfoActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/WhatsNewActivity.java
	app/src/main/java/io/github/muntashirakon/music/activities/base/AbsBaseActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/base/AbsMusicServiceActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/base/AbsSlidingMusicPanelActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/base/AbsThemeActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/bugreport/BugReportActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/bugreport/model/DeviceInfo.java
	app/src/main/java/io/github/muntashirakon/music/activities/bugreport/model/Report.java
	app/src/main/java/io/github/muntashirakon/music/activities/saf/SAFGuideActivity.java
	app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AbsTagEditorActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AlbumTagEditorActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/tageditor/SongTagEditorActivity.kt
	app/src/main/java/io/github/muntashirakon/music/activities/tageditor/WriteTagsAsyncTask.java
	app/src/main/java/io/github/muntashirakon/music/adapter/CategoryInfoAdapter.java
	app/src/main/java/io/github/muntashirakon/music/adapter/ContributorAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/GenreAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/HomeAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/SearchAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/SongFileAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/TranslatorsAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumCoverPagerAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/album/HorizontalAlbumAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/artist/ArtistAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/base/AbsMultiSelectAdapter.java
	app/src/main/java/io/github/muntashirakon/music/adapter/base/MediaEntryViewHolder.java
	app/src/main/java/io/github/muntashirakon/music/adapter/playlist/LegacyPlaylistAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/playlist/PlaylistAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/AbsOffsetSongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/OrderablePlaylistSongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/PlayingQueueAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/PlaylistSongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/ShuffleButtonSongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/SimpleSongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/adapter/song/SongAdapter.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutIconGenerator.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutLauncherActivity.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/DynamicShortcutManager.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/BaseShortcutType.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/LastAddedShortcutType.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/ShuffleAllShortcutType.kt
	app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/TopTracksShortcutType.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/AppWidgetBig.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/AppWidgetCard.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/AppWidgetClassic.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/AppWidgetSmall.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/AppWidgetText.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/BootReceiver.kt
	app/src/main/java/io/github/muntashirakon/music/appwidgets/base/BaseAppWidget.kt
	app/src/main/java/io/github/muntashirakon/music/db/BlackListStoreDao.kt
	app/src/main/java/io/github/muntashirakon/music/db/BlackListStoreEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/HistoryDao.kt
	app/src/main/java/io/github/muntashirakon/music/db/HistoryEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/LyricsDao.kt
	app/src/main/java/io/github/muntashirakon/music/db/LyricsEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/PlayCountDao.kt
	app/src/main/java/io/github/muntashirakon/music/db/PlayCountEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/PlaylistDao.kt
	app/src/main/java/io/github/muntashirakon/music/db/PlaylistEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/PlaylistWithSongs.kt
	app/src/main/java/io/github/muntashirakon/music/db/RetroDatabase.kt
	app/src/main/java/io/github/muntashirakon/music/db/SongEntity.kt
	app/src/main/java/io/github/muntashirakon/music/db/SongExtension.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/AddToPlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/BlacklistFolderChooserDialog.java
	app/src/main/java/io/github/muntashirakon/music/dialogs/CreatePlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/DeletePlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/DeleteSongsDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/ImportPlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/LyricsDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/RemoveSongFromPlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/RenamePlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/SavePlaylistDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/SleepTimerDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/SongDetailDialog.kt
	app/src/main/java/io/github/muntashirakon/music/dialogs/SongShareDialog.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/ActivityEx.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/ColorExt.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/CursorExtensions.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/DialogExtension.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/DimenExtension.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/DrawableExt.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/FragmentExt.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/NavigationExtensions.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/PaletteEX.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/Preference.kt
	app/src/main/java/io/github/muntashirakon/music/extensions/ViewExtensions.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/AlbumCoverStyle.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/CoroutineViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/DetailListFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/LibraryViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/MiniPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/NowPlayingScreen.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/VolumeFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/about/AboutFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMainActivityFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMusicServiceFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewCustomGridSizeFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/folder/FoldersFragment.java
	app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenresFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/home/HomeFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/library/LibraryFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/NowPlayingPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/PlayerAlbumCoverFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/adaptive/AdaptiveFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/blur/BlurPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/blur/BlurPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/card/CardFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/card/CardPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/cardblur/CardBlurFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/circle/CirclePlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/classic/ClassicPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/color/ColorFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/color/ColorPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/fit/FitFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/fit/FitPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/flat/FlatPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/flat/FlatPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/gradient/GradientPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/home/HomePlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/lockscreen/LockScreenControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/lockscreen/LockScreenPlayerControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/material/MaterialControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/material/MaterialFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/peak/PeakPlayerControlFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/peak/PeakPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/plain/PlainPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/plain/PlainPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/simple/SimplePlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/simple/SimplePlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/tiny/TinyPlaybackControlsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/player/tiny/TinyPlayerFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchViewModel.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/AbsSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/AudioSettings.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/ImageSettingFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/MainSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/NotificationSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/NowPlayingSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/OtherSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/PersonalizeSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/settings/ThemeSettingsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/fragments/songs/SongsFragment.kt
	app/src/main/java/io/github/muntashirakon/music/glide/AlbumGlideRequest.java
	app/src/main/java/io/github/muntashirakon/music/glide/ArtistGlideRequest.java
	app/src/main/java/io/github/muntashirakon/music/glide/BlurTransformation.kt
	app/src/main/java/io/github/muntashirakon/music/glide/ProfileBannerGlideRequest.java
	app/src/main/java/io/github/muntashirakon/music/glide/RetroMusicColoredTarget.kt
	app/src/main/java/io/github/muntashirakon/music/glide/RetroMusicGlideModule.kt
	app/src/main/java/io/github/muntashirakon/music/glide/SingleColorTarget.kt
	app/src/main/java/io/github/muntashirakon/music/glide/SongGlideRequest.java
	app/src/main/java/io/github/muntashirakon/music/glide/UserProfileGlideRequest.java
	app/src/main/java/io/github/muntashirakon/music/glide/artistimage/ArtistImageLoader.kt
	app/src/main/java/io/github/muntashirakon/music/glide/palette/BitmapPaletteTranscoder.java
	app/src/main/java/io/github/muntashirakon/music/helper/HorizontalAdapterHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/MusicPlayerRemote.kt
	app/src/main/java/io/github/muntashirakon/music/helper/MusicProgressViewUpdateHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/PlayPauseButtonOnClickHandler.kt
	app/src/main/java/io/github/muntashirakon/music/helper/SearchQueryHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/ShuffleHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/StopWatch.kt
	app/src/main/java/io/github/muntashirakon/music/helper/menu/GenreMenuHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/menu/PlaylistMenuHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/menu/SongMenuHelper.kt
	app/src/main/java/io/github/muntashirakon/music/helper/menu/SongsMenuHelper.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/CabHolder.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/Callbacks.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/ICabHolder.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/IMainActivityFragmentCallbacks.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/IMusicServiceEventListener.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/IPaletteColorHolder.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/MainActivityFragmentCallbacks.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/MusicServiceEventListener.kt
	app/src/main/java/io/github/muntashirakon/music/interfaces/PaletteColorHolder.kt
	app/src/main/java/io/github/muntashirakon/music/lyrics/LrcView.java
	app/src/main/java/io/github/muntashirakon/music/model/Artist.kt
	app/src/main/java/io/github/muntashirakon/music/model/lyrics/Lyrics.java
	app/src/main/java/io/github/muntashirakon/music/providers/BlacklistStore.java
	app/src/main/java/io/github/muntashirakon/music/providers/MusicPlaybackQueueStore.java
	app/src/main/java/io/github/muntashirakon/music/repository/GenreRepository.kt
	app/src/main/java/io/github/muntashirakon/music/repository/PlaylistSongsLoader.kt
	app/src/main/java/io/github/muntashirakon/music/repository/Repository.kt
	app/src/main/java/io/github/muntashirakon/music/repository/RoomRepository.kt
	app/src/main/java/io/github/muntashirakon/music/repository/SongRepository.kt
	app/src/main/java/io/github/muntashirakon/music/service/MultiPlayer.java
	app/src/main/java/io/github/muntashirakon/music/service/MusicService.java
	app/src/main/java/io/github/muntashirakon/music/service/PlaybackHandler.java
	app/src/main/java/io/github/muntashirakon/music/util/FileUtil.java
	app/src/main/java/io/github/muntashirakon/music/util/NavigationUtil.java
	app/src/main/java/io/github/muntashirakon/music/util/PlaylistsUtil.java
	app/src/main/java/io/github/muntashirakon/music/util/PreferenceUtil.kt
	app/src/main/java/io/github/muntashirakon/music/util/RetroUtil.java
	app/src/main/java/io/github/muntashirakon/music/util/SAFUtil.java
	app/src/main/java/io/github/muntashirakon/music/util/color/MediaNotificationProcessor.java
	app/src/main/java/io/github/muntashirakon/music/util/color/NotificationColorUtil.java
	app/src/main/java/io/github/muntashirakon/music/views/BaselineGridTextView.java
	app/src/main/java/io/github/muntashirakon/music/views/BreadCrumbLayout.java
	app/src/main/java/io/github/muntashirakon/music/views/CircularImageView.java
	app/src/main/java/io/github/muntashirakon/music/views/ContributorsView.java
	app/src/main/java/io/github/muntashirakon/music/views/NetworkImageView.java
	app/src/main/java/io/github/muntashirakon/music/views/SeekArc.java
	app/src/main/res/layout-land/fragment_home.xml
	app/src/main/res/layout-xlarge-land/fragment_blur.xml
	app/src/main/res/layout/activity_lock_screen.xml
	app/src/main/res/layout/activity_user_info.xml
	app/src/main/res/layout/fragment_banner_home.xml
	app/src/main/res/layout/fragment_classic_player.xml
	app/src/main/res/layout/fragment_folder.xml
	app/src/main/res/layout/fragment_home.xml
	app/src/main/res/layout/item_image.xml
	app/src/main/res/layout/sliding_music_panel_layout.xml
	app/src/main/res/navigation/now_playing.xml
This commit is contained in:
Muntashir Al-Islam 2020-10-15 19:16:23 +06:00
commit 3c0fc790d1
442 changed files with 18453 additions and 14559 deletions

View file

@ -23,327 +23,300 @@ import android.net.Uri;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.github.muntashirakon.music.R;
import io.github.muntashirakon.music.service.playback.Playback;
import io.github.muntashirakon.music.util.PreferenceUtil;
/**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/
public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
/** @author Andrew Neal, Karim Abou Zeid (kabouzeid) */
public class MultiPlayer
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer;
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer;
private Context context;
@Nullable
private Playback.PlaybackCallbacks callbacks;
private Context context;
@Nullable private Playback.PlaybackCallbacks callbacks;
private boolean mIsInitialized = false;
private boolean mIsInitialized = false;
/**
* Constructor of <code>MultiPlayer</code>
*/
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
/** Constructor of <code>MultiPlayer</code> */
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
}
/**
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
@Override
public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
if (mIsInitialized) {
setNextDataSource(null);
}
return mIsInitialized;
}
/**
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
* @return True if the <code>player</code> has been prepared and is
* ready to play, false otherwise
*/
@Override
public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
if (mIsInitialized) {
setNextDataSource(null);
}
return mIsInitialized;
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
* @return True if the <code>player</code> has been prepared and is ready to play, false otherwise
*/
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
if (context == null) {
return false;
}
try {
player.reset();
player.setOnPreparedListener(null);
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path));
} else {
player.setDataSource(path);
}
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
return false;
}
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
context.sendBroadcast(intent);
return true;
}
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
* @return True if the <code>player</code> has been prepared and is
* ready to play, false otherwise
*/
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
if (context == null) {
return false;
}
/**
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
*
* @param path The path of the file, or the http/rtsp URL of the stream you want to play
*/
@Override
public void setNextDataSource(@Nullable final String path) {
if (context == null) {
return;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(null);
} catch (IllegalArgumentException e) {
Log.i(TAG, "Next media player is current one, continuing");
} catch (IllegalStateException e) {
Log.e(TAG, "Media player not initialized!");
return;
}
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
if (path == null) {
return;
}
if (PreferenceUtil.INSTANCE.isGapLessPlayback()) {
mNextMediaPlayer = new MediaPlayer();
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) {
try {
player.reset();
player.setOnPreparedListener(null);
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path));
} else {
player.setDataSource(path);
}
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
return false;
}
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
context.sendBroadcast(intent);
return true;
}
/**
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
*
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
*/
@Override
public void setNextDataSource(@Nullable final String path) {
if (context == null) {
return;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(null);
} catch (IllegalArgumentException e) {
Log.i(TAG, "Next media player is current one, continuing");
} catch (IllegalStateException e) {
Log.e(TAG, "Media player not initialized!");
return;
}
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
if (path == null) {
return;
}
if (PreferenceUtil.INSTANCE.isGapLessPlayback()) {
mNextMediaPlayer = new MediaPlayer();
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) {
try {
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
} else {
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
}
}
/**
* Sets the callbacks
*
* @param callbacks The callbacks to use
*/
@Override
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
}
/**
* @return True if the player is ready to go, false otherwise
*/
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/**
* Starts or resumes playback.
*/
@Override
public boolean start() {
try {
mCurrentMediaPlayer.start();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Resets the MediaPlayer to its uninitialized state.
*/
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/**
* Releases resources associated with this MediaPlayer object.
*/
@Override
public void release() {
stop();
mCurrentMediaPlayer.release();
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
}
}
/**
* Pauses playback. Call start() to resume.
*/
@Override
public boolean pause() {
try {
mCurrentMediaPlayer.pause();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Checks whether the MultiPlayer is playing.
*/
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
}
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
@Override
public int duration() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getDuration();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @return The current position in milliseconds
*/
@Override
public int position() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to
*/
@Override
public int seek(final int whereto) {
try {
mCurrentMediaPlayer.seekTo(whereto);
return whereto;
} catch (IllegalStateException e) {
return -1;
}
}
@Override
public boolean setVolume(final float vol) {
try {
mCurrentMediaPlayer.setVolume(vol, vol);
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Sets the audio session ID.
*
* @param sessionId The audio session ID
*/
@Override
public boolean setAudioSessionId(final int sessionId) {
try {
mCurrentMediaPlayer.setAudioSessionId(sessionId);
return true;
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
return false;
}
}
/**
* Returns the audio session ID.
*
* @return The current audio session ID.
*/
@Override
public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId();
}
/**
* {@inheritDoc}
*/
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer();
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
if (context != null) {
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true;
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
if (callbacks != null)
callbacks.onTrackWentToNext();
} else {
if (callbacks != null)
callbacks.onTrackEnded();
}
}
} else {
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
}
}
/**
* Sets the callbacks
*
* @param callbacks The callbacks to use
*/
@Override
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
}
}
/** @return True if the player is ready to go, false otherwise */
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/** Starts or resumes playback. */
@Override
public boolean start() {
try {
mCurrentMediaPlayer.start();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/** Resets the MediaPlayer to its uninitialized state. */
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/** Releases resources associated with this MediaPlayer object. */
@Override
public void release() {
stop();
mCurrentMediaPlayer.release();
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
}
}
/** Pauses playback. Call start() to resume. */
@Override
public boolean pause() {
try {
mCurrentMediaPlayer.pause();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/** Checks whether the MultiPlayer is playing. */
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
}
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
@Override
public int duration() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getDuration();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @return The current position in milliseconds
*/
@Override
public int position() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to
*/
@Override
public int seek(final int whereto) {
try {
mCurrentMediaPlayer.seekTo(whereto);
return whereto;
} catch (IllegalStateException e) {
return -1;
}
}
@Override
public boolean setVolume(final float vol) {
try {
mCurrentMediaPlayer.setVolume(vol, vol);
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Sets the audio session ID.
*
* @param sessionId The audio session ID
*/
@Override
public boolean setAudioSessionId(final int sessionId) {
try {
mCurrentMediaPlayer.setAudioSessionId(sessionId);
return true;
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
return false;
}
}
/**
* Returns the audio session ID.
*
* @return The current audio session ID.
*/
@Override
public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId();
}
/** {@inheritDoc} */
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer();
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
if (context != null) {
Toast.makeText(
context,
context.getResources().getString(R.string.unplayable_file),
Toast.LENGTH_SHORT)
.show();
}
return false;
}
/** {@inheritDoc} */
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp.equals(mCurrentMediaPlayer) && mNextMediaPlayer != null) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true;
mNextMediaPlayer = null;
if (callbacks != null) callbacks.onTrackWentToNext();
} else {
if (callbacks != null) callbacks.onTrackEnded();
}
}
}

View file

@ -63,6 +63,7 @@ import java.util.Objects;
import java.util.Random;
import io.github.muntashirakon.music.R;
import io.github.muntashirakon.music.activities.LockScreenActivity;
import io.github.muntashirakon.music.appwidgets.AppWidgetBig;
import io.github.muntashirakon.music.appwidgets.AppWidgetCard;
import io.github.muntashirakon.music.appwidgets.AppWidgetClassic;
@ -71,8 +72,10 @@ import io.github.muntashirakon.music.appwidgets.AppWidgetText;
import io.github.muntashirakon.music.glide.BlurTransformation;
import io.github.muntashirakon.music.glide.SongGlideRequest;
import io.github.muntashirakon.music.helper.ShuffleHelper;
import io.github.muntashirakon.music.model.AbsCustomPlaylist;
import io.github.muntashirakon.music.model.Playlist;
import io.github.muntashirakon.music.model.Song;
import io.github.muntashirakon.music.model.smartplaylist.AbsSmartPlaylist;
import io.github.muntashirakon.music.providers.HistoryStore;
import io.github.muntashirakon.music.providers.MusicPlaybackQueueStore;
import io.github.muntashirakon.music.providers.SongPlayCountStore;
@ -94,8 +97,8 @@ import static io.github.muntashirakon.music.ConstantsKt.TOGGLE_HEADSET;
/**
* @author Karim Abou Zeid (kabouzeid), Andrew Neal
*/
public class MusicService extends Service implements
SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public class MusicService extends Service
implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks {
public static final String TAG = MusicService.class.getSimpleName();
public static final String RETRO_MUSIC_PACKAGE_NAME = "io.github.muntashirakon.music";
@ -109,17 +112,22 @@ public class MusicService extends Service implements
public static final String ACTION_REWIND = RETRO_MUSIC_PACKAGE_NAME + ".rewind";
public static final String ACTION_QUIT = RETRO_MUSIC_PACKAGE_NAME + ".quitservice";
public static final String ACTION_PENDING_QUIT = RETRO_MUSIC_PACKAGE_NAME + ".pendingquitservice";
public static final String INTENT_EXTRA_PLAYLIST = RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist";
public static final String INTENT_EXTRA_SHUFFLE_MODE = RETRO_MUSIC_PACKAGE_NAME + ".intentextra.shufflemode";
public static final String INTENT_EXTRA_PLAYLIST =
RETRO_MUSIC_PACKAGE_NAME + "intentextra.playlist";
public static final String INTENT_EXTRA_SHUFFLE_MODE =
RETRO_MUSIC_PACKAGE_NAME + ".intentextra.shufflemode";
public static final String APP_WIDGET_UPDATE = RETRO_MUSIC_PACKAGE_NAME + ".appwidgetupdate";
public static final String EXTRA_APP_WIDGET_NAME = RETRO_MUSIC_PACKAGE_NAME + "app_widget_name";
// Do not change these three strings as it will break support with other apps (e.g. last.fm scrobbling)
// Do not change these three strings as it will break support with other apps (e.g. last.fm
// scrobbling)
public static final String META_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".metachanged";
public static final String QUEUE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".queuechanged";
public static final String PLAY_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".playstatechanged";
public static final String FAVORITE_STATE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + "favoritestatechanged";
public static final String FAVORITE_STATE_CHANGED =
RETRO_MUSIC_PACKAGE_NAME + "favoritestatechanged";
public static final String REPEAT_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".repeatmodechanged";
public static final String SHUFFLE_MODE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".shufflemodechanged";
public static final String SHUFFLE_MODE_CHANGED =
RETRO_MUSIC_PACKAGE_NAME + ".shufflemodechanged";
public static final String MEDIA_STORE_CHANGED = RETRO_MUSIC_PACKAGE_NAME + ".mediastorechanged";
public static final String CYCLE_REPEAT = RETRO_MUSIC_PACKAGE_NAME + ".cyclerepeat";
public static final String TOGGLE_SHUFFLE = RETRO_MUSIC_PACKAGE_NAME + ".toggleshuffle";
@ -144,13 +152,14 @@ public class MusicService extends Service implements
public static final int REPEAT_MODE_ALL = 1;
public static final int REPEAT_MODE_THIS = 2;
public static final int SAVE_QUEUES = 0;
private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
| PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SEEK_TO;
private static final long MEDIA_SESSION_ACTIONS =
PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
| PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SEEK_TO;
private final IBinder musicBind = new MusicBinder();
public int nextPosition = -1;
@ -171,43 +180,44 @@ public class MusicService extends Service implements
private AppWidgetText appWidgetText = AppWidgetText.Companion.getInstance();
private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME);
final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (command != null) {
switch (command) {
case AppWidgetClassic.NAME: {
appWidgetClassic.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetSmall.NAME: {
appWidgetSmall.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetBig.NAME: {
appWidgetBig.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetCard.NAME: {
appWidgetCard.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetText.NAME: {
appWidgetText.performUpdate(MusicService.this, ids);
break;
private final BroadcastReceiver widgetIntentReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME);
final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (command != null) {
switch (command) {
case AppWidgetClassic.NAME: {
appWidgetClassic.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetSmall.NAME: {
appWidgetSmall.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetBig.NAME: {
appWidgetBig.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetCard.NAME: {
appWidgetCard.performUpdate(MusicService.this, ids);
break;
}
case AppWidgetText.NAME: {
appWidgetText.performUpdate(MusicService.this, ids);
break;
}
}
}
}
}
}
};
};
private AudioManager audioManager;
private IntentFilter becomingNoisyReceiverIntentFilter = new IntentFilter(
AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private IntentFilter becomingNoisyReceiverIntentFilter =
new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private boolean becomingNoisyReceiverRegistered;
private IntentFilter bluetoothConnectedIntentFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
private IntentFilter bluetoothConnectedIntentFilter =
new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
private boolean bluetoothConnectedRegistered = false;
private IntentFilter headsetReceiverIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
private boolean headsetReceiverRegistered = false;
@ -219,96 +229,112 @@ public class MusicService extends Service implements
private List<Song> playingQueue = new ArrayList<>();
private boolean pausedByTransientLossOfFocus;
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() != null && intent.getAction().equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
pause();
}
}
};
private final BroadcastReceiver becomingNoisyReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, @NonNull Intent intent) {
if (intent.getAction() != null
&& intent.getAction().equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
pause();
}
}
};
private PlaybackHandler playerHandler;
private final AudioManager.OnAudioFocusChangeListener audioFocusListener
= new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(final int focusChange) {
playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget();
}
};
private final AudioManager.OnAudioFocusChangeListener audioFocusListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(final int focusChange) {
playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget();
}
};
private PlayingNotification playingNotification;
private final BroadcastReceiver updateFavoriteReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
updateNotification();
}
};
private final BroadcastReceiver updateFavoriteReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
updateNotification();
}
};
private final BroadcastReceiver lockScreenReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (PreferenceUtil.INSTANCE.isLockScreen() && isPlaying()) {
Intent lockIntent = new Intent(context, LockScreenActivity.class);
lockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(lockIntent);
}
}
};
private QueueSaveHandler queueSaveHandler;
private HandlerThread queueSaveHandlerThread;
private boolean queuesRestored;
private int repeatMode;
private int shuffleMode;
private SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper();
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
String action = intent.getAction();
if (action != null) {
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action) &&
PreferenceUtil.INSTANCE.isBluetoothSpeaker()) {
if (VERSION.SDK_INT >= VERSION_CODES.M) {
if (getAudioManager().getDevices(AudioManager.GET_DEVICES_OUTPUTS).length > 0) {
play();
}
} else {
if (getAudioManager().isBluetoothA2dpOn()) {
play();
private final BroadcastReceiver bluetoothReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
String action = intent.getAction();
if (action != null) {
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)
&& PreferenceUtil.INSTANCE.isBluetoothSpeaker()) {
if (VERSION.SDK_INT >= VERSION_CODES.M) {
if (getAudioManager().getDevices(AudioManager.GET_DEVICES_OUTPUTS).length > 0) {
play();
}
} else {
if (getAudioManager().isBluetoothA2dpOn()) {
play();
}
}
}
}
}
}
}
};
private PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
//Not in call: Play music
play();
break;
case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK:
//A call is dialing, active or on hold
pause();
break;
default:
}
super.onCallStateChanged(state, incomingNumber);
}
};
private BroadcastReceiver headsetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
int state = intent.getIntExtra("state", -1);
};
private PhoneStateListener phoneStateListener =
new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case 0:
case TelephonyManager.CALL_STATE_IDLE:
// Not in call: Play music
play();
break;
case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK:
// A call is dialing, active or on hold
pause();
break;
case 1:
play();
break;
default:
}
super.onCallStateChanged(state, incomingNumber);
}
};
private BroadcastReceiver headsetReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
pause();
break;
case 1:
play();
break;
}
}
}
}
}
}
};
};
private ThrottledSeekHandler throttledSeekHandler;
private Handler uiThreadHandler;
private PowerManager.WakeLock wakeLock;
@ -333,7 +359,8 @@ public class MusicService extends Service implements
@Override
public void onCreate() {
super.onCreate();
final TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
final TelephonyManager telephonyManager =
(TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if (telephonyManager != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
@ -353,8 +380,10 @@ public class MusicService extends Service implements
setupMediaSession();
// queue saving needs to run on a separate thread so that it doesn't block the playback handler events
queueSaveHandlerThread = new HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND);
// queue saving needs to run on a separate thread so that it doesn't block the playback handler
// events
queueSaveHandlerThread =
new HandlerThread("QueueSaveHandler", Process.THREAD_PRIORITY_BACKGROUND);
queueSaveHandlerThread.start();
queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper());
@ -362,38 +391,49 @@ public class MusicService extends Service implements
registerReceiver(widgetIntentReceiver, new IntentFilter(APP_WIDGET_UPDATE));
registerReceiver(updateFavoriteReceiver, new IntentFilter(FAVORITE_STATE_CHANGED));
registerReceiver(lockScreenReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
initNotification();
mediaStoreObserver = new MediaStoreObserver(this, playerHandler);
throttledSeekHandler = new ThrottledSeekHandler(this, playerHandler);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Albums.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Albums.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Artists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Artists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Genres.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Genres.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
getContentResolver()
.registerContentObserver(MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
.registerContentObserver(
MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver);
PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this);
restoreState();
sendBroadcast(new Intent(RETRO_MUSIC_PACKAGE_NAME + ".RETRO_MUSIC_SERVICE_CREATED"));
sendBroadcast(new Intent("io.github.muntashirakon.music.RETRO_MUSIC_SERVICE_CREATED"));
registerHeadsetEvents();
registerBluetoothConnected();
@ -403,6 +443,7 @@ public class MusicService extends Service implements
public void onDestroy() {
unregisterReceiver(widgetIntentReceiver);
unregisterReceiver(updateFavoriteReceiver);
unregisterReceiver(lockScreenReceiver);
if (becomingNoisyReceiverRegistered) {
unregisterReceiver(becomingNoisyReceiver);
becomingNoisyReceiverRegistered = false;
@ -422,7 +463,7 @@ public class MusicService extends Service implements
PreferenceUtil.INSTANCE.unregisterOnSharedPreferenceChangedListener(this);
wakeLock.release();
sendBroadcast(new Intent(RETRO_MUSIC_PACKAGE_NAME + ".RETRO_MUSIC_SERVICE_DESTROYED"));
sendBroadcast(new Intent("io.github.muntashirakon.music.RETRO_MUSIC_SERVICE_DESTROYED"));
}
public void acquireWakeLock(long milli) {
@ -591,7 +632,8 @@ public class MusicService extends Service implements
case REPEAT_MODE_ALL:
case REPEAT_MODE_THIS:
this.repeatMode = repeatMode;
PreferenceManager.getDefaultSharedPreferences(this).edit()
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putInt(SAVED_REPEAT_MODE, repeatMode)
.apply();
prepareNext();
@ -605,7 +647,8 @@ public class MusicService extends Service implements
}
public void setShuffleMode(final int shuffleMode) {
PreferenceManager.getDefaultSharedPreferences(this).edit()
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putInt(SAVED_SHUFFLE_MODE, shuffleMode)
.apply();
switch (shuffleMode) {
@ -664,8 +707,8 @@ public class MusicService extends Service implements
}
public void initNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
!PreferenceUtil.INSTANCE.isClassicNotification()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& !PreferenceUtil.INSTANCE.isClassicNotification()) {
playingNotification = new PlayingNotificationImpl();
} else {
playingNotification = new PlayingNotificationOreo();
@ -725,7 +768,8 @@ public class MusicService extends Service implements
}
@Override
public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, @NonNull String key) {
public void onSharedPreferenceChanged(
@NonNull SharedPreferences sharedPreferences, @NonNull String key) {
switch (key) {
case GAP_LESS_PLAYBACK:
if (sharedPreferences.getBoolean(key, false)) {
@ -817,10 +861,14 @@ public class MusicService extends Service implements
return true;
}
public void openQueue(@Nullable final List<Song> playingQueue, final int startPosition,
final boolean startPlaying) {
if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue
.size()) {
public void openQueue(
@Nullable final List<Song> playingQueue,
final int startPosition,
final boolean startPlaying) {
if (playingQueue != null
&& !playingQueue.isEmpty()
&& startPosition >= 0
&& startPosition < playingQueue.size()) {
// it is important to copy the playing queue here first as we might add/remove songs later
originalPlayingQueue = new ArrayList<>(playingQueue);
this.playingQueue = new ArrayList<>(originalPlayingQueue);
@ -878,13 +926,15 @@ public class MusicService extends Service implements
}
notifyChange(PLAY_STATE_CHANGED);
// fixes a bug where the volume would stay ducked because the AudioManager.AUDIOFOCUS_GAIN event is not sent
// fixes a bug where the volume would stay ducked because the
// AudioManager.AUDIOFOCUS_GAIN event is not sent
playerHandler.removeMessages(DUCK);
playerHandler.sendEmptyMessage(UNDUCK);
}
}
} else {
Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT)
Toast.makeText(
this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT)
.show();
}
}
@ -908,7 +958,8 @@ public class MusicService extends Service implements
if (openTrackAndPrepareNextAt(position)) {
play();
} else {
Toast.makeText(this, getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
Toast.makeText(this, getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT)
.show();
}
}
@ -988,12 +1039,15 @@ public class MusicService extends Service implements
public synchronized void restoreQueuesAndPositionIfNecessary() {
if (!queuesRestored && playingQueue.isEmpty()) {
List<Song> restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue();
List<Song> restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue();
int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1);
int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this)
.getInt(SAVED_POSITION_IN_TRACK, -1);
List<Song> restoredOriginalQueue =
MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue();
int restoredPosition =
PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1);
int restoredPositionInTrack =
PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1);
if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size()
if (restoredQueue.size() > 0
&& restoredQueue.size() == restoredOriginalQueue.size()
&& restoredPosition != -1) {
this.originalPlayingQueue = restoredOriginalQueue;
this.playingQueue = restoredQueue;
@ -1019,8 +1073,10 @@ public class MusicService extends Service implements
}
public void savePositionInTrack() {
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putInt(SAVED_POSITION_IN_TRACK, getSongProgressMillis()).apply();
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putInt(SAVED_POSITION_IN_TRACK, getSongProgressMillis())
.apply();
}
public void saveQueuesImpl() {
@ -1076,10 +1132,13 @@ public class MusicService extends Service implements
}
public void updateMediaSessionPlaybackState() {
PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
.setActions(MEDIA_SESSION_ACTIONS)
.setState(isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
getSongProgressMillis(), 1);
PlaybackStateCompat.Builder stateBuilder =
new PlaybackStateCompat.Builder()
.setActions(MEDIA_SESSION_ACTIONS)
.setState(
isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
getSongProgressMillis(),
1);
setCustomAction(stateBuilder);
@ -1092,7 +1151,8 @@ public class MusicService extends Service implements
}
}
void updateMediaSessionMetaData() {
public void updateMediaSessionMetaData() {
Log.i(TAG, "onResourceReady: ");
final Song song = getCurrentSong();
if (song.getId() == -1) {
@ -1108,13 +1168,15 @@ public class MusicService extends Service implements
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.getDuration())
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1)
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.getYear())
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null)
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size());
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size());
}
if (PreferenceUtil.INSTANCE.isAlbumArtOnLockScreen()) {
final Point screenSize = RetroUtil.getScreenSize(MusicService.this);
final BitmapRequestBuilder<?, Bitmap> request = SongGlideRequest.Builder
.from(Glide.with(MusicService.this), song)
final BitmapRequestBuilder<?, Bitmap> request = SongGlideRequest.Builder.from(Glide.with(MusicService.this), song)
.checkIgnoreMediaStore(MusicService.this)
.asBitmap().build();
if (PreferenceUtil.INSTANCE.isBlurredAlbumArt()) {
@ -1132,6 +1194,7 @@ public class MusicService extends Service implements
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
metaData.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, copy(resource));
mediaSession.setMetadata(metaData.build());
}
@ -1144,7 +1207,8 @@ public class MusicService extends Service implements
}
private void closeAudioEffectSession() {
final Intent audioEffectsIntent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
final Intent audioEffectsIntent =
new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
if (playback != null) {
audioEffectsIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, playback.getAudioSessionId());
}
@ -1177,15 +1241,11 @@ public class MusicService extends Service implements
savePosition();
savePositionInTrack();
final Song currentSong = getCurrentSong();
if (currentSong != null) {
HistoryStore.getInstance(this).addSongId(currentSong.getId());
}
HistoryStore.getInstance(this).addSongId(currentSong.getId());
if (songPlayCountHelper.shouldBumpPlayCount()) {
SongPlayCountStore.getInstance(this).bumpPlayCount(songPlayCountHelper.getSong().getId());
}
if (currentSong != null) {
songPlayCountHelper.notifySongChanged(currentSong);
}
songPlayCountHelper.notifySongChanged(currentSong);
break;
case QUEUE_CHANGED:
updateMediaSessionMetaData(); // because playing queue size might have changed
@ -1213,10 +1273,10 @@ public class MusicService extends Service implements
}
private void playFromPlaylist(Intent intent) {
Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST);
AbsSmartPlaylist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST);
int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode());
if (playlist != null) {
List<Song> playlistSongs = playlist.getSongs();
List<Song> playlistSongs = playlist.songs();
if (!playlistSongs.isEmpty()) {
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
int startPosition = new Random().nextInt(playlistSongs.size());
@ -1226,7 +1286,8 @@ public class MusicService extends Service implements
openQueue(playlistSongs, 0, true);
}
} else {
Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG)
.show();
}
} else {
Toast.makeText(getApplicationContext(), R.string.playlist_is_empty, Toast.LENGTH_LONG).show();
@ -1280,7 +1341,8 @@ public class MusicService extends Service implements
private boolean requestFocus() {
return (getAudioManager()
.requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
.requestAudioFocus(
audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
== AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}
@ -1295,7 +1357,10 @@ public class MusicService extends Service implements
}
private void savePosition() {
PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, getPosition()).apply();
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putInt(SAVED_POSITION, getPosition())
.apply();
}
private void saveQueues() {
@ -1313,56 +1378,60 @@ public class MusicService extends Service implements
}
private void setCustomAction(PlaybackStateCompat.Builder stateBuilder) {
int repeatIcon = R.drawable.ic_repeat; // REPEAT_MODE_NONE
int repeatIcon = R.drawable.ic_repeat; // REPEAT_MODE_NONE
if (getRepeatMode() == REPEAT_MODE_THIS) {
repeatIcon = R.drawable.ic_repeat_one;
} else if (getRepeatMode() == REPEAT_MODE_ALL) {
repeatIcon = R.drawable.ic_repeat_white_circle;
}
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
CYCLE_REPEAT, getString(R.string.action_cycle_repeat), repeatIcon)
.build());
stateBuilder.addCustomAction(
new PlaybackStateCompat.CustomAction.Builder(
CYCLE_REPEAT, getString(R.string.action_cycle_repeat), repeatIcon)
.build());
final int shuffleIcon = getShuffleMode() == SHUFFLE_MODE_NONE ? R.drawable.ic_shuffle_off_circled
: R.drawable.ic_shuffle_on_circled;
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon)
.build());
final int shuffleIcon =
getShuffleMode() == SHUFFLE_MODE_NONE
? R.drawable.ic_shuffle_off_circled
: R.drawable.ic_shuffle_on_circled;
stateBuilder.addCustomAction(
new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon)
.build());
final int favoriteIcon = MusicUtil.INSTANCE.isFavorite(getApplicationContext(), getCurrentSong())
? R.drawable.ic_favorite : R.drawable.ic_favorite_border;
stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon)
.build());
final int favoriteIcon =
MusicUtil.INSTANCE.isFavorite(getApplicationContext(), getCurrentSong())
? R.drawable.ic_favorite
: R.drawable.ic_favorite_border;
stateBuilder.addCustomAction(
new PlaybackStateCompat.CustomAction.Builder(
TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon)
.build());
}
private void setupMediaSession() {
ComponentName mediaButtonReceiverComponentName = new ComponentName(
getApplicationContext(),
MediaButtonIntentReceiver.class);
ComponentName mediaButtonReceiverComponentName =
new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mediaButtonReceiverComponentName);
PendingIntent mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(
getApplicationContext(),
0,
mediaButtonIntent,
0);
PendingIntent mediaButtonReceiverPendingIntent =
PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
mediaSession = new MediaSessionCompat(this,
"Metro",
mediaButtonReceiverComponentName,
mediaButtonReceiverPendingIntent);
MediaSessionCallback mediasessionCallback = new MediaSessionCallback(
getApplicationContext(), this);
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
);
mediaSession =
new MediaSessionCompat(
this,
"RetroMusicPlayer",
mediaButtonReceiverComponentName,
mediaButtonReceiverPendingIntent);
MediaSessionCallback mediasessionCallback =
new MediaSessionCallback(getApplicationContext(), this);
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(mediasessionCallback);
mediaSession.setActive(true);
mediaSession.setMediaButtonReceiver(mediaButtonReceiverPendingIntent);
}
public class MusicBinder extends Binder {
@ -1372,4 +1441,4 @@ public class MusicService extends Service implements
return MusicService.this;
}
}
}
}

View file

@ -14,17 +14,6 @@
package io.github.muntashirakon.music.service;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import io.github.muntashirakon.music.util.PreferenceUtil;
import static io.github.muntashirakon.music.service.MusicService.DUCK;
import static io.github.muntashirakon.music.service.MusicService.META_CHANGED;
import static io.github.muntashirakon.music.service.MusicService.PLAY_STATE_CHANGED;
@ -32,140 +21,148 @@ import static io.github.muntashirakon.music.service.MusicService.REPEAT_MODE_NON
import static io.github.muntashirakon.music.service.MusicService.TRACK_ENDED;
import static io.github.muntashirakon.music.service.MusicService.TRACK_WENT_TO_NEXT;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import io.github.muntashirakon.music.util.PreferenceUtil;
import java.lang.ref.WeakReference;
class PlaybackHandler extends Handler {
@NonNull
private final WeakReference<MusicService> mService;
private float currentDuckVolume = 1.0f;
@NonNull private final WeakReference<MusicService> mService;
private float currentDuckVolume = 1.0f;
PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
super(looper);
mService = new WeakReference<>(service);
PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
super(looper);
mService = new WeakReference<>(service);
}
@Override
public void handleMessage(@NonNull final Message msg) {
final MusicService service = mService.get();
if (service == null) {
return;
}
@Override
public void handleMessage(@NonNull final Message msg) {
final MusicService service = mService.get();
if (service == null) {
return;
switch (msg.what) {
case MusicService.DUCK:
if (PreferenceUtil.INSTANCE.isAudioDucking()) {
currentDuckVolume -= .05f;
if (currentDuckVolume > .2f) {
sendEmptyMessageDelayed(DUCK, 10);
} else {
currentDuckVolume = .2f;
}
} else {
currentDuckVolume = 1f;
}
service.playback.setVolume(currentDuckVolume);
break;
switch (msg.what) {
case MusicService.DUCK:
if (PreferenceUtil.INSTANCE.isAudioDucking()) {
currentDuckVolume -= .05f;
if (currentDuckVolume > .2f) {
sendEmptyMessageDelayed(DUCK, 10);
} else {
currentDuckVolume = .2f;
}
} else {
currentDuckVolume = 1f;
}
service.playback.setVolume(currentDuckVolume);
break;
case MusicService.UNDUCK:
if (PreferenceUtil.INSTANCE.isAudioDucking()) {
currentDuckVolume += .03f;
if (currentDuckVolume < 1f) {
sendEmptyMessageDelayed(MusicService.UNDUCK, 10);
} else {
currentDuckVolume = 1f;
}
} else {
currentDuckVolume = 1f;
}
service.playback.setVolume(currentDuckVolume);
break;
case TRACK_WENT_TO_NEXT:
if (service.pendingQuit || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.pause();
service.seek(0);
if (service.pendingQuit) {
service.pendingQuit = false;
service.quit();
break;
}
} else {
service.position = service.nextPosition;
service.prepareNextImpl();
service.notifyChange(META_CHANGED);
}
break;
case TRACK_ENDED:
// if there is a timer finished, don't continue
if (service.pendingQuit ||
service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.notifyChange(PLAY_STATE_CHANGED);
service.seek(0);
if (service.pendingQuit) {
service.pendingQuit = false;
service.quit();
break;
}
} else {
service.playNextSong(false);
}
sendEmptyMessage(MusicService.RELEASE_WAKELOCK);
break;
case MusicService.RELEASE_WAKELOCK:
service.releaseWakeLock();
break;
case MusicService.PLAY_SONG:
service.playSongAtImpl(msg.arg1);
break;
case MusicService.SET_POSITION:
service.openTrackAndPrepareNextAt(msg.arg1);
service.notifyChange(PLAY_STATE_CHANGED);
break;
case MusicService.PREPARE_NEXT:
service.prepareNextImpl();
break;
case MusicService.RESTORE_QUEUES:
service.restoreQueuesAndPositionIfNecessary();
break;
case MusicService.FOCUS_CHANGE:
switch (msg.arg1) {
case AudioManager.AUDIOFOCUS_GAIN:
if (!service.isPlaying() && service.isPausedByTransientLossOfFocus()) {
service.play();
service.setPausedByTransientLossOfFocus(false);
}
removeMessages(DUCK);
sendEmptyMessage(MusicService.UNDUCK);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media playback
service.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media playback because playback
// is likely to resume
boolean wasPlaying = service.isPlaying();
service.pause();
service.setPausedByTransientLossOfFocus(wasPlaying);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
removeMessages(MusicService.UNDUCK);
sendEmptyMessage(DUCK);
break;
}
break;
case MusicService.UNDUCK:
if (PreferenceUtil.INSTANCE.isAudioDucking()) {
currentDuckVolume += .03f;
if (currentDuckVolume < 1f) {
sendEmptyMessageDelayed(MusicService.UNDUCK, 10);
} else {
currentDuckVolume = 1f;
}
} else {
currentDuckVolume = 1f;
}
service.playback.setVolume(currentDuckVolume);
break;
case TRACK_WENT_TO_NEXT:
if (service.pendingQuit
|| service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.pause();
service.seek(0);
if (service.pendingQuit) {
service.pendingQuit = false;
service.quit();
break;
}
} else {
service.position = service.nextPosition;
service.prepareNextImpl();
service.notifyChange(META_CHANGED);
}
break;
case TRACK_ENDED:
// if there is a timer finished, don't continue
if (service.pendingQuit
|| service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.notifyChange(PLAY_STATE_CHANGED);
service.seek(0);
if (service.pendingQuit) {
service.pendingQuit = false;
service.quit();
break;
}
} else {
service.playNextSong(false);
}
sendEmptyMessage(MusicService.RELEASE_WAKELOCK);
break;
case MusicService.RELEASE_WAKELOCK:
service.releaseWakeLock();
break;
case MusicService.PLAY_SONG:
service.playSongAtImpl(msg.arg1);
break;
case MusicService.SET_POSITION:
service.openTrackAndPrepareNextAt(msg.arg1);
service.notifyChange(PLAY_STATE_CHANGED);
break;
case MusicService.PREPARE_NEXT:
service.prepareNextImpl();
break;
case MusicService.RESTORE_QUEUES:
service.restoreQueuesAndPositionIfNecessary();
break;
case MusicService.FOCUS_CHANGE:
switch (msg.arg1) {
case AudioManager.AUDIOFOCUS_GAIN:
if (!service.isPlaying() && service.isPausedByTransientLossOfFocus()) {
service.play();
service.setPausedByTransientLossOfFocus(false);
}
removeMessages(DUCK);
sendEmptyMessage(MusicService.UNDUCK);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media playback
service.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media playback because playback
// is likely to resume
boolean wasPlaying = service.isPlaying();
service.pause();
service.setPausedByTransientLossOfFocus(wasPlaying);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
removeMessages(MusicService.UNDUCK);
sendEmptyMessage(DUCK);
break;
}
break;
}
}
}

View file

@ -55,7 +55,7 @@ class PlayingNotificationImpl : PlayingNotification() {
if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
val action = Intent(service, MainActivity::class.java)
action.putExtra(MainActivity.EXPAND_PANEL, true)
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent =
PendingIntent.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT)

View file

@ -73,7 +73,7 @@ class PlayingNotificationOreo : PlayingNotification() {
val notificationLayoutBig = getCombinedRemoteViews(false, song)
val action = Intent(service, MainActivity::class.java)
action.putExtra(MainActivity.EXPAND_PANEL, true)
action.putExtra(MainActivity.EXPAND_PANEL, PreferenceUtil.isExpandPanel)
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val clickIntent = PendingIntent