Added NowPlayingFragment

This commit is contained in:
Nite 2021-02-08 20:24:20 +01:00
parent f0917820cb
commit cf90abb77e
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
23 changed files with 368 additions and 431 deletions

View File

@ -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<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private Lazy<MediaPlayerLifecycleSupport> lifecycleSupport = inject(MediaPlayerLifecycleSupport.class);
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private final Lazy<MediaPlayerLifecycleSupport> lifecycleSupport = inject(MediaPlayerLifecycleSupport.class);
protected Lazy<ImageLoaderProvider> 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<Void>(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;
}
}
}

View File

@ -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<ChatMessage> messageList = new ArrayList<ChatMessage>();
private CancellationToken cancellationToken;
private final Lazy<ActiveServerProvider> 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<Void> task = new TabActivityBackgroundTask<Void>(getActivity(), false)
BackgroundTask<Void> task = new TabActivityBackgroundTask<Void>(getActivity(), false, null, cancellationToken)
{
@Override
protected Void doInBackground() throws Throwable
@ -252,7 +262,7 @@ public class ChatFragment extends Fragment {
private synchronized void load()
{
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(getActivity(), false)
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(getActivity(), false, null, cancellationToken)
{
@Override
protected List<ChatMessage> doInBackground() throws Throwable

View File

@ -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<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
private final Lazy<NowPlayingEventDistributor> 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;
}
}

View File

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

View File

@ -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<List<PodcastsChannel>> task = new TabActivityBackgroundTask<List<PodcastsChannel>>(getActivity(), true)
BackgroundTask<List<PodcastsChannel>> task = new TabActivityBackgroundTask<List<PodcastsChannel>>(getActivity(), true, null, cancellationToken)
{
@Override
protected List<PodcastsChannel> doInBackground() throws Throwable

View File

@ -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> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class);
private Lazy<DownloadQueueSerializer> downloadQueueSerializerLazy = inject(DownloadQueueSerializer.class);
private Lazy<ShufflePlayBuffer> shufflePlayBufferLazy = inject(ShufflePlayBuffer.class);
private Lazy<Downloader> downloaderLazy = inject(Downloader.class);
private Lazy<LocalMediaPlayer> localMediaPlayerLazy = inject(LocalMediaPlayer.class);
private final Lazy<DownloadQueueSerializer> downloadQueueSerializerLazy = inject(DownloadQueueSerializer.class);
private final Lazy<ShufflePlayBuffer> shufflePlayBufferLazy = inject(ShufflePlayBuffer.class);
private final Lazy<Downloader> downloaderLazy = inject(Downloader.class);
private final Lazy<LocalMediaPlayer> localMediaPlayerLazy = inject(LocalMediaPlayer.class);
private final Lazy<NowPlayingEventDistributor> 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;

View File

@ -10,18 +10,9 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
*/
public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
{
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)

View File

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

View File

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

View File

@ -0,0 +1,25 @@
package org.moire.ultrasonic.util
class NowPlayingEventDistributor {
var eventListenerList: MutableList<NowPlayingEventListener> = listOf<NowPlayingEventListener>().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() }
}
}

View File

@ -0,0 +1,7 @@
package org.moire.ultrasonic.util
interface NowPlayingEventListener {
fun onDismissNowPlaying()
fun onHideNowPlaying()
fun onShowNowPlaying()
}

View File

@ -46,6 +46,4 @@
</LinearLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -60,7 +60,5 @@
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing"/>
</LinearLayout>

View File

@ -15,6 +15,4 @@
a:layout_width="0dp"
a:layout_height="0dp" />
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -8,7 +8,7 @@
a:layout_height="match_parent"
tools:context="org.moire.ultrasonic.activity.NavigationActivity">
<LinearLayout
<RelativeLayout
a:layout_width="match_parent"
a:layout_height="match_parent"
a:orientation="vertical">
@ -18,14 +18,25 @@
a:layout_width="match_parent"
a:layout_height="wrap_content" />
<fragment
<androidx.fragment.app.FragmentContainerView
a:id="@+id/nav_host_fragment"
a:name="androidx.navigation.fragment.NavHostFragment"
a:layout_width="match_parent"
a:layout_height="match_parent"
a:layout_above="@+id/now_playing_fragment"
a:layout_below="@+id/toolbar"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_graph" />
</LinearLayout>
<androidx.fragment.app.FragmentContainerView
a:id="@+id/now_playing_fragment"
a:name="org.moire.ultrasonic.fragment.NowPlayingFragment"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_alignParentBottom="true"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_graph" />
</RelativeLayout>
<com.google.android.material.navigation.NavigationView
a:id="@+id/nav_view"

View File

@ -3,8 +3,7 @@
android:id="@+id/now_playing"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" >
android:orientation="vertical" >
<LinearLayout
android:layout_height="4dip"

View File

@ -22,6 +22,4 @@
a:fastScrollEnabled="true"
a:textFilterEnabled="true" />
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -18,6 +18,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -33,6 +33,4 @@
<include layout="@layout/album_buttons" />
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -32,6 +32,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -28,6 +28,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -28,6 +28,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

View File

@ -29,6 +29,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/now_playing" />
</LinearLayout>