From cf90abb77e1cb0b05e002067e1a0513b6392ec6e Mon Sep 17 00:00:00 2001 From: Nite Date: Mon, 8 Feb 2021 20:24:20 +0100 Subject: [PATCH] Added NowPlayingFragment --- .../activity/SubsonicTabActivity.java | 368 +----------------- .../ultrasonic/fragment/ChatFragment.java | 14 +- .../fragment/NowPlayingFragment.java | 205 ++++++++++ .../ultrasonic/fragment/PlayerFragment.java | 1 + .../ultrasonic/fragment/PodcastFragment.java | 12 +- .../service/MediaPlayerService.java | 32 +- .../util/TabActivityBackgroundTask.java | 11 +- .../ultrasonic/activity/NavigationActivity.kt | 82 +++- .../moire/ultrasonic/di/ApplicationModule.kt | 2 + .../util/NowPlayingEventDistributor.kt | 25 ++ .../util/NowPlayingEventListener.kt | 7 + ultrasonic/src/main/res/layout/chat.xml | 2 - ultrasonic/src/main/res/layout/lyrics.xml | 2 - ultrasonic/src/main/res/layout/main.xml | 2 - .../main/res/layout/navigation_activity.xml | 17 +- .../src/main/res/layout/now_playing.xml | 3 +- ultrasonic/src/main/res/layout/podcasts.xml | 2 - ultrasonic/src/main/res/layout/search.xml | 2 - .../src/main/res/layout/select_album.xml | 2 - .../src/main/res/layout/select_artist.xml | 2 - .../src/main/res/layout/select_genre.xml | 2 - .../src/main/res/layout/select_playlist.xml | 2 - .../src/main/res/layout/select_share.xml | 2 - 23 files changed, 368 insertions(+), 431 deletions(-) create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventDistributor.kt create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventListener.kt diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java index d8ce6438..94267c1e 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java @@ -78,8 +78,8 @@ public class SubsonicTabActivity extends ResultActivity private static final String STATE_ACTIVE_POSITION = "org.moire.ultrasonic.activePosition"; private static final int DIALOG_ASK_FOR_SHARE_DETAILS = 102; - private Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); - private Lazy lifecycleSupport = inject(MediaPlayerLifecycleSupport.class); + private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); + private final Lazy lifecycleSupport = inject(MediaPlayerLifecycleSupport.class); protected Lazy imageLoader = inject(ImageLoaderProvider.class); public MenuDrawer menuDrawer; @@ -118,7 +118,7 @@ public class SubsonicTabActivity extends ResultActivity bookmarksMenuItem = findViewById(R.id.menu_bookmarks); sharesMenuItem = findViewById(R.id.menu_shares); - setActionBarDisplayHomeAsUp(true); + //setActionBarDisplayHomeAsUp(true); TextView activeView = (TextView) findViewById(menuActiveViewId); @@ -163,11 +163,11 @@ public class SubsonicTabActivity extends ResultActivity if (!nowPlayingHidden) { - showNowPlaying(); + //showNowPlaying(); } else { - hideNowPlaying(); + //hideNowPlaying(); } } @@ -194,23 +194,6 @@ public class SubsonicTabActivity extends ResultActivity imageLoader.getValue().clearImageLoader(); } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - boolean isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN; - boolean isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP; - boolean isVolumeAdjust = isVolumeDown || isVolumeUp; - boolean isJukebox = getMediaPlayerController() != null && getMediaPlayerController().isJukeboxEnabled(); - - if (isVolumeAdjust && isJukebox) - { - getMediaPlayerController().adjustJukeboxVolume(isVolumeUp); - return true; - } - - return super.onKeyDown(keyCode, event); - } - protected void restart() { Intent intent = new Intent(this, this.getClass()); @@ -246,270 +229,11 @@ public class SubsonicTabActivity extends ResultActivity } } - public void showNowPlaying() - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - new SilentBackgroundTask(SubsonicTabActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - if (!Util.getShowNowPlayingPreference(SubsonicTabActivity.this)) - { - hideNowPlaying(); - return null; - } - - if (nowPlayingView != null) - { - PlayerState playerState = mediaPlayerControllerLazy.getValue().getPlayerState(); - - if (playerState.equals(PlayerState.PAUSED) || playerState.equals(PlayerState.STARTED)) - { - DownloadFile file = mediaPlayerControllerLazy.getValue().getCurrentPlaying(); - - if (file != null) - { - final Entry song = file.getSong(); - showNowPlaying(SubsonicTabActivity.this, mediaPlayerControllerLazy.getValue(), song, playerState); - } - } - else - { - hideNowPlaying(); - } - } - - return null; - } - - @Override - protected void done(Void result) - { - } - }.execute(); - } - }); - } - - private void showNowPlaying(final Context context, final MediaPlayerController mediaPlayerController, final Entry song, final PlayerState playerState) - { - if (context == null || mediaPlayerController == null || song == null || playerState == null) - { - return; - } - - if (!Util.getShowNowPlayingPreference(context)) - { - hideNowPlaying(); - return; - } - - if (nowPlayingView != null) - { - try - { - setVisibilityOnUiThread(nowPlayingView, View.VISIBLE); - nowPlayingHidden = false; - - ImageView playButton = (ImageView) nowPlayingView.findViewById(R.id.now_playing_control_play); - - if (playerState == PlayerState.PAUSED) - { - setImageDrawableOnUiThread(playButton, Util.getDrawableFromAttribute(context, R.attr.media_play)); - } - else if (playerState == PlayerState.STARTED) - { - setImageDrawableOnUiThread(playButton, Util.getDrawableFromAttribute(context, R.attr.media_pause)); - } - - String title = song.getTitle(); - String artist = song.getArtist(); - - final ImageView nowPlayingAlbumArtImage = (ImageView) nowPlayingView.findViewById(R.id.now_playing_image); - TextView nowPlayingTrack = (TextView) nowPlayingView.findViewById(R.id.now_playing_trackname); - TextView nowPlayingArtist = (TextView) nowPlayingView.findViewById(R.id.now_playing_artist); - - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(context), false, true); - } - }); - - // TODO: Refactor to use navigation - final Intent intent = new Intent(context, SelectAlbumFragment.class);// SelectAlbumActivity.class); - - if (Util.getShouldUseId3Tags(context)) - { - intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, song.getAlbumId()); - } - else - { - intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, song.getParent()); - } - - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, song.getAlbum()); - - setOnClickListenerOnUiThread(nowPlayingAlbumArtImage, new OnClickListener() - { - @Override - public void onClick(View view) - { - startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent); - } - }); - - setTextOnUiThread(nowPlayingTrack, title); - setTextOnUiThread(nowPlayingArtist, artist); - - ImageView nowPlayingControlPlay = (ImageView) nowPlayingView.findViewById(R.id.now_playing_control_play); - - SwipeDetector swipeDetector = new SwipeDetector(SubsonicTabActivity.this, mediaPlayerController); - setOnTouchListenerOnUiThread(nowPlayingView, swipeDetector); - - setOnClickListenerOnUiThread(nowPlayingView, new OnClickListener() - { - @Override - public void onClick(View v) - { - } - }); - - setOnClickListenerOnUiThread(nowPlayingControlPlay, new OnClickListener() - { - @Override - public void onClick(View view) - { - mediaPlayerController.togglePlayPause(); - } - }); - - } - catch (Exception x) - { - Timber.w(x, "Failed to get notification cover art"); - } - } - } - - public void hideNowPlaying() - { - try - { - if (nowPlayingView != null) - { - setVisibilityOnUiThread(nowPlayingView, View.GONE); - } - } - catch (Exception ex) - { - Timber.w(ex, "Exception in hideNowPlaying"); - } - } - - public void setOnTouchListenerOnUiThread(final View view, final OnTouchListener listener) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null && view.getVisibility() != View.GONE) - { - view.setOnTouchListener(listener); - } - } - }); - } - - public void setOnClickListenerOnUiThread(final View view, final OnClickListener listener) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null && view.getVisibility() != View.GONE) - { - view.setOnClickListener(listener); - } - } - }); - } - - public void setTextOnUiThread(final TextView view, final CharSequence text) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null && view.getVisibility() != View.GONE) - { - view.setText(text); - } - } - }); - } - - public void setImageDrawableOnUiThread(final ImageView view, final Drawable drawable) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null && view.getVisibility() != View.GONE) - { - view.setImageDrawable(drawable); - } - } - }); - } - - public void setVisibilityOnUiThread(final View view, final int visibility) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null && view.getVisibility() != visibility) - { - view.setVisibility(visibility); - } - } - }); - } - public static SubsonicTabActivity getInstance() { return instance; } - public MediaPlayerController getMediaPlayerController() - { - return mediaPlayerControllerLazy.getValue(); - } - - protected void setActionBarDisplayHomeAsUp(boolean enabled) - { - ActionBar actionBar = getSupportActionBar(); - - if (actionBar != null) - { - actionBar.setDisplayHomeAsUpEnabled(enabled); - } - } @Override protected void onRestoreInstanceState(Bundle inState) @@ -527,87 +251,5 @@ public class SubsonicTabActivity extends ResultActivity outState.putInt(STATE_ACTIVE_POSITION, activePosition); } - @Override - public void onBackPressed() - { - final int drawerState = menuDrawer.getDrawerState(); - if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) - { - menuDrawer.closeMenu(true); - return; - } - - super.onBackPressed(); - } - - protected class SwipeDetector implements OnTouchListener - { - public SwipeDetector(SubsonicTabActivity activity, final MediaPlayerController mediaPlayerController) - { - this.mediaPlayerController = mediaPlayerController; - this.activity = activity; - } - - private static final int MIN_DISTANCE = 30; - private float downX, downY, upX, upY; - private MediaPlayerController mediaPlayerController; - private SubsonicTabActivity activity; - - @Override - public boolean onTouch(View v, MotionEvent event) - { - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - { - downX = event.getX(); - downY = event.getY(); - return false; - } - case MotionEvent.ACTION_UP: - { - upX = event.getX(); - upY = event.getY(); - - float deltaX = downX - upX; - float deltaY = downY - upY; - - if (Math.abs(deltaX) > MIN_DISTANCE) - { - // left or right - if (deltaX < 0) - { - mediaPlayerController.previous(); - return false; - } - if (deltaX > 0) - { - mediaPlayerController.next(); - return false; - } - } - else if (Math.abs(deltaY) > MIN_DISTANCE) - { - if (deltaY < 0) - { - SubsonicTabActivity.nowPlayingHidden = true; - activity.hideNowPlaying(); - return false; - } - if (deltaY > 0) - { - return false; - } - } - - // TODO: Refactor this to Navigation. It should automatically go to the PlayerFragment. - //SubsonicTabActivity.this.startActivityForResultWithoutTransition(activity, DownloadActivity.class); - return false; - } - } - - return false; - } - } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ChatFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ChatFragment.java index 6fe91062..483044a2 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ChatFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ChatFragment.java @@ -28,6 +28,7 @@ import org.moire.ultrasonic.domain.ChatMessage; import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.util.BackgroundTask; +import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.view.ChatAdapter; @@ -50,6 +51,7 @@ public class ChatFragment extends Fragment { private Timer timer; private volatile static Long lastChatMessageTime = (long) 0; private static final ArrayList messageList = new ArrayList(); + private CancellationToken cancellationToken; private final Lazy activeServerProvider = inject(ActiveServerProvider.class); @@ -67,6 +69,8 @@ public class ChatFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + cancellationToken = new CancellationToken(); messageEditText = view.findViewById(R.id.chat_edittext); sendButton = view.findViewById(R.id.chat_send); @@ -184,6 +188,12 @@ public class ChatFragment extends Fragment { } } + @Override + public void onDestroyView() { + cancellationToken.cancel(); + super.onDestroyView(); + } + private void timerMethod() { int refreshInterval = Util.getChatRefreshInterval(getContext()); @@ -228,7 +238,7 @@ public class ChatFragment extends Fragment { { messageEditText.setText(""); - BackgroundTask task = new TabActivityBackgroundTask(getActivity(), false) + BackgroundTask task = new TabActivityBackgroundTask(getActivity(), false, null, cancellationToken) { @Override protected Void doInBackground() throws Throwable @@ -252,7 +262,7 @@ public class ChatFragment extends Fragment { private synchronized void load() { - BackgroundTask> task = new TabActivityBackgroundTask>(getActivity(), false) + BackgroundTask> task = new TabActivityBackgroundTask>(getActivity(), false, null, cancellationToken) { @Override protected List doInBackground() throws Throwable diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java new file mode 100644 index 00000000..4f9882f6 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java @@ -0,0 +1,205 @@ +package org.moire.ultrasonic.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.navigation.Navigation; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.MusicDirectory; +import org.moire.ultrasonic.domain.PlayerState; +import org.moire.ultrasonic.service.DownloadFile; +import org.moire.ultrasonic.service.MediaPlayerController; +import org.moire.ultrasonic.subsonic.ImageLoaderProvider; +import org.moire.ultrasonic.util.Constants; +import org.moire.ultrasonic.util.NowPlayingEventDistributor; +import org.moire.ultrasonic.util.NowPlayingEventListener; +import org.moire.ultrasonic.util.Util; + +import kotlin.Lazy; +import timber.log.Timber; + +import static org.koin.java.KoinJavaComponent.inject; + +public class NowPlayingFragment extends Fragment { + + private static final int MIN_DISTANCE = 30; + private float downX; + private float downY; + ImageView playButton; + ImageView nowPlayingAlbumArtImage; + TextView nowPlayingTrack; + TextView nowPlayingArtist; + + private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); + private final Lazy imageLoader = inject(ImageLoaderProvider.class); + private final Lazy nowPlayingEventDistributor = inject(NowPlayingEventDistributor.class); + private NowPlayingEventListener nowPlayingEventListener; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + Util.applyTheme(this.getContext()); + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.now_playing, container, false); + } + + @Override + public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { + + playButton = (ImageView) view.findViewById(R.id.now_playing_control_play); + nowPlayingAlbumArtImage = (ImageView) view.findViewById(R.id.now_playing_image); + nowPlayingTrack = (TextView) view.findViewById(R.id.now_playing_trackname); + nowPlayingArtist = (TextView) view.findViewById(R.id.now_playing_artist); + + nowPlayingEventListener = new NowPlayingEventListener() { + @Override + public void onDismissNowPlaying() { } + @Override + public void onHideNowPlaying() { } + @Override + public void onShowNowPlaying() { Update(); } + }; + + nowPlayingEventDistributor.getValue().subscribe(nowPlayingEventListener); + } + + @Override + public void onResume() { + super.onResume(); + Update(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + nowPlayingEventDistributor.getValue().unsubscribe(nowPlayingEventListener); + } + + private void Update() { + try + { + PlayerState playerState = mediaPlayerControllerLazy.getValue().getPlayerState(); + if (playerState == PlayerState.PAUSED) { + playButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_play)); + } else if (playerState == PlayerState.STARTED) { + playButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_pause)); + } + + DownloadFile file = mediaPlayerControllerLazy.getValue().getCurrentPlaying(); + if (file != null) { + final MusicDirectory.Entry song = file.getSong(); + String title = song.getTitle(); + String artist = song.getArtist(); + + imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(getContext()), false, true); + nowPlayingTrack.setText(title); + nowPlayingArtist.setText(artist); + + nowPlayingAlbumArtImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Bundle bundle = new Bundle(); + + if (Util.getShouldUseId3Tags(getContext())) { + bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true); + bundle.putString(Constants.INTENT_EXTRA_NAME_ID, song.getAlbumId()); + } else { + bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false); + bundle.putString(Constants.INTENT_EXTRA_NAME_ID, song.getParent()); + } + + bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, song.getAlbum()); + Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); + } + }); + } + + getView().setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return handleOnTouch(v, event); + } + }); + + // TODO: Check if this empty onClickListener is necessary + getView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + } + }); + + playButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mediaPlayerControllerLazy.getValue().togglePlayPause(); + } + }); + } + catch (Exception x) { + Timber.w(x, "Failed to get notification cover art"); + } + } + + private boolean handleOnTouch(View v, MotionEvent event) { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + { + downX = event.getX(); + downY = event.getY(); + return false; + } + case MotionEvent.ACTION_UP: + { + float upX = event.getX(); + float upY = event.getY(); + + float deltaX = downX - upX; + float deltaY = downY - upY; + + if (Math.abs(deltaX) > MIN_DISTANCE) + { + // left or right + if (deltaX < 0) + { + mediaPlayerControllerLazy.getValue().previous(); + return false; + } + if (deltaX > 0) + { + mediaPlayerControllerLazy.getValue().next(); + return false; + } + } + else if (Math.abs(deltaY) > MIN_DISTANCE) + { + if (deltaY < 0) + { + nowPlayingEventDistributor.getValue().RaiseNowPlayingDismissedEvent(); + return false; + } + if (deltaY > 0) + { + return false; + } + } + Navigation.findNavController(getView()).navigate(R.id.playerFragment); + return false; + } + } + return false; + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java index f1d9ebb0..7f794c02 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java @@ -1404,6 +1404,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur @Override protected void done(final Void result) { + if (cancellationToken.isCancellationRequested()) return; if (currentPlaying != null) { final int millisTotal = duration == null ? 0 : duration; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java index e901c812..61dd6adf 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PodcastFragment.java @@ -17,6 +17,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel; import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.util.BackgroundTask; +import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.Util; @@ -28,6 +29,7 @@ public class PodcastFragment extends Fragment { private View emptyTextView; ListView channelItemsListView = null; + private CancellationToken cancellationToken; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -43,6 +45,8 @@ public class PodcastFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + cancellationToken = new CancellationToken(); FragmentTitle.Companion.setTitle(this, R.string.podcasts_label); emptyTextView = view.findViewById(R.id.select_podcasts_empty); @@ -65,9 +69,15 @@ public class PodcastFragment extends Fragment { load(); } + @Override + public void onDestroyView() { + cancellationToken.cancel(); + super.onDestroyView(); + } + private void load() { - BackgroundTask> task = new TabActivityBackgroundTask>(getActivity(), true) + BackgroundTask> task = new TabActivityBackgroundTask>(getActivity(), true, null, cancellationToken) { @Override protected List doInBackground() throws Throwable diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java index a902719c..39800635 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java @@ -32,6 +32,7 @@ import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2; import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3; import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4; import org.moire.ultrasonic.util.FileUtil; +import org.moire.ultrasonic.util.NowPlayingEventDistributor; import org.moire.ultrasonic.util.ShufflePlayBuffer; import org.moire.ultrasonic.util.SimpleServiceBinder; import org.moire.ultrasonic.util.Util; @@ -64,10 +65,11 @@ public class MediaPlayerService extends Service private final Scrobbler scrobbler = new Scrobbler(); public Lazy jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); - private Lazy downloadQueueSerializerLazy = inject(DownloadQueueSerializer.class); - private Lazy shufflePlayBufferLazy = inject(ShufflePlayBuffer.class); - private Lazy downloaderLazy = inject(Downloader.class); - private Lazy localMediaPlayerLazy = inject(LocalMediaPlayer.class); + private final Lazy downloadQueueSerializerLazy = inject(DownloadQueueSerializer.class); + private final Lazy shufflePlayBufferLazy = inject(ShufflePlayBuffer.class); + private final Lazy downloaderLazy = inject(Downloader.class); + private final Lazy localMediaPlayerLazy = inject(LocalMediaPlayer.class); + private final Lazy nowPlayingEventDistributor = inject(NowPlayingEventDistributor.class); private LocalMediaPlayer localMediaPlayer; private Downloader downloader; private ShufflePlayBuffer shufflePlayBuffer; @@ -280,21 +282,14 @@ public class MediaPlayerService extends Service UltrasonicAppWidgetProvider4X3.getInstance().notifyChange(MediaPlayerService.this, song, playerState == PlayerState.STARTED, false); UltrasonicAppWidgetProvider4X4.getInstance().notifyChange(MediaPlayerService.this, song, playerState == PlayerState.STARTED, false); - SubsonicTabActivity tabInstance = SubsonicTabActivity.getInstance(); - if (currentPlaying != null) { updateNotification(localMediaPlayer.playerState, currentPlaying); - if (tabInstance != null) { - tabInstance.showNowPlaying(); - } + nowPlayingEventDistributor.getValue().RaiseShowNowPlayingEvent(); } else { - if (tabInstance != null) - { - tabInstance.hideNowPlaying(); - } + nowPlayingEventDistributor.getValue().RaiseHideNowPlayingEvent(); stopForeground(true); localMediaPlayer.clearRemoteControl(); isInForeground = false; @@ -499,7 +494,6 @@ public class MediaPlayerService extends Service UltrasonicAppWidgetProvider4X2.getInstance().notifyChange(MediaPlayerService.this, song, playerState == PlayerState.STARTED, true); UltrasonicAppWidgetProvider4X3.getInstance().notifyChange(MediaPlayerService.this, song, playerState == PlayerState.STARTED, false); UltrasonicAppWidgetProvider4X4.getInstance().notifyChange(MediaPlayerService.this, song, playerState == PlayerState.STARTED, false); - SubsonicTabActivity tabInstance = SubsonicTabActivity.getInstance(); if (show) { @@ -507,18 +501,12 @@ public class MediaPlayerService extends Service if (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED) { updateNotification(playerState, currentPlaying); - if (tabInstance != null) - { - tabInstance.showNowPlaying(); - } + nowPlayingEventDistributor.getValue().RaiseShowNowPlayingEvent(); } } else { - if (tabInstance != null) - { - tabInstance.hideNowPlaying(); - } + nowPlayingEventDistributor.getValue().RaiseHideNowPlayingEvent(); stopForeground(true); localMediaPlayer.clearRemoteControl(); isInForeground = false; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TabActivityBackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TabActivityBackgroundTask.java index 861bb5f3..62cd0248 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TabActivityBackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TabActivityBackgroundTask.java @@ -10,18 +10,9 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; */ public abstract class TabActivityBackgroundTask extends BackgroundTask { - private final boolean changeProgress; private final SwipeRefreshLayout swipe; - private CancellationToken cancel; - - // TODO: Try to remove this constructor - public TabActivityBackgroundTask(Activity activity, boolean changeProgress) - { - super(activity); - this.changeProgress = changeProgress; - this.swipe = null; - } + private final CancellationToken cancel; public TabActivityBackgroundTask(Activity activity, boolean changeProgress, SwipeRefreshLayout swipe, CancellationToken cancel) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index aff46138..6b2ccdb1 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -12,9 +12,11 @@ import android.provider.SearchRecentSuggestions import android.view.KeyEvent import android.view.Menu import android.view.MenuItem +import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.drawerlayout.widget.DrawerLayout +import androidx.fragment.app.FragmentContainerView import androidx.navigation.NavController import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment @@ -28,12 +30,16 @@ import org.koin.android.ext.android.inject import org.koin.android.viewmodel.ext.android.viewModel import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline +import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.provider.SearchSuggestionProvider +import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport import org.moire.ultrasonic.subsonic.ImageLoaderProvider import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.FileUtil +import org.moire.ultrasonic.util.NowPlayingEventDistributor +import org.moire.ultrasonic.util.NowPlayingEventListener import org.moire.ultrasonic.util.SubsonicUncaughtExceptionHandler import org.moire.ultrasonic.util.Util import timber.log.Timber @@ -47,14 +53,20 @@ class NavigationActivity : AppCompatActivity() { var bookmarksMenuItem: MenuItem? = null var sharesMenuItem: MenuItem? = null private var theme: String? = null + var nowPlayingView: FragmentContainerView? = null + var nowPlayingHidden = false private lateinit var appBarConfiguration : AppBarConfiguration + private lateinit var nowPlayingEventListener : NowPlayingEventListener + private val serverSettingsModel: ServerSettingsModel by viewModel() private val lifecycleSupport: MediaPlayerLifecycleSupport by inject() private val mediaPlayerController: MediaPlayerController by inject() private val imageLoaderProvider: ImageLoaderProvider by inject() + private val nowPlayingEventDistributor: NowPlayingEventDistributor by inject() private var infoDialogDisplayed = false + private var currentFragmentId: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { setUncaughtExceptionHandler() @@ -64,6 +76,7 @@ class NavigationActivity : AppCompatActivity() { volumeControlStream = AudioManager.STREAM_MUSIC setContentView(R.layout.navigation_activity) + nowPlayingView = findViewById(R.id.now_playing_fragment) val toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) @@ -93,7 +106,15 @@ class NavigationActivity : AppCompatActivity() { } Timber.d("Navigated to $dest") - // TODO: Maybe we can find a better place for theme change. Currently the change occures when navigating between fragments + currentFragmentId = destination.id + // Handle the hiding of the NowPlaying fragment when the Player is active + if (currentFragmentId == R.id.playerFragment) { + hideNowPlaying() + } else { + showNowPlaying() + } + + // TODO: Maybe we can find a better place for theme change. Currently the change occurs when navigating between fragments // but theoretically Settings could request a Navigation Activity recreate instantly when the theme setting changes // Make sure to update theme if it has changed if (theme == null) theme = Util.getTheme(this) @@ -112,6 +133,24 @@ class NavigationActivity : AppCompatActivity() { loadSettings() showInfoDialog(showWelcomeScreen) + + nowPlayingEventListener = object : NowPlayingEventListener { + override fun onDismissNowPlaying() { + // TODO: When will it be set back to false? + nowPlayingHidden = true; + hideNowPlaying(); + } + + override fun onHideNowPlaying() { + hideNowPlaying() + } + + override fun onShowNowPlaying() { + showNowPlaying() + } + } + + nowPlayingEventDistributor.subscribe(nowPlayingEventListener) } override fun onResume() { @@ -125,19 +164,16 @@ class NavigationActivity : AppCompatActivity() { // Lifecycle support's constructor registers some event receivers so it should be created early lifecycleSupport.onCreate() - // TODO: Implement NowPlaying as a Fragment - // This must be filled here because onCreate is called before the derived objects would call setContentView - //getNowPlayingView() - - if (!SubsonicTabActivity.nowPlayingHidden) { - //showNowPlaying() + if (!nowPlayingHidden) { + showNowPlaying() } else { - //hideNowPlaying() + hideNowPlaying() } } override fun onDestroy() { Util.unregisterMediaButtonEventReceiver(this, false) + nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener) super.onDestroy() // TODO: Handle NowPlaying if necessary @@ -259,4 +295,34 @@ class NavigationActivity : AppCompatActivity() { Thread.setDefaultUncaughtExceptionHandler(SubsonicUncaughtExceptionHandler(this)) } } + + private fun showNowPlaying() { + if (!Util.getShowNowPlayingPreference(this) || nowPlayingHidden) { + hideNowPlaying() + return + } + + // Do not show for Player fragment + if (currentFragmentId == R.id.playerFragment) { + hideNowPlaying() + return + } + + if (nowPlayingView != null) { + val playerState: PlayerState = mediaPlayerController.playerState + if (playerState == PlayerState.PAUSED || playerState == PlayerState.STARTED) { + val file: DownloadFile? = mediaPlayerController.currentPlaying + if (file != null) { + val song = file.song + nowPlayingView?.visibility = View.VISIBLE + } + } else { + hideNowPlaying() + } + } + } + + private fun hideNowPlaying() { + nowPlayingView?.visibility = View.GONE + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt index 91342d99..fad1902a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt @@ -7,10 +7,12 @@ import org.moire.ultrasonic.cache.AndroidDirectories import org.moire.ultrasonic.cache.Directories import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.subsonic.ImageLoaderProvider +import org.moire.ultrasonic.util.NowPlayingEventDistributor import org.moire.ultrasonic.util.PermissionUtil val applicationModule = module { single { ActiveServerProvider(get(), androidContext()) } single { ImageLoaderProvider(androidContext()) } single { PermissionUtil(androidContext()) } + single { NowPlayingEventDistributor() } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventDistributor.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventDistributor.kt new file mode 100644 index 00000000..64772f9e --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventDistributor.kt @@ -0,0 +1,25 @@ +package org.moire.ultrasonic.util + +class NowPlayingEventDistributor { + var eventListenerList: MutableList = listOf().toMutableList() + + fun subscribe(listener: NowPlayingEventListener) { + eventListenerList.add(listener) + } + + fun unsubscribe(listener: NowPlayingEventListener) { + eventListenerList.remove(listener) + } + + fun RaiseShowNowPlayingEvent() { + eventListenerList.forEach{ listener -> listener.onShowNowPlaying() } + } + + fun RaiseHideNowPlayingEvent() { + eventListenerList.forEach{ listener -> listener.onHideNowPlaying() } + } + + fun RaiseNowPlayingDismissedEvent() { + eventListenerList.forEach{ listener -> listener.onDismissNowPlaying() } + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventListener.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventListener.kt new file mode 100644 index 00000000..ead2499b --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/NowPlayingEventListener.kt @@ -0,0 +1,7 @@ +package org.moire.ultrasonic.util + +interface NowPlayingEventListener { + fun onDismissNowPlaying() + fun onHideNowPlaying() + fun onShowNowPlaying() +} \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/chat.xml b/ultrasonic/src/main/res/layout/chat.xml index 55304ad2..5b2cc137 100644 --- a/ultrasonic/src/main/res/layout/chat.xml +++ b/ultrasonic/src/main/res/layout/chat.xml @@ -46,6 +46,4 @@ - - diff --git a/ultrasonic/src/main/res/layout/lyrics.xml b/ultrasonic/src/main/res/layout/lyrics.xml index 4e5b22a2..6880d374 100644 --- a/ultrasonic/src/main/res/layout/lyrics.xml +++ b/ultrasonic/src/main/res/layout/lyrics.xml @@ -60,7 +60,5 @@ - - diff --git a/ultrasonic/src/main/res/layout/main.xml b/ultrasonic/src/main/res/layout/main.xml index 719d45aa..dcd8fbd0 100644 --- a/ultrasonic/src/main/res/layout/main.xml +++ b/ultrasonic/src/main/res/layout/main.xml @@ -15,6 +15,4 @@ a:layout_width="0dp" a:layout_height="0dp" /> - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/navigation_activity.xml b/ultrasonic/src/main/res/layout/navigation_activity.xml index 114f6b21..337d952d 100644 --- a/ultrasonic/src/main/res/layout/navigation_activity.xml +++ b/ultrasonic/src/main/res/layout/navigation_activity.xml @@ -8,7 +8,7 @@ a:layout_height="match_parent" tools:context="org.moire.ultrasonic.activity.NavigationActivity"> - @@ -18,14 +18,25 @@ a:layout_width="match_parent" a:layout_height="wrap_content" /> - - + + + + android:orientation="vertical" > - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/search.xml b/ultrasonic/src/main/res/layout/search.xml index e0378fa5..5ef70eaa 100644 --- a/ultrasonic/src/main/res/layout/search.xml +++ b/ultrasonic/src/main/res/layout/search.xml @@ -18,6 +18,4 @@ - - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/select_album.xml b/ultrasonic/src/main/res/layout/select_album.xml index b2f27f13..662245b4 100644 --- a/ultrasonic/src/main/res/layout/select_album.xml +++ b/ultrasonic/src/main/res/layout/select_album.xml @@ -33,6 +33,4 @@ - - diff --git a/ultrasonic/src/main/res/layout/select_artist.xml b/ultrasonic/src/main/res/layout/select_artist.xml index 455f8531..b448a34d 100644 --- a/ultrasonic/src/main/res/layout/select_artist.xml +++ b/ultrasonic/src/main/res/layout/select_artist.xml @@ -32,6 +32,4 @@ - - diff --git a/ultrasonic/src/main/res/layout/select_genre.xml b/ultrasonic/src/main/res/layout/select_genre.xml index ccfb8797..4082ed84 100644 --- a/ultrasonic/src/main/res/layout/select_genre.xml +++ b/ultrasonic/src/main/res/layout/select_genre.xml @@ -28,6 +28,4 @@ - - diff --git a/ultrasonic/src/main/res/layout/select_playlist.xml b/ultrasonic/src/main/res/layout/select_playlist.xml index e7176513..b3584ed6 100644 --- a/ultrasonic/src/main/res/layout/select_playlist.xml +++ b/ultrasonic/src/main/res/layout/select_playlist.xml @@ -28,6 +28,4 @@ - - diff --git a/ultrasonic/src/main/res/layout/select_share.xml b/ultrasonic/src/main/res/layout/select_share.xml index 66a07823..3c656ef3 100644 --- a/ultrasonic/src/main/res/layout/select_share.xml +++ b/ultrasonic/src/main/res/layout/select_share.xml @@ -29,6 +29,4 @@ - -