From a395bd6feb85952ed566198d42bf4bbecceafbd8 Mon Sep 17 00:00:00 2001 From: Nite Date: Fri, 5 Feb 2021 21:45:50 +0100 Subject: [PATCH] Refactored DownloadActivity --- ultrasonic/src/main/AndroidManifest.xml | 51 +- .../ultrasonic/activity/DownloadActivity.java | 1692 ---------------- .../ultrasonic/activity/LyricsActivity.java | 89 - .../ultrasonic/activity/MainActivity.java | 110 -- .../activity/QueryReceiverActivity.java | 58 - .../ultrasonic/activity/SearchActivity.java | 554 ------ .../activity/SelectAlbumActivity.java | 159 -- .../activity/SubsonicTabActivity.java | 407 +--- .../activity/VoiceQueryReceiverActivity.java | 61 - .../ultrasonic/fragment/LyricsFragment.java | 98 + .../ultrasonic/fragment/PlayerFragment.java | 1693 +++++++++++++++++ .../ultrasonic/fragment/SearchFragment.java | 17 + .../fragment/SelectAlbumFragment.java | 3 +- .../ultrasonic/fragment/SettingsFragment.java | 7 +- .../provider/UltrasonicAppWidgetProvider.java | 14 +- .../ultrasonic/service/LocalMediaPlayer.java | 8 +- .../service/MediaPlayerService.java | 5 +- .../org/moire/ultrasonic/util/FileUtil.java | 5 +- .../moire/ultrasonic/util/PermissionUtil.java | 20 +- .../java/org/moire/ultrasonic/util/Util.java | 7 +- .../ultrasonic/activity/NavigationActivity.kt | 26 +- .../kotlin/org/moire/ultrasonic/app/UApp.kt | 2 + .../di/AppPermanentStorageModule.kt | 4 - .../moire/ultrasonic/di/ApplicationModule.kt | 16 + .../ultrasonic/subsonic/DownloadHandler.kt | 10 +- .../moire/ultrasonic/subsonic/ShareHandler.kt | 6 +- ultrasonic/src/main/res/layout/lyrics.xml | 86 +- ultrasonic/src/main/res/menu/navigation.xml | 2 +- .../main/res/navigation/navigation_graph.xml | 46 +- 29 files changed, 1969 insertions(+), 3287 deletions(-) delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/DownloadActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/LyricsActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/MainActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/QueryReceiverActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/SearchActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/activity/VoiceQueryReceiverActivity.java create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/LyricsFragment.java create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index 9ec6b2eb..30de8b56 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -30,10 +30,11 @@ + android:launchMode="singleTask"> + @@ -43,59 +44,11 @@ android:name="android.app.searchable" android:resource="@xml/searchable"/> - - - - - - - - - - - - - - diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/DownloadActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/DownloadActivity.java deleted file mode 100644 index 99f4e536..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/DownloadActivity.java +++ /dev/null @@ -1,1692 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package org.moire.ultrasonic.activity; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.Handler; -import timber.log.Timber; -import android.view.ContextMenu; -import android.view.Display; -import android.view.GestureDetector; -import android.view.GestureDetector.OnGestureListener; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.animation.AnimationUtils; -import android.widget.AdapterView; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.SeekBar; -import android.widget.TextView; -import android.widget.ViewFlipper; - -import androidx.lifecycle.Observer; - -import com.mobeta.android.dslv.DragSortListView; - -import org.koin.java.KoinJavaComponent; -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.audiofx.EqualizerController; -import org.moire.ultrasonic.audiofx.VisualizerController; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.domain.MusicDirectory.Entry; -import org.moire.ultrasonic.domain.PlayerState; -import org.moire.ultrasonic.domain.RepeatMode; -import org.moire.ultrasonic.featureflags.Feature; -import org.moire.ultrasonic.featureflags.FeatureStorage; -import org.moire.ultrasonic.service.DownloadFile; -import org.moire.ultrasonic.service.MediaPlayerController; -import org.moire.ultrasonic.service.MusicService; -import org.moire.ultrasonic.service.MusicServiceFactory; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.SilentBackgroundTask; -import org.moire.ultrasonic.util.Util; -import org.moire.ultrasonic.view.AutoRepeatButton; -import org.moire.ultrasonic.view.SongListAdapter; -import org.moire.ultrasonic.view.VisualizerView; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static org.moire.ultrasonic.domain.PlayerState.COMPLETED; -import static org.moire.ultrasonic.domain.PlayerState.IDLE; -import static org.moire.ultrasonic.domain.PlayerState.PAUSED; -import static org.moire.ultrasonic.domain.PlayerState.STOPPED; - -public class DownloadActivity extends SubsonicTabActivity implements OnGestureListener -{ - private static final int DIALOG_SAVE_PLAYLIST = 100; - private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5; - - private ViewFlipper playlistFlipper; - private TextView emptyTextView; - private TextView songTitleTextView; - private TextView albumTextView; - private TextView artistTextView; - private ImageView albumArtImageView; - private DragSortListView playlistView; - private TextView positionTextView; - private TextView downloadTrackTextView; - private TextView downloadTotalDurationTextView; - private TextView durationTextView; - private static SeekBar progressBar; - private View pauseButton; - private View stopButton; - private View startButton; - private ImageView repeatButton; - private ScheduledExecutorService executorService; - private DownloadFile currentPlaying; - private Entry currentSong; - private long currentRevision; - private EditText playlistNameView; - private GestureDetector gestureScanner; - private int swipeDistance; - private int swipeVelocity; - private VisualizerView visualizerView; - private boolean jukeboxAvailable; - private SilentBackgroundTask onProgressChangedTask; - LinearLayout visualizerViewLayout; - private MenuItem starMenuItem; - private LinearLayout ratingLinearLayout; - private ImageView fiveStar1ImageView; - private ImageView fiveStar2ImageView; - private ImageView fiveStar3ImageView; - private ImageView fiveStar4ImageView; - private ImageView fiveStar5ImageView; - private boolean useFiveStarRating; - private Drawable hollowStar; - private Drawable fullStar; - - private boolean isEqualizerAvailable; - private boolean isVisualizerAvailable; - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(final Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - setContentView(R.layout.download); - - final WindowManager windowManager = getWindowManager(); - final Display display = windowManager.getDefaultDisplay(); - Point size = new Point(); - display.getSize(size); - int width = size.x; - int height = size.y; - - useFiveStarRating = KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING); - - swipeDistance = (width + height) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100; - swipeVelocity = swipeDistance; - gestureScanner = new GestureDetector(this, this); - - playlistFlipper = (ViewFlipper) findViewById(R.id.download_playlist_flipper); - emptyTextView = (TextView) findViewById(R.id.download_empty); - songTitleTextView = (TextView) findViewById(R.id.download_song_title); - albumTextView = (TextView) findViewById(R.id.download_album); - artistTextView = (TextView) findViewById(R.id.download_artist); - albumArtImageView = (ImageView) findViewById(R.id.download_album_art_image); - positionTextView = (TextView) findViewById(R.id.download_position); - downloadTrackTextView = (TextView) findViewById(R.id.download_track); - downloadTotalDurationTextView = (TextView) findViewById(R.id.download_total_duration); - durationTextView = (TextView) findViewById(R.id.download_duration); - progressBar = (SeekBar) findViewById(R.id.download_progress_bar); - playlistView = (DragSortListView) findViewById(R.id.download_list); - final AutoRepeatButton previousButton = (AutoRepeatButton) findViewById(R.id.download_previous); - final AutoRepeatButton nextButton = (AutoRepeatButton) findViewById(R.id.download_next); - pauseButton = findViewById(R.id.download_pause); - stopButton = findViewById(R.id.download_stop); - startButton = findViewById(R.id.download_start); - final View shuffleButton = findViewById(R.id.download_shuffle); - repeatButton = (ImageView) findViewById(R.id.download_repeat); - - visualizerViewLayout = (LinearLayout) findViewById(R.id.download_visualizer_view_layout); - - ratingLinearLayout = findViewById(R.id.song_rating); - fiveStar1ImageView = findViewById(R.id.song_five_star_1); - fiveStar2ImageView = findViewById(R.id.song_five_star_2); - fiveStar3ImageView = findViewById(R.id.song_five_star_3); - fiveStar4ImageView = findViewById(R.id.song_five_star_4); - fiveStar5ImageView = findViewById(R.id.song_five_star_5); - - if (!useFiveStarRating) ratingLinearLayout.setVisibility(View.GONE); - - hollowStar = Util.getDrawableFromAttribute(this, R.attr.star_hollow); - fullStar = Util.getDrawableFromAttribute(this, R.attr.star_full); - - fiveStar1ImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) { - setSongRating(1); - } - }); - fiveStar2ImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) { - setSongRating(2); - } - }); - fiveStar3ImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) { - setSongRating(3); - } - }); - fiveStar4ImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) { - setSongRating(4); - } - }); - fiveStar5ImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) { - setSongRating(5); - } - }); - - View.OnTouchListener touchListener = new View.OnTouchListener() - { - @Override - public boolean onTouch(View view, MotionEvent me) - { - return gestureScanner.onTouchEvent(me); - } - }; - - albumArtImageView.setOnTouchListener(touchListener); - - albumArtImageView.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - toggleFullScreenAlbumArt(); - } - }); - - previousButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - warnIfNetworkOrStorageUnavailable(); - - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - getMediaPlayerController().previous(); - return null; - } - - @Override - protected void done(final Void result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - }.execute(); - } - }); - - previousButton.setOnRepeatListener(new Runnable() - { - @Override - public void run() - { - int incrementTime = Util.getIncrementTime(DownloadActivity.this); - changeProgress(-incrementTime); - } - }); - - nextButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - warnIfNetworkOrStorageUnavailable(); - - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Boolean doInBackground() throws Throwable - { - if (getMediaPlayerController().getCurrentPlayingNumberOnPlaylist() < getMediaPlayerController().getPlaylistSize() - 1) - { - getMediaPlayerController().next(); - return true; - } - else - { - return false; - } - } - - @Override - protected void done(final Boolean result) - { - if (result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - } - }.execute(); - } - }); - - nextButton.setOnRepeatListener(new Runnable() - { - @Override - public void run() - { - int incrementTime = Util.getIncrementTime(DownloadActivity.this); - changeProgress(incrementTime); - } - }); - - pauseButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - getMediaPlayerController().pause(); - return null; - } - - @Override - protected void done(final Void result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - }.execute(); - } - }); - - stopButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - getMediaPlayerController().reset(); - return null; - } - - @Override - protected void done(final Void result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - }.execute(); - } - }); - - startButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - warnIfNetworkOrStorageUnavailable(); - - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - start(); - return null; - } - - @Override - protected void done(final Void result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - }.execute(); - } - }); - - shuffleButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - getMediaPlayerController().shuffle(); - Util.toast(DownloadActivity.this, R.string.download_menu_shuffle_notification); - } - }); - - repeatButton.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(final View view) - { - final RepeatMode repeatMode = getMediaPlayerController().getRepeatMode().next(); - - getMediaPlayerController().setRepeatMode(repeatMode); - onDownloadListChanged(); - - switch (repeatMode) - { - case OFF: - Util.toast(DownloadActivity.this, R.string.download_repeat_off); - break; - case ALL: - Util.toast(DownloadActivity.this, R.string.download_repeat_all); - break; - case SINGLE: - Util.toast(DownloadActivity.this, R.string.download_repeat_single); - break; - default: - break; - } - } - }); - - progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() - { - @Override - public void onStopTrackingTouch(final SeekBar seekBar) - { - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - getMediaPlayerController().seekTo(getProgressBar().getProgress()); - return null; - } - - @Override - protected void done(final Void result) - { - onSliderProgressChanged(); - } - }.execute(); - } - - @Override - public void onStartTrackingTouch(final SeekBar seekBar) - { - } - - @Override - public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) - { - } - }); - - playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() - { - @Override - public void onItemClick(final AdapterView parent, final View view, final int position, final long id) - { - warnIfNetworkOrStorageUnavailable(); - - new SilentBackgroundTask(DownloadActivity.this) - { - @Override - protected Void doInBackground() throws Throwable - { - getMediaPlayerController().play(position); - return null; - } - - @Override - protected void done(final Void result) - { - onCurrentChanged(); - onSliderProgressChanged(); - } - }.execute(); - } - }); - - registerForContextMenu(playlistView); - - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (mediaPlayerController != null && getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) - { - warnIfNetworkOrStorageUnavailable(); - mediaPlayerController.setShufflePlayEnabled(true); - } - - visualizerViewLayout.setVisibility(View.GONE); - VisualizerController.get().observe(this, new Observer() { - @Override - public void onChanged(VisualizerController visualizerController) { - if (visualizerController != null) { - Timber.d("VisualizerController Observer.onChanged received controller"); - visualizerView = new VisualizerView(DownloadActivity.this); - visualizerViewLayout.addView(visualizerView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); - - if (!visualizerView.isActive()) - { - visualizerViewLayout.setVisibility(View.GONE); - } - else - { - visualizerViewLayout.setVisibility(View.VISIBLE); - } - - visualizerView.setOnTouchListener(new View.OnTouchListener() - { - @Override - public boolean onTouch(final View view, final MotionEvent motionEvent) - { - visualizerView.setActive(!visualizerView.isActive()); - getMediaPlayerController().setShowVisualization(visualizerView.isActive()); - return true; - } - }); - isVisualizerAvailable = true; - } else { - Timber.d("VisualizerController Observer.onChanged has no controller"); - visualizerViewLayout.setVisibility(View.GONE); - isVisualizerAvailable = false; - } - } - }); - - EqualizerController.get().observe(this, new Observer() { - @Override - public void onChanged(EqualizerController equalizerController) { - if (equalizerController != null) { - Timber.d("EqualizerController Observer.onChanged received controller"); - isEqualizerAvailable = true; - } else { - Timber.d("EqualizerController Observer.onChanged has no controller"); - isEqualizerAvailable = false; - } - } - }); - - new Thread(new Runnable() - { - @Override - public void run() - { - try - { - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - jukeboxAvailable = (mediaPlayerController != null) && (mediaPlayerController.isJukeboxAvailable()); - } - catch (Exception e) - { - Timber.e(e); - } - } - }).start(); - - final View nowPlayingMenuItem = findViewById(R.id.menu_now_playing); - menuDrawer.setActiveView(nowPlayingMenuItem); - } - - @Override - protected void onResume() - { - super.onResume(); - - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController == null || mediaPlayerController.getCurrentPlaying() == null) - { - playlistFlipper.setDisplayedChild(1); - } - - final Handler handler = new Handler(); - final Runnable runnable = new Runnable() - { - @Override - public void run() - { - handler.post(new Runnable() - { - @Override - public void run() - { - update(); - } - }); - } - }; - - executorService = Executors.newSingleThreadScheduledExecutor(); - executorService.scheduleWithFixedDelay(runnable, 0L, 250L, TimeUnit.MILLISECONDS); - - if (mediaPlayerController != null && mediaPlayerController.getKeepScreenOn()) - { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - else - { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - - if (visualizerView != null) - { - visualizerView.setActive(mediaPlayerController != null && mediaPlayerController.getShowVisualization()); - } - - invalidateOptionsMenu(); - } - - // Scroll to current playing/downloading. - private void scrollToCurrent() - { - if (getMediaPlayerController() == null) - { - return; - } - - ListAdapter adapter = playlistView.getAdapter(); - - if (adapter != null) - { - int count = adapter.getCount(); - - for (int i = 0; i < count; i++) - { - if (currentPlaying == playlistView.getItemAtPosition(i)) - { - playlistView.smoothScrollToPositionFromTop(i, 40); - return; - } - } - - final DownloadFile currentDownloading = getMediaPlayerController().getCurrentDownloading(); - for (int i = 0; i < count; i++) - { - if (currentDownloading == playlistView.getItemAtPosition(i)) - { - playlistView.smoothScrollToPositionFromTop(i, 40); - return; - } - } - } - } - - @Override - protected void onPause() - { - super.onPause(); - executorService.shutdown(); - - if (visualizerView != null) - { - visualizerView.setActive(false); - } - } - - @Override - protected Dialog onCreateDialog(final int id) - { - if (id == DIALOG_SAVE_PLAYLIST) - { - final AlertDialog.Builder builder; - - final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - final View layout = layoutInflater.inflate(R.layout.save_playlist, (ViewGroup) findViewById(R.id.save_playlist_root)); - - if (layout != null) - { - playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name); - } - - builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.download_playlist_title); - builder.setMessage(R.string.download_playlist_name); - builder.setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() - { - @Override - public void onClick(final DialogInterface dialog, final int clickId) - { - savePlaylistInBackground(String.valueOf(playlistNameView.getText())); - } - }); - builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() - { - @Override - public void onClick(final DialogInterface dialog, final int clickId) - { - dialog.cancel(); - } - }); - builder.setView(layout); - builder.setCancelable(true); - - return builder.create(); - } - else - { - return super.onCreateDialog(id); - } - } - - @Override - protected void onPrepareDialog(final int id, final Dialog dialog) - { - if (id == DIALOG_SAVE_PLAYLIST) - { - final String playlistName = (getMediaPlayerController() != null) ? getMediaPlayerController().getSuggestedPlaylistName() : null; - if (playlistName != null) - { - playlistNameView.setText(playlistName); - } - else - { - final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - playlistNameView.setText(dateFormat.format(new Date())); - } - } - } - - @Override - public boolean onCreateOptionsMenu(final Menu menu) - { - final MenuInflater menuInflater = getMenuInflater(); - menuInflater.inflate(R.menu.nowplaying, menu); - super.onCreateOptionsMenu(menu); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(final Menu menu) - { - super.onPrepareOptionsMenu(menu); - - final MenuItem screenOption = menu.findItem(R.id.menu_item_screen_on_off); - final MenuItem jukeboxOption = menu.findItem(R.id.menu_item_jukebox); - final MenuItem equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer); - final MenuItem visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer); - final MenuItem shareMenuItem = menu.findItem(R.id.menu_item_share); - starMenuItem = menu.findItem(R.id.menu_item_star); - MenuItem bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set); - MenuItem bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete); - - - if (ActiveServerProvider.Companion.isOffline(this)) - { - if (shareMenuItem != null) - { - shareMenuItem.setVisible(false); - } - - if (starMenuItem != null) - { - starMenuItem.setVisible(false); - } - - if (bookmarkMenuItem != null) - { - bookmarkMenuItem.setVisible(false); - } - - if (bookmarkRemoveMenuItem != null) - { - bookmarkRemoveMenuItem.setVisible(false); - } - } - - if (equalizerMenuItem != null) - { - equalizerMenuItem.setEnabled(isEqualizerAvailable); - equalizerMenuItem.setVisible(isEqualizerAvailable); - } - - if (visualizerMenuItem != null) - { - visualizerMenuItem.setEnabled(isVisualizerAvailable); - visualizerMenuItem.setVisible(isVisualizerAvailable); - } - - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController != null) - { - DownloadFile downloadFile = mediaPlayerController.getCurrentPlaying(); - - if (downloadFile != null) - { - currentSong = downloadFile.getSong(); - } - - if (useFiveStarRating) starMenuItem.setVisible(false); - - if (currentSong != null) - { - if (starMenuItem != null) - { - starMenuItem.setIcon(currentSong.getStarred() ? fullStar : hollowStar); - } - } - else - { - if (starMenuItem != null) - { - starMenuItem.setIcon(hollowStar); - } - } - - - if (mediaPlayerController.getKeepScreenOn()) - { - if (screenOption != null) - { - screenOption.setTitle(R.string.download_menu_screen_off); - } - } - else - { - if (screenOption != null) - { - screenOption.setTitle(R.string.download_menu_screen_on); - } - } - - if (jukeboxOption != null) - { - jukeboxOption.setEnabled(jukeboxAvailable); - jukeboxOption.setVisible(jukeboxAvailable); - - if (mediaPlayerController.isJukeboxEnabled()) - { - jukeboxOption.setTitle(R.string.download_menu_jukebox_off); - } - else - { - jukeboxOption.setTitle(R.string.download_menu_jukebox_on); - } - } - } - - return true; - } - - @Override - public void onCreateContextMenu(final ContextMenu menu, final View view, final ContextMenu.ContextMenuInfo menuInfo) - { - super.onCreateContextMenu(menu, view, menuInfo); - if (view == playlistView) - { - final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - final DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); - - final MenuInflater menuInflater = getMenuInflater(); - menuInflater.inflate(R.menu.nowplaying_context, menu); - - Entry song = null; - - if (downloadFile != null) - { - song = downloadFile.getSong(); - } - - if (song != null && song.getParent() == null) - { - MenuItem menuItem = menu.findItem(R.id.menu_show_album); - - if (menuItem != null) - { - menuItem.setVisible(false); - } - } - - if (ActiveServerProvider.Companion.isOffline(this) || !Util.getShouldUseId3Tags(this)) - { - MenuItem menuItem = menu.findItem(R.id.menu_show_artist); - - if (menuItem != null) - { - menuItem.setVisible(false); - } - } - - if (ActiveServerProvider.Companion.isOffline(this)) - { - MenuItem menuItem = menu.findItem(R.id.menu_lyrics); - - if (menuItem != null) - { - menuItem.setVisible(false); - } - } - } - } - - @Override - public boolean onContextItemSelected(final MenuItem menuItem) - { - final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - - DownloadFile downloadFile = null; - - if (info != null) - { - downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); - } - - return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem); - } - - @Override - public boolean onOptionsItemSelected(final MenuItem menuItem) - { - return menuItemSelected(menuItem.getItemId(), null) || super.onOptionsItemSelected(menuItem); - } - - private boolean menuItemSelected(final int menuItemId, final DownloadFile song) - { - Entry entry = null; - - if (song != null) - { - entry = song.getSong(); - } - - switch (menuItemId) - { - case R.id.menu_show_artist: - if (entry == null) - { - return false; - } - - if (Util.getShouldUseId3Tags(DownloadActivity.this)) - { - Intent intent = new Intent(DownloadActivity.this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getArtistId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true); - startActivityForResultWithoutTransition(DownloadActivity.this, intent); - } - - return true; - case R.id.menu_show_album: - if (entry == null) - { - return false; - } - - Intent intent = new Intent(this, SelectAlbumActivity.class); - String albumId = Util.getShouldUseId3Tags(this) ? entry.getAlbumId() : entry.getParent(); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true); - intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent()); - startActivityForResultWithoutTransition(this, intent); - return true; - case R.id.menu_lyrics: - if (entry == null) - { - return false; - } - - intent = new Intent(this, LyricsActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, entry.getArtist()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_TITLE, entry.getTitle()); - startActivityForResultWithoutTransition(this, intent); - return true; - case R.id.menu_remove: - getMediaPlayerController().remove(song); - onDownloadListChanged(); - return true; - case R.id.menu_item_screen_on_off: - if (getMediaPlayerController().getKeepScreenOn()) - { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - getMediaPlayerController().setKeepScreenOn(false); - } - else - { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - getMediaPlayerController().setKeepScreenOn(true); - } - return true; - case R.id.menu_shuffle: - getMediaPlayerController().shuffle(); - Util.toast(this, R.string.download_menu_shuffle_notification); - return true; - case R.id.menu_item_equalizer: - startActivity(new Intent(DownloadActivity.this, EqualizerActivity.class)); - return true; - case R.id.menu_item_visualizer: - final boolean active = !visualizerView.isActive(); - visualizerView.setActive(active); - - if (!visualizerView.isActive()) - { - visualizerViewLayout.setVisibility(View.GONE); - } - else - { - visualizerViewLayout.setVisibility(View.VISIBLE); - } - - getMediaPlayerController().setShowVisualization(visualizerView.isActive()); - Util.toast(DownloadActivity.this, active ? R.string.download_visualizer_on : R.string.download_visualizer_off); - return true; - case R.id.menu_item_jukebox: - final boolean jukeboxEnabled = !getMediaPlayerController().isJukeboxEnabled(); - getMediaPlayerController().setJukeboxEnabled(jukeboxEnabled); - Util.toast(DownloadActivity.this, jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false); - return true; - case R.id.menu_item_toggle_list: - toggleFullScreenAlbumArt(); - return true; - case R.id.menu_item_clear_playlist: - getMediaPlayerController().setShufflePlayEnabled(false); - getMediaPlayerController().clear(); - onDownloadListChanged(); - return true; - case R.id.menu_item_save_playlist: - if (getMediaPlayerController().getPlaylistSize() > 0) - { - showDialog(DIALOG_SAVE_PLAYLIST); - } - return true; - case R.id.menu_item_star: - if (currentSong == null) - { - return true; - } - - final boolean isStarred = currentSong.getStarred(); - final String id = currentSong.getId(); - - if (isStarred) - { - starMenuItem.setIcon(hollowStar); - currentSong.setStarred(false); - } - else - { - starMenuItem.setIcon(fullStar); - currentSong.setStarred(true); - } - - new Thread(new Runnable() - { - @Override - public void run() - { - final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); - - try - { - if (isStarred) - { - musicService.unstar(id, null, null, DownloadActivity.this, null); - } - else - { - musicService.star(id, null, null, DownloadActivity.this, null); - } - } - catch (Exception e) - { - Timber.e(e); - } - } - }).start(); - - return true; - case R.id.menu_item_bookmark_set: - if (currentSong == null) - { - return true; - } - - final String songId = currentSong.getId(); - final int playerPosition = getMediaPlayerController().getPlayerPosition(); - - currentSong.setBookmarkPosition(playerPosition); - - String bookmarkTime = Util.formatTotalDuration(playerPosition, true); - - new Thread(new Runnable() - { - @Override - public void run() - { - final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); - - try - { - musicService.createBookmark(songId, playerPosition, DownloadActivity.this, null); - } - catch (Exception e) - { - Timber.e(e); - } - } - }).start(); - - String msg = getResources().getString(R.string.download_bookmark_set_at_position, bookmarkTime); - - Util.toast(DownloadActivity.this, msg); - - return true; - case R.id.menu_item_bookmark_delete: - if (currentSong == null) - { - return true; - } - - final String bookmarkSongId = currentSong.getId(); - currentSong.setBookmarkPosition(0); - - new Thread(new Runnable() - { - @Override - public void run() - { - final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); - - try - { - musicService.deleteBookmark(bookmarkSongId, DownloadActivity.this, null); - } - catch (Exception e) - { - Timber.e(e); - } - } - }).start(); - - Util.toast(DownloadActivity.this, R.string.download_bookmark_removed); - - return true; - case R.id.menu_item_share: - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - List entries = new ArrayList(); - - if (mediaPlayerController != null) - { - List downloadServiceSongs = mediaPlayerController.getPlayList(); - - if (downloadServiceSongs != null) - { - for (DownloadFile downloadFile : downloadServiceSongs) - { - if (downloadFile != null) - { - Entry playlistEntry = downloadFile.getSong(); - - if (playlistEntry != null) - { - entries.add(playlistEntry); - } - } - } - } - } - - createShare(entries); - return true; - default: - return false; - } - } - - private void update() - { - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (mediaPlayerController == null) - { - return; - } - - if (currentRevision != mediaPlayerController.getPlayListUpdateRevision()) - { - onDownloadListChanged(); - } - - if (currentPlaying != mediaPlayerController.getCurrentPlaying()) - { - onCurrentChanged(); - } - - onSliderProgressChanged(); - invalidateOptionsMenu(); - } - - private void savePlaylistInBackground(final String playlistName) - { - Util.toast(DownloadActivity.this, getResources().getString(R.string.download_playlist_saving, playlistName)); - getMediaPlayerController().setSuggestedPlaylistName(playlistName); - new SilentBackgroundTask(this) - { - @Override - protected Void doInBackground() throws Throwable - { - final List entries = new LinkedList(); - for (final DownloadFile downloadFile : getMediaPlayerController().getPlayList()) - { - entries.add(downloadFile.getSong()); - } - final MusicService musicService = MusicServiceFactory.getMusicService(DownloadActivity.this); - musicService.createPlaylist(null, playlistName, entries, DownloadActivity.this, null); - return null; - } - - @Override - protected void done(final Void result) - { - Util.toast(DownloadActivity.this, R.string.download_playlist_done); - } - - @Override - protected void error(final Throwable error) - { - Timber.e(error, "Exception has occurred in savePlaylistInBackground"); - final String msg = String.format("%s %s", getResources().getString(R.string.download_playlist_error), getErrorMessage(error)); - Util.toast(DownloadActivity.this, msg); - } - }.execute(); - } - - private void toggleFullScreenAlbumArt() - { - if (playlistFlipper.getDisplayedChild() == 1) - { - playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_down_in)); - playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_down_out)); - playlistFlipper.setDisplayedChild(0); - } - else - { - playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_up_in)); - playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_up_out)); - playlistFlipper.setDisplayedChild(1); - } - - scrollToCurrent(); - } - - private void start() - { - final MediaPlayerController service = getMediaPlayerController(); - final PlayerState state = service.getPlayerState(); - - if (state == PAUSED || state == COMPLETED || state == STOPPED) - { - service.start(); - } - else if (state == IDLE) - { - warnIfNetworkOrStorageUnavailable(); - - final int current = getMediaPlayerController().getCurrentPlayingNumberOnPlaylist(); - - if (current == -1) - { - service.play(0); - } - else - { - service.play(current); - } - } - } - - private void onDownloadListChanged() - { - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (mediaPlayerController == null) - { - return; - } - - final List list = mediaPlayerController.getPlayList(); - - emptyTextView.setText(R.string.download_empty); - final SongListAdapter adapter = new SongListAdapter(this, list); - playlistView.setAdapter(adapter); - - playlistView.setDragSortListener(new DragSortListView.DragSortListener() - { - @Override - public void drop(int from, int to) - { - if (from != to) - { - DownloadFile item = adapter.getItem(from); - adapter.remove(item); - adapter.notifyDataSetChanged(); - adapter.insert(item, to); - adapter.notifyDataSetChanged(); - } - } - - @Override - public void drag(int from, int to) - { - - } - - @Override - public void remove(int which) - { - DownloadFile item = adapter.getItem(which); - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (item == null || mediaPlayerController == null) - { - return; - } - - DownloadFile currentPlaying = mediaPlayerController.getCurrentPlaying(); - - if (currentPlaying == item) - { - getMediaPlayerController().next(); - } - - adapter.remove(item); - adapter.notifyDataSetChanged(); - - String songRemoved = String.format(getResources().getString(R.string.download_song_removed), item.getSong().getTitle()); - - Util.toast(DownloadActivity.this, songRemoved); - - onDownloadListChanged(); - onCurrentChanged(); - } - }); - - emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); - currentRevision = mediaPlayerController.getPlayListUpdateRevision(); - - switch (mediaPlayerController.getRepeatMode()) - { - case OFF: - repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_off)); - break; - case ALL: - repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_all)); - break; - case SINGLE: - repeatButton.setImageDrawable(Util.getDrawableFromAttribute(this, R.attr.media_repeat_single)); - break; - default: - break; - } - } - - private void onCurrentChanged() - { - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController == null) - { - return; - } - - currentPlaying = mediaPlayerController.getCurrentPlaying(); - - scrollToCurrent(); - - long totalDuration = mediaPlayerController.getPlayListDuration(); - long totalSongs = mediaPlayerController.getPlaylistSize(); - int currentSongIndex = mediaPlayerController.getCurrentPlayingNumberOnPlaylist() + 1; - - String duration = Util.formatTotalDuration(totalDuration); - - String trackFormat = String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs); - - if (currentPlaying != null) - { - currentSong = currentPlaying.getSong(); - songTitleTextView.setText(currentSong.getTitle()); - albumTextView.setText(currentSong.getAlbum()); - artistTextView.setText(currentSong.getArtist()); - downloadTrackTextView.setText(trackFormat); - downloadTotalDurationTextView.setText(duration); - imageLoader.getValue().getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true); - - displaySongRating(); - } - else - { - currentSong = null; - songTitleTextView.setText(null); - albumTextView.setText(null); - artistTextView.setText(null); - downloadTrackTextView.setText(null); - downloadTotalDurationTextView.setText(null); - imageLoader.getValue().getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true); - } - } - - private void onSliderProgressChanged() - { - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController == null || onProgressChangedTask != null) - { - return; - } - - onProgressChangedTask = new SilentBackgroundTask(this) - { - MediaPlayerController mediaPlayerController; - boolean isJukeboxEnabled; - int millisPlayed; - Integer duration; - PlayerState playerState; - - @Override - protected Void doInBackground() throws Throwable - { - this.mediaPlayerController = getMediaPlayerController(); - isJukeboxEnabled = this.mediaPlayerController.isJukeboxEnabled(); - millisPlayed = Math.max(0, this.mediaPlayerController.getPlayerPosition()); - duration = this.mediaPlayerController.getPlayerDuration(); - playerState = getMediaPlayerController().getPlayerState(); - return null; - } - - @Override - protected void done(final Void result) - { - if (currentPlaying != null) - { - final int millisTotal = duration == null ? 0 : duration; - - positionTextView.setText(Util.formatTotalDuration(millisPlayed, true)); - durationTextView.setText(Util.formatTotalDuration(millisTotal, true)); - progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug. - progressBar.setProgress(millisPlayed); - progressBar.setEnabled(currentPlaying.isWorkDone() || isJukeboxEnabled); - } - else - { - positionTextView.setText(R.string.util_zero_time); - durationTextView.setText(R.string.util_no_time); - progressBar.setProgress(0); - progressBar.setMax(0); - progressBar.setEnabled(false); - } - - switch (playerState) - { - case DOWNLOADING: - final long bytes = currentPlaying != null ? currentPlaying.getPartialFile().length() : 0; - String downloadStatus = getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, DownloadActivity.this)); - setActionBarSubtitle(downloadStatus); - break; - case PREPARING: - setActionBarSubtitle(R.string.download_playerstate_buffering); - break; - case STARTED: - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController != null && mediaPlayerController.isShufflePlayEnabled()) - { - setActionBarSubtitle(R.string.download_playerstate_playing_shuffle); - } - else - { - setActionBarSubtitle(null); - } - break; - default: - setActionBarSubtitle(null); - break; - case IDLE: - break; - case PREPARED: - break; - case STOPPED: - break; - case PAUSED: - break; - case COMPLETED: - break; - } - - switch (playerState) - { - case STARTED: - pauseButton.setVisibility(View.VISIBLE); - stopButton.setVisibility(View.GONE); - startButton.setVisibility(View.GONE); - break; - case DOWNLOADING: - case PREPARING: - pauseButton.setVisibility(View.GONE); - stopButton.setVisibility(View.VISIBLE); - startButton.setVisibility(View.GONE); - break; - default: - pauseButton.setVisibility(View.GONE); - stopButton.setVisibility(View.GONE); - startButton.setVisibility(View.VISIBLE); - break; - } - - // TODO: It would be a lot nicer if MediaPlayerController would send an event when this is necessary instead of updating every time - displaySongRating(); - - onProgressChangedTask = null; - } - }; - onProgressChangedTask.execute(); - } - - private void changeProgress(final int ms) - { - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (mediaPlayerController == null) - { - return; - } - - new SilentBackgroundTask(this) - { - int msPlayed; - Integer duration; - int seekTo; - - @Override - protected Void doInBackground() throws Throwable - { - msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition()); - duration = mediaPlayerController.getPlayerDuration(); - - final int msTotal = duration; - seekTo = msPlayed + ms > msTotal ? msTotal : msPlayed + ms; - mediaPlayerController.seekTo(seekTo); - return null; - } - - @Override - protected void done(final Void result) - { - progressBar.setProgress(seekTo); - } - }.execute(); - } - - @Override - public boolean onTouchEvent(final MotionEvent me) - { - return gestureScanner.onTouchEvent(me); - } - - @Override - public boolean onDown(final MotionEvent me) - { - return false; - } - - @Override - public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) - { - - final MediaPlayerController mediaPlayerController = getMediaPlayerController(); - - if (mediaPlayerController == null || e1 == null || e2 == null) - { - return false; - } - - float e1X = e1.getX(); - float e2X = e2.getX(); - float e1Y = e1.getY(); - float e2Y = e2.getY(); - float absX = Math.abs(velocityX); - float absY = Math.abs(velocityY); - - // Right to Left swipe - if (e1X - e2X > swipeDistance && absX > swipeVelocity) - { - warnIfNetworkOrStorageUnavailable(); - if (mediaPlayerController.getCurrentPlayingNumberOnPlaylist() < mediaPlayerController.getPlaylistSize() - 1) - { - mediaPlayerController.next(); - onCurrentChanged(); - onSliderProgressChanged(); - } - return true; - } - - // Left to Right swipe - if (e2X - e1X > swipeDistance && absX > swipeVelocity) - { - warnIfNetworkOrStorageUnavailable(); - mediaPlayerController.previous(); - onCurrentChanged(); - onSliderProgressChanged(); - return true; - } - - // Top to Bottom swipe - if (e2Y - e1Y > swipeDistance && absY > swipeVelocity) - { - warnIfNetworkOrStorageUnavailable(); - mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() + 30000); - onSliderProgressChanged(); - return true; - } - - // Bottom to Top swipe - if (e1Y - e2Y > swipeDistance && absY > swipeVelocity) - { - warnIfNetworkOrStorageUnavailable(); - mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() - 8000); - onSliderProgressChanged(); - return true; - } - - return false; - } - - @Override - public void onLongPress(final MotionEvent e) - { - } - - @Override - public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) - { - return false; - } - - @Override - public void onShowPress(final MotionEvent e) - { - } - - @Override - public boolean onSingleTapUp(final MotionEvent e) - { - return false; - } - - public static SeekBar getProgressBar() - { - return progressBar; - } - - private void displaySongRating() - { - int rating = currentSong == null || currentSong.getUserRating() == null ? 0 : currentSong.getUserRating(); - fiveStar1ImageView.setImageDrawable(rating > 0 ? fullStar : hollowStar); - fiveStar2ImageView.setImageDrawable(rating > 1 ? fullStar : hollowStar); - fiveStar3ImageView.setImageDrawable(rating > 2 ? fullStar : hollowStar); - fiveStar4ImageView.setImageDrawable(rating > 3 ? fullStar : hollowStar); - fiveStar5ImageView.setImageDrawable(rating > 4 ? fullStar : hollowStar); - } - - private void setSongRating(final int rating) - { - if (currentSong == null) - return; - - displaySongRating(); - getMediaPlayerController().setSongRating(rating); - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/LyricsActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/LyricsActivity.java deleted file mode 100644 index f067c576..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/LyricsActivity.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ - -package org.moire.ultrasonic.activity; - -import android.os.Bundle; -import android.view.View; -import android.widget.TextView; - -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.domain.Lyrics; -import org.moire.ultrasonic.service.MusicService; -import org.moire.ultrasonic.service.MusicServiceFactory; -import org.moire.ultrasonic.util.BackgroundTask; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.TabActivityBackgroundTask; - -/** - * Displays song lyrics. - * - * @author Sindre Mehus - */ -public final class LyricsActivity extends SubsonicTabActivity -{ - - @Override - protected void onCreate(Bundle bundle) - { - super.onCreate(bundle); - setContentView(R.layout.lyrics); - - View nowPlayingMenuItem = findViewById(R.id.menu_now_playing); - menuDrawer.setActiveView(nowPlayingMenuItem); - - load(); - } - - private void load() - { - BackgroundTask task = new TabActivityBackgroundTask(this, true) - { - @Override - protected Lyrics doInBackground() throws Throwable - { - String artist = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ARTIST); - String title = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_TITLE); - MusicService musicService = MusicServiceFactory.getMusicService(LyricsActivity.this); - return musicService.getLyrics(artist, title, LyricsActivity.this, this); - } - - @Override - protected void done(Lyrics result) - { - TextView artistView = (TextView) findViewById(R.id.lyrics_artist); - TextView titleView = (TextView) findViewById(R.id.lyrics_title); - TextView textView = (TextView) findViewById(R.id.lyrics_text); - - if (result != null && result.getArtist() != null) - { - artistView.setText(result.getArtist()); - titleView.setText(result.getTitle()); - textView.setText(result.getText()); - } - else - { - artistView.setText(R.string.lyrics_nomatch); - } - - } - }; - task.execute(); - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/MainActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/MainActivity.java deleted file mode 100644 index 946bef64..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/MainActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ - -package org.moire.ultrasonic.activity; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.TextView; - -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.data.ServerSetting; -import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.FileUtil; -import org.moire.ultrasonic.util.MergeAdapter; -import org.moire.ultrasonic.util.Util; - -import java.util.Collections; - -import kotlin.Lazy; - -import static java.util.Arrays.asList; -import static org.koin.android.viewmodel.compat.ViewModelCompat.viewModel; -import static org.koin.java.KoinJavaComponent.inject; - -public class MainActivity extends SubsonicTabActivity -{ - private static boolean infoDialogDisplayed; - private static boolean shouldUseId3; - private static String lastActiveServerProperties; - - private Lazy lifecycleSupport = inject(MediaPlayerLifecycleSupport.class); - private Lazy activeServerProvider = inject(ActiveServerProvider.class); - private Lazy serverSettingsModel = viewModel(this, ServerSettingsModel.class); - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(final Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - setContentView(R.layout.main); - - final View homeMenuItem = findViewById(R.id.menu_home); - menuDrawer.setActiveView(homeMenuItem); - - setActionBarTitle(R.string.common_appname); - setTitle(R.string.common_appname); - - // Remember the current theme. - theme = Util.getTheme(this); - } - - - @Override - public boolean onCreateOptionsMenu(final Menu menu) - { - final MenuInflater menuInflater = getMenuInflater(); - menuInflater.inflate(R.menu.main, menu); - super.onCreateOptionsMenu(menu); - - return true; - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) - { - switch (item.getItemId()) - { - case android.R.id.home: - menuDrawer.toggleMenu(); - return true; - case R.id.main_shuffle: - final Intent intent1 = new Intent(this, DownloadActivity.class); - intent1.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); - startActivityForResultWithoutTransition(this, intent1); - return true; - } - - return false; - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/QueryReceiverActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/QueryReceiverActivity.java deleted file mode 100644 index 4000e107..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/QueryReceiverActivity.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ - -package org.moire.ultrasonic.activity; - -import android.app.SearchManager; -import android.content.Intent; -import android.os.Bundle; -import android.provider.SearchRecentSuggestions; - -import org.moire.ultrasonic.provider.SearchSuggestionProvider; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.Util; - -/** - * Receives search queries and forwards to the SelectAlbumActivity. - * - * @author Sindre Mehus - */ -public class QueryReceiverActivity extends ResultActivity -{ - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - String query = getIntent().getStringExtra(SearchManager.QUERY); - - if (query != null) - { - SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE); - suggestions.saveRecentQuery(query, null); - - Intent intent = new Intent(QueryReceiverActivity.this, SearchActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_QUERY, query); - startActivityForResultWithoutTransition(QueryReceiverActivity.this, intent); - } - finish(); - Util.disablePendingTransition(this); - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SearchActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SearchActivity.java deleted file mode 100644 index ee4015dc..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SearchActivity.java +++ /dev/null @@ -1,554 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ - -package org.moire.ultrasonic.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.domain.Artist; -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.domain.MusicDirectory.Entry; -import org.moire.ultrasonic.domain.SearchCriteria; -import org.moire.ultrasonic.domain.SearchResult; -import org.moire.ultrasonic.service.MediaPlayerController; -import org.moire.ultrasonic.service.MusicService; -import org.moire.ultrasonic.service.MusicServiceFactory; -import org.moire.ultrasonic.subsonic.VideoPlayer; -import org.moire.ultrasonic.util.BackgroundTask; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.MergeAdapter; -import org.moire.ultrasonic.util.TabActivityBackgroundTask; -import org.moire.ultrasonic.util.Util; -import org.moire.ultrasonic.view.ArtistAdapter; -import org.moire.ultrasonic.view.EntryAdapter; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import kotlin.Lazy; - -import static org.koin.java.KoinJavaComponent.inject; - -/** - * Performs searches and displays the matching artists, albums and songs. - * - * @author Sindre Mehus - */ -public class SearchActivity extends SubsonicTabActivity -{ - - private static int DEFAULT_ARTISTS; - private static int DEFAULT_ALBUMS; - private static int DEFAULT_SONGS; - - private ListView list; - - private View artistsHeading; - private View albumsHeading; - private View songsHeading; - private TextView searchButton; - private View moreArtistsButton; - private View moreAlbumsButton; - private View moreSongsButton; - private SearchResult searchResult; - private MergeAdapter mergeAdapter; - private ArtistAdapter artistAdapter; - private ListAdapter moreArtistsAdapter; - private EntryAdapter albumAdapter; - private ListAdapter moreAlbumsAdapter; - private ListAdapter moreSongsAdapter; - private EntryAdapter songAdapter; - - private final Lazy videoPlayer = inject(VideoPlayer.class); - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - setContentView(R.layout.search); - - setActionBarTitle(R.string.common_appname); - setActionBarSubtitle(R.string.search_title); - - View searchMenuItem = findViewById(R.id.menu_search); - menuDrawer.setActiveView(searchMenuItem); - - DEFAULT_ARTISTS = Util.getDefaultArtists(this); - DEFAULT_ALBUMS = Util.getDefaultAlbums(this); - DEFAULT_SONGS = Util.getDefaultSongs(this); - - View buttons = LayoutInflater.from(this).inflate(R.layout.search_buttons, null); - - if (buttons != null) - { - artistsHeading = buttons.findViewById(R.id.search_artists); - albumsHeading = buttons.findViewById(R.id.search_albums); - songsHeading = buttons.findViewById(R.id.search_songs); - searchButton = (TextView) buttons.findViewById(R.id.search_search); - moreArtistsButton = buttons.findViewById(R.id.search_more_artists); - moreAlbumsButton = buttons.findViewById(R.id.search_more_albums); - moreSongsButton = buttons.findViewById(R.id.search_more_songs); - } - - list = (ListView) findViewById(R.id.search_list); - - list.setOnItemClickListener(new AdapterView.OnItemClickListener() - { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) - { - if (view == searchButton) - { - onSearchRequested(); - } - else if (view == moreArtistsButton) - { - expandArtists(); - } - else if (view == moreAlbumsButton) - { - expandAlbums(); - } - else if (view == moreSongsButton) - { - expandSongs(); - } - else - { - Object item = parent.getItemAtPosition(position); - if (item instanceof Artist) - { - onArtistSelected((Artist) item); - } - else if (item instanceof MusicDirectory.Entry) - { - MusicDirectory.Entry entry = (MusicDirectory.Entry) item; - if (entry.isDirectory()) - { - onAlbumSelected(entry, false); - } - else if (entry.isVideo()) - { - onVideoSelected(entry); - } - else - { - onSongSelected(entry, false, true, true, false); - } - - } - } - } - }); - - registerForContextMenu(list); - - onNewIntent(getIntent()); - } - - @Override - protected void onNewIntent(Intent intent) - { - super.onNewIntent(intent); - String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY); - boolean autoPlay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); - boolean requestSearch = intent.getBooleanExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, false); - - if (query != null) - { - mergeAdapter = new MergeAdapter(); - list.setAdapter(mergeAdapter); - search(query, autoPlay); - } - else - { - populateList(); - if (requestSearch) onSearchRequested(); - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) - { - super.onCreateContextMenu(menu, view, menuInfo); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object selectedItem = list.getItemAtPosition(info.position); - - boolean isArtist = selectedItem instanceof Artist; - boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory(); - - if (!isArtist && !isAlbum) - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_song_context, menu); - } - else - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_album_context, menu); - } - - MenuItem shareButton = menu.findItem(R.id.menu_item_share); - MenuItem downloadMenuItem = menu.findItem(R.id.album_menu_download); - - if (downloadMenuItem != null) - { - downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline(this)); - } - - if (ActiveServerProvider.Companion.isOffline(this) || isArtist) - { - if (shareButton != null) - { - shareButton.setVisible(false); - } - } - } - - @Override - public boolean onContextItemSelected(MenuItem menuItem) - { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - - if (info == null) - { - return true; - } - - Object selectedItem = list.getItemAtPosition(info.position); - - Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null; - Entry entry = selectedItem instanceof Entry ? (Entry) selectedItem : null; - - String entryId = null; - - if (entry != null) - { - entryId = entry.getId(); - } - - String id = artist != null ? artist.getId() : entryId; - - if (id == null) - { - return true; - } - - List songs = new ArrayList(1); - - switch (menuItem.getItemId()) - { - case R.id.album_menu_play_now: - downloadRecursively(id, false, false, true, false, false, false, false, false); - break; - case R.id.album_menu_play_next: - downloadRecursively(id, false, true, false, true, false, true, false, false); - break; - case R.id.album_menu_play_last: - downloadRecursively(id, false, true, false, false, false, false, false, false); - break; - case R.id.album_menu_pin: - downloadRecursively(id, true, true, false, false, false, false, false, false); - break; - case R.id.album_menu_unpin: - downloadRecursively(id, false, false, false, false, false, false, true, false); - break; - case R.id.album_menu_download: - downloadRecursively(id, false, false, false, false, true, false, false, false); - break; - case R.id.song_menu_play_now: - if (entry != null) - { - songs = new ArrayList(1); - songs.add(entry); - download(false, false, true, false, false, songs); - } - break; - case R.id.song_menu_play_next: - if (entry != null) - { - songs = new ArrayList(1); - songs.add(entry); - download(true, false, false, true, false, songs); - } - break; - case R.id.song_menu_play_last: - if (entry != null) - { - songs = new ArrayList(1); - songs.add(entry); - download(true, false, false, false, false, songs); - } - break; - case R.id.song_menu_pin: - if (entry != null) - { - songs.add(entry); - Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); - downloadBackground(true, songs); - } - break; - case R.id.song_menu_download: - if (entry != null) - { - songs.add(entry); - Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size())); - downloadBackground(false, songs); - } - break; - case R.id.song_menu_unpin: - if (entry != null) - { - songs.add(entry); - Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size())); - getMediaPlayerController().unpin(songs); - } - break; - case R.id.menu_item_share: - if (entry != null) - { - songs = new ArrayList(1); - songs.add(entry); - createShare(songs); - } - default: - return super.onContextItemSelected(menuItem); - } - - return true; - } - - private void downloadBackground(final boolean save, final List songs) - { - if (getMediaPlayerController() == null) - { - return; - } - - Runnable onValid = new Runnable() - { - @Override - public void run() - { - warnIfNetworkOrStorageUnavailable(); - getMediaPlayerController().downloadBackground(songs, save); - } - }; - - checkLicenseAndTrialPeriod(onValid); - } - - private void search(final String query, final boolean autoplay) - { - final int maxArtists = Util.getMaxArtists(this); - final int maxAlbums = Util.getMaxAlbums(this); - final int maxSongs = Util.getMaxSongs(this); - - BackgroundTask task = new TabActivityBackgroundTask(this, true) - { - @Override - protected SearchResult doInBackground() throws Throwable - { - SearchCriteria criteria = new SearchCriteria(query, maxArtists, maxAlbums, maxSongs); - MusicService service = MusicServiceFactory.getMusicService(SearchActivity.this); - licenseValid = service.isLicenseValid(SearchActivity.this, this); - return service.search(criteria, SearchActivity.this, this); - } - - @Override - protected void done(SearchResult result) - { - searchResult = result; - - populateList(); - - if (autoplay) - { - autoplay(); - } - - } - }; - task.execute(); - } - - private void populateList() - { - mergeAdapter = new MergeAdapter(); - mergeAdapter.addView(searchButton, true); - - if (searchResult != null) - { - List artists = searchResult.getArtists(); - if (!artists.isEmpty()) - { - mergeAdapter.addView(artistsHeading); - List displayedArtists = new ArrayList(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size()))); - artistAdapter = new ArtistAdapter(this, displayedArtists); - mergeAdapter.addAdapter(artistAdapter); - if (artists.size() > DEFAULT_ARTISTS) - { - moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true); - } - } - - List albums = searchResult.getAlbums(); - if (!albums.isEmpty()) - { - mergeAdapter.addView(albumsHeading); - List displayedAlbums = new ArrayList(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size()))); - albumAdapter = new EntryAdapter(this, imageLoader.getValue().getImageLoader(), displayedAlbums, false); - mergeAdapter.addAdapter(albumAdapter); - if (albums.size() > DEFAULT_ALBUMS) - { - moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true); - } - } - - List songs = searchResult.getSongs(); - if (!songs.isEmpty()) - { - mergeAdapter.addView(songsHeading); - List displayedSongs = new ArrayList(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size()))); - songAdapter = new EntryAdapter(this, imageLoader.getValue().getImageLoader(), displayedSongs, false); - mergeAdapter.addAdapter(songAdapter); - if (songs.size() > DEFAULT_SONGS) - { - moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true); - } - } - - boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty(); - searchButton.setText(empty ? R.string.search_no_match : R.string.search_search); - } - - list.setAdapter(mergeAdapter); - } - - private void expandArtists() - { - artistAdapter.clear(); - - for (Artist artist : searchResult.getArtists()) - { - artistAdapter.add(artist); - } - - artistAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreArtistsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void expandAlbums() - { - albumAdapter.clear(); - - for (MusicDirectory.Entry album : searchResult.getAlbums()) - { - albumAdapter.add(album); - } - - albumAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreAlbumsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void expandSongs() - { - songAdapter.clear(); - - for (MusicDirectory.Entry song : searchResult.getSongs()) - { - songAdapter.add(song); - } - - songAdapter.notifyDataSetChanged(); - mergeAdapter.removeAdapter(moreSongsAdapter); - mergeAdapter.notifyDataSetChanged(); - } - - private void onArtistSelected(Artist artist) - { - Intent intent = new Intent(this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, artist.getId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, artist.getName()); - startActivityForResultWithoutTransition(this, intent); - } - - private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) - { - Intent intent = new Intent(SearchActivity.this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, album.getId()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, album.isDirectory()); - intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay); - startActivityForResultWithoutTransition(SearchActivity.this, intent); - } - - private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) - { - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (mediaPlayerController != null) - { - if (!append && !playNext) - { - mediaPlayerController.clear(); - } - - mediaPlayerController.download(Collections.singletonList(song), save, false, playNext, false, false); - - if (autoplay) - { - mediaPlayerController.play(mediaPlayerController.getPlaylistSize() - 1); - } - - Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1)); - } - } - - private void onVideoSelected(MusicDirectory.Entry entry) - { - videoPlayer.getValue().playVideo(entry); - } - - private void autoplay() - { - if (!searchResult.getSongs().isEmpty()) - { - onSongSelected(searchResult.getSongs().get(0), false, false, true, false); - } - else if (!searchResult.getAlbums().isEmpty()) - { - onAlbumSelected(searchResult.getAlbums().get(0), true); - } - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java deleted file mode 100644 index 59a35d22..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ -package org.moire.ultrasonic.activity; - -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import org.moire.ultrasonic.R; -import org.moire.ultrasonic.data.ActiveServerProvider; -import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.domain.Share; -import org.moire.ultrasonic.service.DownloadFile; -import org.moire.ultrasonic.service.MediaPlayerController; -import org.moire.ultrasonic.service.MusicService; -import org.moire.ultrasonic.service.MusicServiceFactory; -import org.moire.ultrasonic.util.AlbumHeader; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator; -import org.moire.ultrasonic.util.Pair; -import org.moire.ultrasonic.util.TabActivityBackgroundTask; -import org.moire.ultrasonic.util.Util; -import org.moire.ultrasonic.view.AlbumView; -import org.moire.ultrasonic.view.EntryAdapter; -import org.moire.ultrasonic.view.SongView; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Random; - -public class SelectAlbumActivity extends SubsonicTabActivity -{ - - public static final String allSongsId = "-1"; - private SwipeRefreshLayout refreshAlbumListView; - private ListView albumListView; - private View header; - private View albumButtons; - private View emptyView; - private ImageView selectButton; - private ImageView playNowButton; - private ImageView playNextButton; - private ImageView playLastButton; - private ImageView pinButton; - private ImageView unpinButton; - private ImageView downloadButton; - private ImageView deleteButton; - private ImageView moreButton; - private boolean playAllButtonVisible; - private boolean shareButtonVisible; - private MenuItem playAllButton; - private MenuItem shareButton; - private boolean showHeader = true; - private Random random = new java.security.SecureRandom(); - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.select_album); - - - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) - { - super.onPrepareOptionsMenu(menu); - playAllButton = menu.findItem(R.id.select_album_play_all); - - if (playAllButton != null) - { - playAllButton.setVisible(playAllButtonVisible); - } - - shareButton = menu.findItem(R.id.menu_item_share); - - if (shareButton != null) - { - shareButton.setVisible(shareButtonVisible); - } - - return true; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.select_album, menu); - super.onCreateOptionsMenu(menu); - - return true; - } - - - - - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - switch (item.getItemId()) - { - case android.R.id.home: - menuDrawer.toggleMenu(); - return true; - case R.id.main_shuffle: - Intent intent1 = new Intent(this, DownloadActivity.class); - intent1.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); - startActivityForResultWithoutTransition(this, intent1); - return true; - case R.id.select_album_play_all: - // TODO - //playAll(); - return true; - case R.id.menu_item_share: - // TODO - //createShare(getSelectedSongs(albumListView)); - return true; - } - - return false; - } - - -} 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 d972d2a7..3809896a 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java @@ -48,6 +48,7 @@ import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.featureflags.Feature; import org.moire.ultrasonic.featureflags.FeatureStorage; +import org.moire.ultrasonic.fragment.SelectAlbumFragment; import org.moire.ultrasonic.service.*; import org.moire.ultrasonic.subsonic.ImageLoaderProvider; import org.moire.ultrasonic.subsonic.SubsonicImageLoaderProxy; @@ -344,7 +345,8 @@ public class SubsonicTabActivity extends ResultActivity } }); - final Intent intent = new Intent(context, SelectAlbumActivity.class); + // TODO: Refactor to use navigation + final Intent intent = new Intent(context, SelectAlbumFragment.class);// SelectAlbumActivity.class); if (Util.getShouldUseId3Tags(context)) { @@ -595,51 +597,6 @@ public class SubsonicTabActivity extends ResultActivity task.execute(); } - public void setTextViewTextOnUiThread(final RemoteViews view, final int id, final CharSequence text) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null) - { - view.setTextViewText(id, text); - } - } - }); - } - - public void setImageViewBitmapOnUiThread(final RemoteViews view, final int id, final Bitmap bitmap) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null) - { - view.setImageViewBitmap(id, bitmap); - } - } - }); - } - - public void setImageViewResourceOnUiThread(final RemoteViews view, final int id, final int resource) - { - this.runOnUiThread(new Runnable() - { - @Override - public void run() - { - if (view != null) - { - view.setImageViewResource(id, resource); - } - } - }); - } - public void setOnTouchListenerOnUiThread(final View view, final OnTouchListener listener) { this.runOnUiThread(new Runnable() @@ -720,29 +677,6 @@ public class SubsonicTabActivity extends ResultActivity return instance; } - public boolean getIsDestroyed() - { - return destroyed; - } - - public void setProgressVisible(boolean visible) - { - View view = findViewById(R.id.tab_progress); - if (view != null) - { - view.setVisibility(visible ? View.VISIBLE : View.GONE); - } - } - - public void updateProgress(CharSequence message) - { - TextView view = (TextView) findViewById(R.id.tab_progress_message); - if (view != null) - { - view.setText(message); - } - } - public MediaPlayerController getMediaPlayerController() { return mediaPlayerControllerLazy.getValue(); @@ -760,292 +694,6 @@ public class SubsonicTabActivity extends ResultActivity } } - void download(final boolean append, final boolean save, final boolean autoPlay, final boolean playNext, final boolean shuffle, final List songs) - { - if (getMediaPlayerController() == null) - { - return; - } - - Runnable onValid = new Runnable() - { - @Override - public void run() - { - if (!append && !playNext) - { - getMediaPlayerController().clear(); - } - - warnIfNetworkOrStorageUnavailable(); - getMediaPlayerController().download(songs, save, autoPlay, playNext, shuffle, false); - String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); - - if (playlistName != null) - { - getMediaPlayerController().setSuggestedPlaylistName(playlistName); - } - - if (autoPlay) - { - if (Util.getShouldTransitionOnPlaybackPreference(SubsonicTabActivity.this)) - { - startActivityForResultWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); - } - } - else if (save) - { - Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); - } - else if (playNext) - { - Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_play_next, songs.size(), songs.size())); - } - else if (append) - { - Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size())); - } - } - }; - - checkLicenseAndTrialPeriod(onValid); - } - - protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin, final boolean isArtist) - { - downloadRecursively(id, "", false, true, save, append, autoplay, shuffle, background, playNext, unpin, isArtist); - } - - protected void downloadShare(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin) - { - downloadRecursively(id, name, true, false, save, append, autoplay, shuffle, background, playNext, unpin, false); - } - - protected void downloadPlaylist(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin) - { - downloadRecursively(id, name, false, false, save, append, autoplay, shuffle, background, playNext, unpin, false); - } - - protected void downloadRecursively(final String id, final String name, final boolean isShare, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin, final boolean isArtist) - { - ModalBackgroundTask> task = new ModalBackgroundTask>(this, false) - { - private static final int MAX_SONGS = 500; - - @Override - protected List doInBackground() throws Throwable - { - MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); - List songs = new LinkedList(); - MusicDirectory root; - - if (!ActiveServerProvider.Companion.isOffline(SubsonicTabActivity.this) && isArtist && Util.getShouldUseId3Tags(SubsonicTabActivity.this)) - { - getSongsForArtist(id, songs); - } - else - { - if (isDirectory) - { - root = !ActiveServerProvider.Companion.isOffline(SubsonicTabActivity.this) && Util.getShouldUseId3Tags(SubsonicTabActivity.this) ? musicService.getAlbum(id, name, false, SubsonicTabActivity.this, this) : musicService.getMusicDirectory(id, name, false, SubsonicTabActivity.this, this); - } - else if (isShare) - { - root = new MusicDirectory(); - - List shares = musicService.getShares(true, SubsonicTabActivity.this, this); - - for (Share share : shares) - { - if (share.getId().equals(id)) - { - for (Entry entry : share.getEntries()) - { - root.addChild(entry); - } - - break; - } - } - } - else - { - root = musicService.getPlaylist(id, name, SubsonicTabActivity.this, this); - } - - getSongsRecursively(root, songs); - } - - return songs; - } - - private void getSongsRecursively(MusicDirectory parent, List songs) throws Exception - { - if (songs.size() > MAX_SONGS) - { - return; - } - - for (Entry song : parent.getChildren(false, true)) - { - if (!song.isVideo()) - { - songs.add(song); - } - } - - MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); - - for (Entry dir : parent.getChildren(true, false)) - { - MusicDirectory root; - - root = !ActiveServerProvider.Companion.isOffline(SubsonicTabActivity.this) && Util.getShouldUseId3Tags(SubsonicTabActivity.this) ? musicService.getAlbum(dir.getId(), dir.getTitle(), false, SubsonicTabActivity.this, this) : musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, SubsonicTabActivity.this, this); - - getSongsRecursively(root, songs); - } - } - - private void getSongsForArtist(String id, Collection songs) throws Exception - { - if (songs.size() > MAX_SONGS) - { - return; - } - - MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); - MusicDirectory artist = musicService.getArtist(id, "", false, SubsonicTabActivity.this, this); - - for (Entry album : artist.getChildren()) - { - MusicDirectory albumDirectory = musicService.getAlbum(album.getId(), "", false, SubsonicTabActivity.this, this); - - for (Entry song : albumDirectory.getChildren()) - { - if (!song.isVideo()) - { - songs.add(song); - } - } - } - } - - @Override - protected void done(List songs) - { - if (Util.getShouldSortByDisc(SubsonicTabActivity.this)) - { - Collections.sort(songs, new EntryByDiscAndTrackComparator()); - } - - MediaPlayerController mediaPlayerController = getMediaPlayerController(); - if (!songs.isEmpty() && mediaPlayerController != null) - { - if (!append && !playNext && !unpin && !background) - { - mediaPlayerController.clear(); - } - warnIfNetworkOrStorageUnavailable(); - if (!background) - { - if (unpin) - { - mediaPlayerController.unpin(songs); - } - else - { - mediaPlayerController.download(songs, save, autoplay, playNext, shuffle, false); - if (!append && Util.getShouldTransitionOnPlaybackPreference(SubsonicTabActivity.this)) - { - startActivityForResultWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); - } - } - } - else - { - if (unpin) - { - mediaPlayerController.unpin(songs); - } - else - { - mediaPlayerController.downloadBackground(songs, save); - } - } - } - } - }; - - task.execute(); - } - - protected void checkLicenseAndTrialPeriod(Runnable onValid) - { - if (licenseValid) - { - onValid.run(); - return; - } - - int trialDaysLeft = Util.getRemainingTrialDays(this); - Timber.i("%s trial days left.", trialDaysLeft); - - if (trialDaysLeft == 0) - { - showDonationDialog(trialDaysLeft, null); - } - else if (trialDaysLeft < Constants.FREE_TRIAL_DAYS / 2) - { - showDonationDialog(trialDaysLeft, onValid); - } - else - { - Util.toast(this, getResources().getString(R.string.select_album_not_licensed, trialDaysLeft)); - onValid.run(); - } - } - - private void showDonationDialog(int trialDaysLeft, final Runnable onValid) - { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIcon(android.R.drawable.ic_dialog_info); - - if (trialDaysLeft == 0) - { - builder.setTitle(R.string.select_album_donate_dialog_0_trial_days_left); - } - else - { - builder.setTitle(getResources().getQuantityString(R.plurals.select_album_donate_dialog_n_trial_days_left, trialDaysLeft, trialDaysLeft)); - } - - builder.setMessage(R.string.select_album_donate_dialog_message); - - builder.setPositiveButton(R.string.select_album_donate_dialog_now, new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialogInterface, int i) - { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.DONATION_URL))); - } - }); - - builder.setNegativeButton(R.string.select_album_donate_dialog_later, new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialogInterface, int i) - { - dialogInterface.dismiss(); - if (onValid != null) - { - onValid.run(); - } - } - }); - - builder.create().show(); - } - protected void setActionBarDisplayHomeAsUp(boolean enabled) { ActionBar actionBar = getSupportActionBar(); @@ -1056,39 +704,6 @@ public class SubsonicTabActivity extends ResultActivity } } - protected void setActionBarTitle(CharSequence title) - { - ActionBar actionBar = getSupportActionBar(); - - if (actionBar != null) - { - actionBar.setTitle(title); - } - } - - protected void setActionBarTitle(int id) - { - ActionBar actionBar = getSupportActionBar(); - - if (actionBar != null) - { - actionBar.setTitle(id); - } - } - - protected CharSequence getActionBarTitle() - { - ActionBar actionBar = getSupportActionBar(); - CharSequence title = null; - - if (actionBar != null) - { - title = actionBar.getTitle(); - } - - return title; - } - protected void setActionBarSubtitle(CharSequence title) { ActionBar actionBar = getSupportActionBar(); @@ -1109,19 +724,6 @@ public class SubsonicTabActivity extends ResultActivity } } - protected CharSequence getActionBarSubtitle() - { - ActionBar actionBar = getSupportActionBar(); - CharSequence subtitle = null; - - if (actionBar != null) - { - subtitle = actionBar.getSubtitle(); - } - - return subtitle; - } - private void setUncaughtExceptionHandler() { Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); @@ -1272,7 +874,8 @@ public class SubsonicTabActivity extends ResultActivity } } - SubsonicTabActivity.this.startActivityForResultWithoutTransition(activity, DownloadActivity.class); + // TODO: Refactor this to Navigation. It should automatically go to the PlayerFragment. + //SubsonicTabActivity.this.startActivityForResultWithoutTransition(activity, DownloadActivity.class); return false; } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/VoiceQueryReceiverActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/VoiceQueryReceiverActivity.java deleted file mode 100644 index fd51bf45..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/VoiceQueryReceiverActivity.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2009 (C) Sindre Mehus - */ - -package org.moire.ultrasonic.activity; - -import android.app.SearchManager; -import android.content.Intent; -import android.os.Bundle; -import android.provider.SearchRecentSuggestions; - -import org.moire.ultrasonic.provider.SearchSuggestionProvider; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.Util; - -/** - * Receives voice search queries and forwards to the SearchActivity. - *

- * http://android-developers.blogspot.com/2010/09/supporting-new-music-voice-action.html - * - * @author Sindre Mehus - */ -public class VoiceQueryReceiverActivity extends ResultActivity -{ - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - String query = getIntent().getStringExtra(SearchManager.QUERY); - - if (query != null) - { - SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE); - suggestions.saveRecentQuery(query, null); - - Intent intent = new Intent(VoiceQueryReceiverActivity.this, SearchActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_QUERY, query); - intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true); - startActivityForResultWithoutTransition(VoiceQueryReceiverActivity.this, intent); - } - finish(); - Util.disablePendingTransition(this); - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/LyricsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/LyricsFragment.java new file mode 100644 index 00000000..3948aaf0 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/LyricsFragment.java @@ -0,0 +1,98 @@ +package org.moire.ultrasonic.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.Lyrics; +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; + +import timber.log.Timber; + +public class LyricsFragment extends Fragment { + + private TextView artistView; + private TextView titleView; + private TextView textView; + private SwipeRefreshLayout swipe; + private CancellationToken cancellationToken; + + @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.lyrics, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + cancellationToken = new CancellationToken(); + Timber.d("Lyrics set title"); + FragmentTitle.Companion.setTitle(this, R.string.download_menu_lyrics); + + swipe = view.findViewById(R.id.lyrics_refresh); + swipe.setEnabled(false); + artistView = view.findViewById(R.id.lyrics_artist); + titleView = view.findViewById(R.id.lyrics_title); + textView = view.findViewById(R.id.lyrics_text); + + load(); + } + + @Override + public void onDestroyView() { + cancellationToken.cancel(); + super.onDestroyView(); + } + + private void load() + { + BackgroundTask task = new TabActivityBackgroundTask(getActivity(), true, swipe, cancellationToken) + { + @Override + protected Lyrics doInBackground() throws Throwable + { + String artist = getArguments().getString(Constants.INTENT_EXTRA_NAME_ARTIST); + String title = getArguments().getString(Constants.INTENT_EXTRA_NAME_TITLE); + MusicService musicService = MusicServiceFactory.getMusicService(getContext()); + return musicService.getLyrics(artist, title, getContext(), this); + } + + @Override + protected void done(Lyrics result) + { + if (result != null && result.getArtist() != null) + { + artistView.setText(result.getArtist()); + titleView.setText(result.getTitle()); + textView.setText(result.getText()); + } + else + { + artistView.setText(R.string.lyrics_nomatch); + } + + } + }; + task.execute(); + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java new file mode 100644 index 00000000..f1d9ebb0 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java @@ -0,0 +1,1693 @@ +package org.moire.ultrasonic.fragment; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.view.ContextMenu; +import android.view.Display; +import android.view.GestureDetector; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.ViewFlipper; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.navigation.Navigation; + +import com.mobeta.android.dslv.DragSortListView; + +import org.jetbrains.annotations.NotNull; +import org.koin.java.KoinJavaComponent; +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.activity.EqualizerActivity; +import org.moire.ultrasonic.audiofx.EqualizerController; +import org.moire.ultrasonic.audiofx.VisualizerController; +import org.moire.ultrasonic.data.ActiveServerProvider; +import org.moire.ultrasonic.domain.MusicDirectory; +import org.moire.ultrasonic.domain.PlayerState; +import org.moire.ultrasonic.domain.RepeatMode; +import org.moire.ultrasonic.featureflags.Feature; +import org.moire.ultrasonic.featureflags.FeatureStorage; +import org.moire.ultrasonic.service.DownloadFile; +import org.moire.ultrasonic.service.MediaPlayerController; +import org.moire.ultrasonic.service.MusicService; +import org.moire.ultrasonic.service.MusicServiceFactory; +import org.moire.ultrasonic.subsonic.ImageLoaderProvider; +import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker; +import org.moire.ultrasonic.subsonic.ShareHandler; +import org.moire.ultrasonic.util.CancellationToken; +import org.moire.ultrasonic.util.Constants; +import org.moire.ultrasonic.util.SilentBackgroundTask; +import org.moire.ultrasonic.util.Util; +import org.moire.ultrasonic.view.AutoRepeatButton; +import org.moire.ultrasonic.view.SongListAdapter; +import org.moire.ultrasonic.view.VisualizerView; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import kotlin.Lazy; +import timber.log.Timber; + +import static android.content.Context.LAYOUT_INFLATER_SERVICE; +import static org.koin.java.KoinJavaComponent.inject; +import static org.moire.ultrasonic.domain.PlayerState.COMPLETED; +import static org.moire.ultrasonic.domain.PlayerState.IDLE; +import static org.moire.ultrasonic.domain.PlayerState.PAUSED; +import static org.moire.ultrasonic.domain.PlayerState.STOPPED; + +public class PlayerFragment extends Fragment implements GestureDetector.OnGestureListener { + + private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5; + + private ViewFlipper playlistFlipper; + private TextView emptyTextView; + private TextView songTitleTextView; + private TextView albumTextView; + private TextView artistTextView; + private ImageView albumArtImageView; + private DragSortListView playlistView; + private TextView positionTextView; + private TextView downloadTrackTextView; + private TextView downloadTotalDurationTextView; + private TextView durationTextView; + private static SeekBar progressBar; // TODO: Refactor this to not be static + private View pauseButton; + private View stopButton; + private View startButton; + private ImageView repeatButton; + private ScheduledExecutorService executorService; + private DownloadFile currentPlaying; + private MusicDirectory.Entry currentSong; + private long currentRevision; + private EditText playlistNameView; + private GestureDetector gestureScanner; + private int swipeDistance; + private int swipeVelocity; + private VisualizerView visualizerView; + private boolean jukeboxAvailable; + private SilentBackgroundTask onProgressChangedTask; + LinearLayout visualizerViewLayout; + private MenuItem starMenuItem; + private LinearLayout ratingLinearLayout; + private ImageView fiveStar1ImageView; + private ImageView fiveStar2ImageView; + private ImageView fiveStar3ImageView; + private ImageView fiveStar4ImageView; + private ImageView fiveStar5ImageView; + private boolean useFiveStarRating; + private Drawable hollowStar; + private Drawable fullStar; + private CancellationToken cancellationToken; + + private boolean isEqualizerAvailable; + private boolean isVisualizerAvailable; + + private final Lazy networkAndStorageChecker = inject(NetworkAndStorageChecker.class); + private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); + private final Lazy shareHandler = inject(ShareHandler.class); + private final Lazy imageLoaderProvider = inject(ImageLoaderProvider.class); + + @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.download, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + cancellationToken = new CancellationToken(); + FragmentTitle.Companion.setTitle(this, R.string.common_appname); + + final WindowManager windowManager = getActivity().getWindowManager(); + final Display display = windowManager.getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + int width = size.x; + int height = size.y; + + setHasOptionsMenu(true); + + useFiveStarRating = KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING); + + swipeDistance = (width + height) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100; + swipeVelocity = swipeDistance; + gestureScanner = new GestureDetector(getContext(), this); + + playlistFlipper = view.findViewById(R.id.download_playlist_flipper); + emptyTextView = view.findViewById(R.id.download_empty); + songTitleTextView = view.findViewById(R.id.download_song_title); + albumTextView = view.findViewById(R.id.download_album); + artistTextView = view.findViewById(R.id.download_artist); + albumArtImageView = view.findViewById(R.id.download_album_art_image); + positionTextView = view.findViewById(R.id.download_position); + downloadTrackTextView = view.findViewById(R.id.download_track); + downloadTotalDurationTextView = view.findViewById(R.id.download_total_duration); + durationTextView = view.findViewById(R.id.download_duration); + progressBar = view.findViewById(R.id.download_progress_bar); + playlistView = view.findViewById(R.id.download_list); + final AutoRepeatButton previousButton = view.findViewById(R.id.download_previous); + final AutoRepeatButton nextButton = view.findViewById(R.id.download_next); + pauseButton = view.findViewById(R.id.download_pause); + stopButton = view.findViewById(R.id.download_stop); + startButton = view.findViewById(R.id.download_start); + final View shuffleButton = view.findViewById(R.id.download_shuffle); + repeatButton = view.findViewById(R.id.download_repeat); + + visualizerViewLayout = view.findViewById(R.id.download_visualizer_view_layout); + + ratingLinearLayout = view.findViewById(R.id.song_rating); + fiveStar1ImageView = view.findViewById(R.id.song_five_star_1); + fiveStar2ImageView = view.findViewById(R.id.song_five_star_2); + fiveStar3ImageView = view.findViewById(R.id.song_five_star_3); + fiveStar4ImageView = view.findViewById(R.id.song_five_star_4); + fiveStar5ImageView = view.findViewById(R.id.song_five_star_5); + + if (!useFiveStarRating) ratingLinearLayout.setVisibility(View.GONE); + + hollowStar = Util.getDrawableFromAttribute(getContext(), R.attr.star_hollow); + fullStar = Util.getDrawableFromAttribute(getContext(), R.attr.star_full); + + fiveStar1ImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) { + setSongRating(1); + } + }); + fiveStar2ImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) { + setSongRating(2); + } + }); + fiveStar3ImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) { + setSongRating(3); + } + }); + fiveStar4ImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) { + setSongRating(4); + } + }); + fiveStar5ImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) { + setSongRating(5); + } + }); + + View.OnTouchListener touchListener = new View.OnTouchListener() + { + @Override + public boolean onTouch(View view, MotionEvent me) + { + return gestureScanner.onTouchEvent(me); + } + }; + + albumArtImageView.setOnTouchListener(touchListener); + + albumArtImageView.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + toggleFullScreenAlbumArt(); + } + }); + + previousButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + mediaPlayerControllerLazy.getValue().previous(); + return null; + } + + @Override + protected void done(final Void result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + }.execute(); + } + }); + + previousButton.setOnRepeatListener(new Runnable() + { + @Override + public void run() + { + int incrementTime = Util.getIncrementTime(getActivity()); + changeProgress(-incrementTime); + } + }); + + nextButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + + new SilentBackgroundTask(getActivity()) + { + @Override + protected Boolean doInBackground() throws Throwable + { + if (mediaPlayerControllerLazy.getValue().getCurrentPlayingNumberOnPlaylist() < mediaPlayerControllerLazy.getValue().getPlaylistSize() - 1) + { + mediaPlayerControllerLazy.getValue().next(); + return true; + } + else + { + return false; + } + } + + @Override + protected void done(final Boolean result) + { + if (result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + } + }.execute(); + } + }); + + nextButton.setOnRepeatListener(new Runnable() + { + @Override + public void run() + { + int incrementTime = Util.getIncrementTime(getActivity()); + changeProgress(incrementTime); + } + }); + + pauseButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + mediaPlayerControllerLazy.getValue().pause(); + return null; + } + + @Override + protected void done(final Void result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + }.execute(); + } + }); + + stopButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + mediaPlayerControllerLazy.getValue().reset(); + return null; + } + + @Override + protected void done(final Void result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + }.execute(); + } + }); + + startButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + start(); + return null; + } + + @Override + protected void done(final Void result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + }.execute(); + } + }); + + shuffleButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + mediaPlayerControllerLazy.getValue().shuffle(); + Util.toast(getActivity(), R.string.download_menu_shuffle_notification); + } + }); + + repeatButton.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(final View view) + { + final RepeatMode repeatMode = mediaPlayerControllerLazy.getValue().getRepeatMode().next(); + + mediaPlayerControllerLazy.getValue().setRepeatMode(repeatMode); + onDownloadListChanged(); + + switch (repeatMode) + { + case OFF: + Util.toast(getContext(), R.string.download_repeat_off); + break; + case ALL: + Util.toast(getContext(), R.string.download_repeat_all); + break; + case SINGLE: + Util.toast(getContext(), R.string.download_repeat_single); + break; + default: + break; + } + } + }); + + progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() + { + @Override + public void onStopTrackingTouch(final SeekBar seekBar) + { + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + mediaPlayerControllerLazy.getValue().seekTo(getProgressBar().getProgress()); + return null; + } + + @Override + protected void done(final Void result) + { + onSliderProgressChanged(); + } + }.execute(); + } + + @Override + public void onStartTrackingTouch(final SeekBar seekBar) + { + } + + @Override + public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) + { + } + }); + + playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() + { + @Override + public void onItemClick(final AdapterView parent, final View view, final int position, final long id) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + mediaPlayerControllerLazy.getValue().play(position); + return null; + } + + @Override + protected void done(final Void result) + { + onCurrentChanged(); + onSliderProgressChanged(); + } + }.execute(); + } + }); + + registerForContextMenu(playlistView); + + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + if (mediaPlayerController != null && getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + mediaPlayerController.setShufflePlayEnabled(true); + } + + visualizerViewLayout.setVisibility(View.GONE); + VisualizerController.get().observe(getActivity(), new Observer() { + @Override + public void onChanged(VisualizerController visualizerController) { + if (visualizerController != null) { + Timber.d("VisualizerController Observer.onChanged received controller"); + visualizerView = new VisualizerView(getContext()); + visualizerViewLayout.addView(visualizerView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); + + if (!visualizerView.isActive()) + { + visualizerViewLayout.setVisibility(View.GONE); + } + else + { + visualizerViewLayout.setVisibility(View.VISIBLE); + } + + visualizerView.setOnTouchListener(new View.OnTouchListener() + { + @Override + public boolean onTouch(final View view, final MotionEvent motionEvent) + { + visualizerView.setActive(!visualizerView.isActive()); + mediaPlayerControllerLazy.getValue().setShowVisualization(visualizerView.isActive()); + return true; + } + }); + isVisualizerAvailable = true; + } else { + Timber.d("VisualizerController Observer.onChanged has no controller"); + visualizerViewLayout.setVisibility(View.GONE); + isVisualizerAvailable = false; + } + } + }); + + EqualizerController.get().observe(getActivity(), new Observer() { + @Override + public void onChanged(EqualizerController equalizerController) { + if (equalizerController != null) { + Timber.d("EqualizerController Observer.onChanged received controller"); + isEqualizerAvailable = true; + } else { + Timber.d("EqualizerController Observer.onChanged has no controller"); + isEqualizerAvailable = false; + } + } + }); + + new Thread(new Runnable() + { + @Override + public void run() + { + try + { + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + jukeboxAvailable = (mediaPlayerController != null) && (mediaPlayerController.isJukeboxAvailable()); + } + catch (Exception e) + { + Timber.e(e); + } + } + }).start(); + + view.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureScanner.onTouchEvent(event); + } + }); + } + + @Override + public void onResume() + { + super.onResume(); + + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController == null || mediaPlayerController.getCurrentPlaying() == null) + { + playlistFlipper.setDisplayedChild(1); + } + else + { + // Download list and Album art must be updated when Resumed + onDownloadListChanged(); + onCurrentChanged(); + } + + + final Handler handler = new Handler(); + final Runnable runnable = new Runnable() + { + @Override + public void run() + { + handler.post(new Runnable() + { + @Override + public void run() + { + update(cancellationToken); + } + }); + } + }; + + executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.scheduleWithFixedDelay(runnable, 0L, 250L, TimeUnit.MILLISECONDS); + + if (mediaPlayerController != null && mediaPlayerController.getKeepScreenOn()) + { + getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + else + { + getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + if (visualizerView != null) + { + visualizerView.setActive(mediaPlayerController != null && mediaPlayerController.getShowVisualization()); + } + + getActivity().invalidateOptionsMenu(); + } + + // Scroll to current playing/downloading. + private void scrollToCurrent() + { + ListAdapter adapter = playlistView.getAdapter(); + + if (adapter != null) + { + int count = adapter.getCount(); + + for (int i = 0; i < count; i++) + { + if (currentPlaying == playlistView.getItemAtPosition(i)) + { + playlistView.smoothScrollToPositionFromTop(i, 40); + return; + } + } + + final DownloadFile currentDownloading = mediaPlayerControllerLazy.getValue().getCurrentDownloading(); + for (int i = 0; i < count; i++) + { + if (currentDownloading == playlistView.getItemAtPosition(i)) + { + playlistView.smoothScrollToPositionFromTop(i, 40); + return; + } + } + } + } + + @Override + public void onPause() + { + super.onPause(); + executorService.shutdown(); + + if (visualizerView != null) + { + visualizerView.setActive(false); + } + } + + @Override + public void onDestroyView() { + cancellationToken.cancel(); + super.onDestroyView(); + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + inflater.inflate(R.menu.nowplaying, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public void onPrepareOptionsMenu(@NotNull Menu menu) { + super.onPrepareOptionsMenu(menu); + + final MenuItem screenOption = menu.findItem(R.id.menu_item_screen_on_off); + final MenuItem jukeboxOption = menu.findItem(R.id.menu_item_jukebox); + final MenuItem equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer); + final MenuItem visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer); + final MenuItem shareMenuItem = menu.findItem(R.id.menu_item_share); + starMenuItem = menu.findItem(R.id.menu_item_star); + MenuItem bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set); + MenuItem bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete); + + + if (ActiveServerProvider.Companion.isOffline(getContext())) + { + if (shareMenuItem != null) + { + shareMenuItem.setVisible(false); + } + + if (starMenuItem != null) + { + starMenuItem.setVisible(false); + } + + if (bookmarkMenuItem != null) + { + bookmarkMenuItem.setVisible(false); + } + + if (bookmarkRemoveMenuItem != null) + { + bookmarkRemoveMenuItem.setVisible(false); + } + } + + if (equalizerMenuItem != null) + { + equalizerMenuItem.setEnabled(isEqualizerAvailable); + equalizerMenuItem.setVisible(isEqualizerAvailable); + } + + if (visualizerMenuItem != null) + { + visualizerMenuItem.setEnabled(isVisualizerAvailable); + visualizerMenuItem.setVisible(isVisualizerAvailable); + } + + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController != null) + { + DownloadFile downloadFile = mediaPlayerController.getCurrentPlaying(); + + if (downloadFile != null) + { + currentSong = downloadFile.getSong(); + } + + if (useFiveStarRating) starMenuItem.setVisible(false); + + if (currentSong != null) + { + if (starMenuItem != null) + { + starMenuItem.setIcon(currentSong.getStarred() ? fullStar : hollowStar); + } + } + else + { + if (starMenuItem != null) + { + starMenuItem.setIcon(hollowStar); + } + } + + + if (mediaPlayerController.getKeepScreenOn()) + { + if (screenOption != null) + { + screenOption.setTitle(R.string.download_menu_screen_off); + } + } + else + { + if (screenOption != null) + { + screenOption.setTitle(R.string.download_menu_screen_on); + } + } + + if (jukeboxOption != null) + { + jukeboxOption.setEnabled(jukeboxAvailable); + jukeboxOption.setVisible(jukeboxAvailable); + + if (mediaPlayerController.isJukeboxEnabled()) + { + jukeboxOption.setTitle(R.string.download_menu_jukebox_off); + } + else + { + jukeboxOption.setTitle(R.string.download_menu_jukebox_on); + } + } + } + } + + @Override + public void onCreateContextMenu(final ContextMenu menu, final View view, final ContextMenu.ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, view, menuInfo); + if (view == playlistView) + { + final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + final DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); + + final MenuInflater menuInflater = getActivity().getMenuInflater(); + menuInflater.inflate(R.menu.nowplaying_context, menu); + + MusicDirectory.Entry song = null; + + if (downloadFile != null) + { + song = downloadFile.getSong(); + } + + if (song != null && song.getParent() == null) + { + MenuItem menuItem = menu.findItem(R.id.menu_show_album); + + if (menuItem != null) + { + menuItem.setVisible(false); + } + } + + if (ActiveServerProvider.Companion.isOffline(getContext()) || !Util.getShouldUseId3Tags(getContext())) + { + MenuItem menuItem = menu.findItem(R.id.menu_show_artist); + + if (menuItem != null) + { + menuItem.setVisible(false); + } + } + + if (ActiveServerProvider.Companion.isOffline(getContext())) + { + MenuItem menuItem = menu.findItem(R.id.menu_lyrics); + + if (menuItem != null) + { + menuItem.setVisible(false); + } + } + } + } + + @Override + public boolean onContextItemSelected(final MenuItem menuItem) + { + final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); + + DownloadFile downloadFile = null; + + if (info != null) + { + downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); + } + + return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return menuItemSelected(item.getItemId(), null) || super.onOptionsItemSelected(item); + } + + private boolean menuItemSelected(final int menuItemId, final DownloadFile song) + { + MusicDirectory.Entry entry = null; + Bundle bundle; + + if (song != null) + { + entry = song.getSong(); + } + + switch (menuItemId) + { + case R.id.menu_show_artist: + if (entry == null) + { + return false; + } + + if (Util.getShouldUseId3Tags(getContext())) + { + bundle = new Bundle(); + bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId()); + bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist()); + bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getArtistId()); + bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true); + Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle); + } + + return true; + case R.id.menu_show_album: + if (entry == null) + { + return false; + } + + String albumId = Util.getShouldUseId3Tags(getContext()) ? entry.getAlbumId() : entry.getParent(); + bundle = new Bundle(); + bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId); + bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum()); + bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent()); + bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true); + Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle); + return true; + case R.id.menu_lyrics: + if (entry == null) + { + return false; + } + + bundle = new Bundle(); + bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.getArtist()); + bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.getTitle()); + Navigation.findNavController(getView()).navigate(R.id.playerToLyrics, bundle); + return true; + case R.id.menu_remove: + mediaPlayerControllerLazy.getValue().remove(song); + onDownloadListChanged(); + return true; + case R.id.menu_item_screen_on_off: + if (mediaPlayerControllerLazy.getValue().getKeepScreenOn()) + { + getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mediaPlayerControllerLazy.getValue().setKeepScreenOn(false); + } + else + { + getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mediaPlayerControllerLazy.getValue().setKeepScreenOn(true); + } + return true; + case R.id.menu_shuffle: + mediaPlayerControllerLazy.getValue().shuffle(); + Util.toast(getContext(), R.string.download_menu_shuffle_notification); + return true; + case R.id.menu_item_equalizer: + startActivity(new Intent(getContext(), EqualizerActivity.class)); + return true; + case R.id.menu_item_visualizer: + final boolean active = !visualizerView.isActive(); + visualizerView.setActive(active); + + if (!visualizerView.isActive()) + { + visualizerViewLayout.setVisibility(View.GONE); + } + else + { + visualizerViewLayout.setVisibility(View.VISIBLE); + } + + mediaPlayerControllerLazy.getValue().setShowVisualization(visualizerView.isActive()); + Util.toast(getContext(), active ? R.string.download_visualizer_on : R.string.download_visualizer_off); + return true; + case R.id.menu_item_jukebox: + final boolean jukeboxEnabled = !mediaPlayerControllerLazy.getValue().isJukeboxEnabled(); + mediaPlayerControllerLazy.getValue().setJukeboxEnabled(jukeboxEnabled); + Util.toast(getContext(), jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false); + return true; + case R.id.menu_item_toggle_list: + toggleFullScreenAlbumArt(); + return true; + case R.id.menu_item_clear_playlist: + mediaPlayerControllerLazy.getValue().setShufflePlayEnabled(false); + mediaPlayerControllerLazy.getValue().clear(); + onDownloadListChanged(); + return true; + case R.id.menu_item_save_playlist: + if (mediaPlayerControllerLazy.getValue().getPlaylistSize() > 0) + { + showSavePlaylistDialog(); + } + return true; + case R.id.menu_item_star: + if (currentSong == null) + { + return true; + } + + final boolean isStarred = currentSong.getStarred(); + final String id = currentSong.getId(); + + if (isStarred) + { + starMenuItem.setIcon(hollowStar); + currentSong.setStarred(false); + } + else + { + starMenuItem.setIcon(fullStar); + currentSong.setStarred(true); + } + + new Thread(new Runnable() + { + @Override + public void run() + { + final MusicService musicService = MusicServiceFactory.getMusicService(getContext()); + + try + { + if (isStarred) + { + musicService.unstar(id, null, null, getContext(), null); + } + else + { + musicService.star(id, null, null, getContext(), null); + } + } + catch (Exception e) + { + Timber.e(e); + } + } + }).start(); + + return true; + case R.id.menu_item_bookmark_set: + if (currentSong == null) + { + return true; + } + + final String songId = currentSong.getId(); + final int playerPosition = mediaPlayerControllerLazy.getValue().getPlayerPosition(); + + currentSong.setBookmarkPosition(playerPosition); + + String bookmarkTime = Util.formatTotalDuration(playerPosition, true); + + new Thread(new Runnable() + { + @Override + public void run() + { + final MusicService musicService = MusicServiceFactory.getMusicService(getContext()); + + try + { + musicService.createBookmark(songId, playerPosition, getContext(), null); + } + catch (Exception e) + { + Timber.e(e); + } + } + }).start(); + + String msg = getResources().getString(R.string.download_bookmark_set_at_position, bookmarkTime); + + Util.toast(getContext(), msg); + + return true; + case R.id.menu_item_bookmark_delete: + if (currentSong == null) + { + return true; + } + + final String bookmarkSongId = currentSong.getId(); + currentSong.setBookmarkPosition(0); + + new Thread(new Runnable() + { + @Override + public void run() + { + final MusicService musicService = MusicServiceFactory.getMusicService(getContext()); + + try + { + musicService.deleteBookmark(bookmarkSongId, getContext(), null); + } + catch (Exception e) + { + Timber.e(e); + } + } + }).start(); + + Util.toast(getContext(), R.string.download_bookmark_removed); + + return true; + case R.id.menu_item_share: + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + List entries = new ArrayList<>(); + + if (mediaPlayerController != null) + { + List downloadServiceSongs = mediaPlayerController.getPlayList(); + + if (downloadServiceSongs != null) + { + for (DownloadFile downloadFile : downloadServiceSongs) + { + if (downloadFile != null) + { + MusicDirectory.Entry playlistEntry = downloadFile.getSong(); + + if (playlistEntry != null) + { + entries.add(playlistEntry); + } + } + } + } + } + + shareHandler.getValue().createShare(this, entries, null, cancellationToken); + return true; + default: + return false; + } + } + + private void update(CancellationToken cancel) + { + if (cancel.isCancellationRequested()) return; + + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + if (mediaPlayerController == null) + { + return; + } + + if (currentRevision != mediaPlayerController.getPlayListUpdateRevision()) + { + onDownloadListChanged(); + } + + if (currentPlaying != mediaPlayerController.getCurrentPlaying()) + { + onCurrentChanged(); + } + + onSliderProgressChanged(); + getActivity().invalidateOptionsMenu(); + } + + private void savePlaylistInBackground(final String playlistName) + { + Util.toast(getContext(), getResources().getString(R.string.download_playlist_saving, playlistName)); + mediaPlayerControllerLazy.getValue().setSuggestedPlaylistName(playlistName); + new SilentBackgroundTask(getActivity()) + { + @Override + protected Void doInBackground() throws Throwable + { + final List entries = new LinkedList<>(); + for (final DownloadFile downloadFile : mediaPlayerControllerLazy.getValue().getPlayList()) + { + entries.add(downloadFile.getSong()); + } + final MusicService musicService = MusicServiceFactory.getMusicService(getContext()); + musicService.createPlaylist(null, playlistName, entries, getContext(), null); + return null; + } + + @Override + protected void done(final Void result) + { + Util.toast(getContext(), R.string.download_playlist_done); + } + + @Override + protected void error(final Throwable error) + { + Timber.e(error, "Exception has occurred in savePlaylistInBackground"); + final String msg = String.format("%s %s", getResources().getString(R.string.download_playlist_error), getErrorMessage(error)); + Util.toast(getContext(), msg); + } + }.execute(); + } + + private void toggleFullScreenAlbumArt() + { + if (playlistFlipper.getDisplayedChild() == 1) + { + playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.push_down_in)); + playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.push_down_out)); + playlistFlipper.setDisplayedChild(0); + } + else + { + playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.push_up_in)); + playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.push_up_out)); + playlistFlipper.setDisplayedChild(1); + } + + scrollToCurrent(); + } + + private void start() + { + final MediaPlayerController service = mediaPlayerControllerLazy.getValue(); + final PlayerState state = service.getPlayerState(); + + if (state == PAUSED || state == COMPLETED || state == STOPPED) + { + service.start(); + } + else if (state == IDLE) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + + final int current = mediaPlayerControllerLazy.getValue().getCurrentPlayingNumberOnPlaylist(); + + if (current == -1) + { + service.play(0); + } + else + { + service.play(current); + } + } + } + + private void onDownloadListChanged() + { + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + if (mediaPlayerController == null) + { + return; + } + + final List list = mediaPlayerController.getPlayList(); + + emptyTextView.setText(R.string.download_empty); + final SongListAdapter adapter = new SongListAdapter(getContext(), list); + playlistView.setAdapter(adapter); + + playlistView.setDragSortListener(new DragSortListView.DragSortListener() + { + @Override + public void drop(int from, int to) + { + if (from != to) + { + DownloadFile item = adapter.getItem(from); + adapter.remove(item); + adapter.notifyDataSetChanged(); + adapter.insert(item, to); + adapter.notifyDataSetChanged(); + } + } + + @Override + public void drag(int from, int to) + { + + } + + @Override + public void remove(int which) + { + DownloadFile item = adapter.getItem(which); + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (item == null || mediaPlayerController == null) + { + return; + } + + DownloadFile currentPlaying = mediaPlayerController.getCurrentPlaying(); + + if (currentPlaying == item) + { + mediaPlayerControllerLazy.getValue().next(); + } + + adapter.remove(item); + adapter.notifyDataSetChanged(); + + String songRemoved = String.format(getResources().getString(R.string.download_song_removed), item.getSong().getTitle()); + + Util.toast(getContext(), songRemoved); + + onDownloadListChanged(); + onCurrentChanged(); + } + }); + + emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); + currentRevision = mediaPlayerController.getPlayListUpdateRevision(); + + switch (mediaPlayerController.getRepeatMode()) + { + case OFF: + repeatButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_repeat_off)); + break; + case ALL: + repeatButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_repeat_all)); + break; + case SINGLE: + repeatButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_repeat_single)); + break; + default: + break; + } + } + + private void onCurrentChanged() + { + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController == null) + { + return; + } + + currentPlaying = mediaPlayerController.getCurrentPlaying(); + + scrollToCurrent(); + + long totalDuration = mediaPlayerController.getPlayListDuration(); + long totalSongs = mediaPlayerController.getPlaylistSize(); + int currentSongIndex = mediaPlayerController.getCurrentPlayingNumberOnPlaylist() + 1; + + String duration = Util.formatTotalDuration(totalDuration); + + String trackFormat = String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs); + + if (currentPlaying != null) + { + currentSong = currentPlaying.getSong(); + songTitleTextView.setText(currentSong.getTitle()); + albumTextView.setText(currentSong.getAlbum()); + artistTextView.setText(currentSong.getArtist()); + downloadTrackTextView.setText(trackFormat); + downloadTotalDurationTextView.setText(duration); + imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true); + + displaySongRating(); + } + else + { + currentSong = null; + songTitleTextView.setText(null); + albumTextView.setText(null); + artistTextView.setText(null); + downloadTrackTextView.setText(null); + downloadTotalDurationTextView.setText(null); + imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true); + } + } + + private void onSliderProgressChanged() + { + MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController == null || onProgressChangedTask != null) + { + return; + } + + onProgressChangedTask = new SilentBackgroundTask(getActivity()) + { + MediaPlayerController mediaPlayerController; + boolean isJukeboxEnabled; + int millisPlayed; + Integer duration; + PlayerState playerState; + + @Override + protected Void doInBackground() throws Throwable + { + this.mediaPlayerController = mediaPlayerControllerLazy.getValue(); + isJukeboxEnabled = this.mediaPlayerController.isJukeboxEnabled(); + millisPlayed = Math.max(0, this.mediaPlayerController.getPlayerPosition()); + duration = this.mediaPlayerController.getPlayerDuration(); + playerState = mediaPlayerControllerLazy.getValue().getPlayerState(); + return null; + } + + @Override + protected void done(final Void result) + { + if (currentPlaying != null) + { + final int millisTotal = duration == null ? 0 : duration; + + positionTextView.setText(Util.formatTotalDuration(millisPlayed, true)); + durationTextView.setText(Util.formatTotalDuration(millisTotal, true)); + progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug. + progressBar.setProgress(millisPlayed); + progressBar.setEnabled(currentPlaying.isWorkDone() || isJukeboxEnabled); + } + else + { + positionTextView.setText(R.string.util_zero_time); + durationTextView.setText(R.string.util_no_time); + progressBar.setProgress(0); + progressBar.setMax(0); + progressBar.setEnabled(false); + } + + switch (playerState) + { + case DOWNLOADING: + final long bytes = currentPlaying != null ? currentPlaying.getPartialFile().length() : 0; + String downloadStatus = getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, getContext())); + Timber.d("Player set title"); + FragmentTitle.Companion.setTitle(PlayerFragment.this, downloadStatus); + break; + case PREPARING: + Timber.d("Player set title"); + FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.download_playerstate_buffering); + break; + case STARTED: + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController != null && mediaPlayerController.isShufflePlayEnabled()) + { + Timber.d("Player set title"); + FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.download_playerstate_playing_shuffle); + } + else + { + Timber.d("Player set title"); + FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.common_appname); + } + break; + default: + Timber.d("Player set title"); + FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.common_appname); + break; + case IDLE: + break; + case PREPARED: + break; + case STOPPED: + break; + case PAUSED: + break; + case COMPLETED: + break; + } + + switch (playerState) + { + case STARTED: + pauseButton.setVisibility(View.VISIBLE); + stopButton.setVisibility(View.GONE); + startButton.setVisibility(View.GONE); + break; + case DOWNLOADING: + case PREPARING: + pauseButton.setVisibility(View.GONE); + stopButton.setVisibility(View.VISIBLE); + startButton.setVisibility(View.GONE); + break; + default: + pauseButton.setVisibility(View.GONE); + stopButton.setVisibility(View.GONE); + startButton.setVisibility(View.VISIBLE); + break; + } + + // TODO: It would be a lot nicer if MediaPlayerController would send an event when this is necessary instead of updating every time + displaySongRating(); + + onProgressChangedTask = null; + } + }; + onProgressChangedTask.execute(); + } + + private void changeProgress(final int ms) + { + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + if (mediaPlayerController == null) + { + return; + } + + new SilentBackgroundTask(getActivity()) + { + int msPlayed; + Integer duration; + int seekTo; + + @Override + protected Void doInBackground() throws Throwable + { + msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition()); + duration = mediaPlayerController.getPlayerDuration(); + + final int msTotal = duration; + seekTo = msPlayed + ms > msTotal ? msTotal : msPlayed + ms; + mediaPlayerController.seekTo(seekTo); + return null; + } + + @Override + protected void done(final Void result) + { + progressBar.setProgress(seekTo); + } + }.execute(); + } + + @Override + public boolean onDown(final MotionEvent me) + { + return false; + } + + @Override + public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) + { + + final MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); + + if (mediaPlayerController == null || e1 == null || e2 == null) + { + return false; + } + + float e1X = e1.getX(); + float e2X = e2.getX(); + float e1Y = e1.getY(); + float e2Y = e2.getY(); + float absX = Math.abs(velocityX); + float absY = Math.abs(velocityY); + + // Right to Left swipe + if (e1X - e2X > swipeDistance && absX > swipeVelocity) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + if (mediaPlayerController.getCurrentPlayingNumberOnPlaylist() < mediaPlayerController.getPlaylistSize() - 1) + { + mediaPlayerController.next(); + onCurrentChanged(); + onSliderProgressChanged(); + } + return true; + } + + // Left to Right swipe + if (e2X - e1X > swipeDistance && absX > swipeVelocity) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + mediaPlayerController.previous(); + onCurrentChanged(); + onSliderProgressChanged(); + return true; + } + + // Top to Bottom swipe + if (e2Y - e1Y > swipeDistance && absY > swipeVelocity) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() + 30000); + onSliderProgressChanged(); + return true; + } + + // Bottom to Top swipe + if (e1Y - e2Y > swipeDistance && absY > swipeVelocity) + { + networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); + mediaPlayerController.seekTo(mediaPlayerController.getPlayerPosition() - 8000); + onSliderProgressChanged(); + return true; + } + + return false; + } + + @Override + public void onLongPress(final MotionEvent e) + { + } + + @Override + public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) + { + return false; + } + + @Override + public void onShowPress(final MotionEvent e) + { + } + + @Override + public boolean onSingleTapUp(final MotionEvent e) + { + return false; + } + + public static SeekBar getProgressBar() + { + return progressBar; + } + + private void displaySongRating() + { + int rating = currentSong == null || currentSong.getUserRating() == null ? 0 : currentSong.getUserRating(); + fiveStar1ImageView.setImageDrawable(rating > 0 ? fullStar : hollowStar); + fiveStar2ImageView.setImageDrawable(rating > 1 ? fullStar : hollowStar); + fiveStar3ImageView.setImageDrawable(rating > 2 ? fullStar : hollowStar); + fiveStar4ImageView.setImageDrawable(rating > 3 ? fullStar : hollowStar); + fiveStar5ImageView.setImageDrawable(rating > 4 ? fullStar : hollowStar); + } + + private void setSongRating(final int rating) + { + if (currentSong == null) + return; + + displaySongRating(); + mediaPlayerControllerLazy.getValue().setSongRating(rating); + } + + private void showSavePlaylistDialog() { + final AlertDialog.Builder builder; + + final LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(LAYOUT_INFLATER_SERVICE); + final View layout = layoutInflater.inflate(R.layout.save_playlist, (ViewGroup) getActivity().findViewById(R.id.save_playlist_root)); + + if (layout != null) + { + playlistNameView = layout.findViewById(R.id.save_playlist_name); + } + + builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.download_playlist_title); + builder.setMessage(R.string.download_playlist_name); + builder.setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() + { + @Override + public void onClick(final DialogInterface dialog, final int clickId) + { + savePlaylistInBackground(String.valueOf(playlistNameView.getText())); + } + }); + builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() + { + @Override + public void onClick(final DialogInterface dialog, final int clickId) + { + dialog.cancel(); + } + }); + builder.setView(layout); + builder.setCancelable(true); + + AlertDialog dialog = builder.create(); + + final String playlistName = mediaPlayerControllerLazy.getValue().getSuggestedPlaylistName(); + if (playlistName != null) + { + playlistNameView.setText(playlistName); + } + else + { + final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + playlistNameView.setText(dateFormat.format(new Date())); + } + + dialog.show(); + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java index 4eb37c6f..4df8e6be 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SearchFragment.java @@ -177,6 +177,22 @@ public class SearchFragment extends Fragment { registerForContextMenu(list); + // Fragment was started with a query, try to execute search right away + Bundle arguments = getArguments(); + if (arguments != null) { + String query = arguments.getString(Constants.INTENT_EXTRA_NAME_QUERY); + boolean autoPlay = arguments.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false); + + if (query != null) + { + mergeAdapter = new MergeAdapter(); + list.setAdapter(mergeAdapter); + search(query, autoPlay); + return; + } + } + + // Fragment was started from the Menu, create empty list populateList(); } @@ -221,6 +237,7 @@ public class SearchFragment extends Fragment { @Override public boolean onQueryTextChange(String newText) { return true; } }); + searchView.setIconifiedByDefault(false); searchItem.expandActionView(); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java index 2c6c4ce7..95c30b47 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java @@ -20,6 +20,7 @@ import androidx.fragment.app.Fragment; import androidx.navigation.Navigation; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import org.jetbrains.annotations.NotNull; import org.moire.ultrasonic.R; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.MusicDirectory; @@ -411,7 +412,7 @@ public class SelectAlbumFragment extends Fragment { } @Override - public void onPrepareOptionsMenu(Menu menu) + public void onPrepareOptionsMenu(@NotNull Menu menu) { super.onPrepareOptionsMenu(menu); playAllButton = menu.findItem(R.id.select_album_play_all); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java index 88843398..39b6bedd 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java @@ -86,8 +86,9 @@ public class SettingsFragment extends PreferenceFragmentCompat private SharedPreferences settings; - private Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); - private Lazy imageLoader = inject(ImageLoaderProvider.class); + private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); + private final Lazy imageLoader = inject(ImageLoaderProvider.class); + private final Lazy permissionUtil = inject(PermissionUtil.class); @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -517,7 +518,7 @@ public class SettingsFragment extends PreferenceFragmentCompat File dir = new File(path); if (!FileUtil.ensureDirectoryExistsAndIsReadWritable(dir)) { - PermissionUtil.handlePermissionFailed(getActivity(), new PermissionUtil.PermissionRequestFinishedCallback() { + permissionUtil.getValue().handlePermissionFailed(new PermissionUtil.PermissionRequestFinishedCallback() { @Override public void onPermissionRequestFinished(boolean hasPermission) { String currentPath = settings.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java index 74b96342..c2ba7fcc 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java @@ -14,8 +14,7 @@ import android.view.KeyEvent; import android.widget.RemoteViews; import org.moire.ultrasonic.R; -import org.moire.ultrasonic.activity.DownloadActivity; -import org.moire.ultrasonic.activity.MainActivity; +import org.moire.ultrasonic.activity.NavigationActivity; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver; import org.moire.ultrasonic.service.MediaPlayerController; @@ -181,15 +180,11 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider /** * Link up various button actions using {@link PendingIntent}. - * - * @param playerActive True if player is active in background, which means - * widget click will launch {@link DownloadActivity}, - * otherwise we launch {@link MainActivity}. */ private static void linkButtons(Context context, RemoteViews views, boolean playerActive) { - - Intent intent = new Intent(context, playerActive ? DownloadActivity.class : MainActivity.class); + // TODO: If playerActive = true, display the PlayerFragment automatically + Intent intent = new Intent(context, playerActive ? NavigationActivity.class : NavigationActivity.class); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.LAUNCHER"); PendingIntent pendingIntent = PendingIntent.getActivity(context, 10, intent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -198,21 +193,18 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider // Emulate media button clicks. intent = new Intent(Constants.CMD_PROCESS_KEYCODE); - //intent.setPackage(context.getPackageName()); intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class)); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); pendingIntent = PendingIntent.getBroadcast(context, 11, intent, 0); views.setOnClickPendingIntent(R.id.control_play, pendingIntent); intent = new Intent(Constants.CMD_PROCESS_KEYCODE); - //intent.setPackage(context.getPackageName()); intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class)); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)); pendingIntent = PendingIntent.getBroadcast(context, 12, intent, 0); views.setOnClickPendingIntent(R.id.control_next, pendingIntent); intent = new Intent(Constants.CMD_PROCESS_KEYCODE); - //intent.setPackage(context.getPackageName()); intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class)); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS)); pendingIntent = PendingIntent.getBroadcast(context, 13, intent, 0); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/LocalMediaPlayer.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/LocalMediaPlayer.java index a9fc4d0f..e12fed1a 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/LocalMediaPlayer.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/LocalMediaPlayer.java @@ -19,12 +19,12 @@ import timber.log.Timber; import android.widget.SeekBar; import org.jetbrains.annotations.NotNull; -import org.moire.ultrasonic.activity.DownloadActivity; import org.moire.ultrasonic.audiofx.EqualizerController; import org.moire.ultrasonic.audiofx.VisualizerController; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.PlayerState; +import org.moire.ultrasonic.fragment.PlayerFragment; import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver; import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.Constants; @@ -598,7 +598,7 @@ public class LocalMediaPlayer @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { - SeekBar progressBar = DownloadActivity.getProgressBar(); + SeekBar progressBar = PlayerFragment.getProgressBar(); MusicDirectory.Entry song = downloadFile.getSong(); if (percent == 100) @@ -627,12 +627,12 @@ public class LocalMediaPlayer setPlayerState(PREPARED); - SeekBar progressBar = DownloadActivity.getProgressBar(); + SeekBar progressBar = PlayerFragment.getProgressBar(); if (progressBar != null && downloadFile.isWorkDone()) { // Populate seek bar secondary progress if we have a complete file for consistency - DownloadActivity.getProgressBar().setSecondaryProgress(100 * progressBar.getMax()); + PlayerFragment.getProgressBar().setSecondaryProgress(100 * progressBar.getMax()); } synchronized (LocalMediaPlayer.this) 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 c6b60a2a..a902719c 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java @@ -20,7 +20,7 @@ import androidx.core.app.NotificationManagerCompat; import org.koin.java.KoinJavaComponent; import org.moire.ultrasonic.R; -import org.moire.ultrasonic.activity.DownloadActivity; +import org.moire.ultrasonic.activity.NavigationActivity; import org.moire.ultrasonic.activity.SubsonicTabActivity; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.PlayerState; @@ -650,7 +650,8 @@ public class MediaPlayerService extends Service notificationBuilder.setContent(contentView); - Intent notificationIntent = new Intent(this, DownloadActivity.class); + // TODO: Navigate automatically to PlayerFragment + Intent notificationIntent = new Intent(this, NavigationActivity.class); notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0)); if (playerState == PlayerState.PAUSED || playerState == PlayerState.IDLE) { diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java index fb9d5f38..eca5d830 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java @@ -60,7 +60,8 @@ public class FileUtil private static final List PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u"); private static final Pattern TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*"); - private static Lazy imageLoaderProvider = inject(ImageLoaderProvider.class); + private static final Lazy imageLoaderProvider = inject(ImageLoaderProvider.class); + private static final Lazy permissionUtil = inject(PermissionUtil.class); public static File getSongFile(Context context, MusicDirectory.Entry song) { @@ -396,7 +397,7 @@ public class FileUtil File dir = new File(path); boolean hasAccess = ensureDirectoryExistsAndIsReadWritable(dir); - if (hasAccess == false) PermissionUtil.handlePermissionFailed(context, null); + if (!hasAccess) permissionUtil.getValue().handlePermissionFailed(null); return hasAccess ? dir : defaultMusicDirectory; } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/PermissionUtil.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/PermissionUtil.java index 18bebcf5..0407b931 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/PermissionUtil.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/PermissionUtil.java @@ -21,7 +21,6 @@ import com.karumi.dexter.listener.PermissionRequestErrorListener; import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import org.moire.ultrasonic.R; -import org.moire.ultrasonic.activity.MainActivity; import java.util.List; @@ -32,6 +31,13 @@ import static androidx.core.content.PermissionChecker.PERMISSION_DENIED; * Contains static functions for Permission handling */ public class PermissionUtil { + + private final Context context; + + public PermissionUtil(Context context) { + this.context = context; + } + public interface PermissionRequestFinishedCallback { void onPermissionRequestFinished(boolean hasPermission); } @@ -42,31 +48,27 @@ public class PermissionUtil { * It will check if the failure is because the necessary permissions aren't available, * and it will request them, if necessary. * - * @param context context for the operation * @param callback callback function to execute after the permission request is finished */ - public static void handlePermissionFailed(final Context context, final PermissionRequestFinishedCallback callback) { - + public void handlePermissionFailed(final PermissionRequestFinishedCallback callback) { + // TODO: Test with ApplicationContext String currentCachePath = Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(context).getPath()); String defaultCachePath = FileUtil.getDefaultMusicDirectory(context).getPath(); // Ultrasonic can do nothing about this error when the Music Directory is already set to the default. if (currentCachePath.compareTo(defaultCachePath) == 0) return; - // We must get the context of the Main Activity for the dialogs, as this function may be called from a background thread where displaying dialogs is not available - final Context mainContext = MainActivity.getInstance(); - if ((PermissionChecker.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_DENIED) || (PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_DENIED)) { // While we request permission, the Music Directory is temporarily reset to its default location setCacheLocation(context, FileUtil.getDefaultMusicDirectory(context).getPath()); - requestFailedPermission(mainContext, currentCachePath, callback); + requestFailedPermission(context, currentCachePath, callback); } else { setCacheLocation(context, FileUtil.getDefaultMusicDirectory(context).getPath()); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { - showWarning(mainContext, context.getString(R.string.permissions_message_box_title), context.getString(R.string.permissions_access_error), null); + showWarning(context, context.getString(R.string.permissions_message_box_title), context.getString(R.string.permissions_access_error), null); } }); callback.onPermissionRequestFinished(false); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java index a7c6492c..837210e1 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java @@ -51,8 +51,7 @@ import android.widget.Toast; import androidx.annotation.ColorInt; import org.moire.ultrasonic.R; -import org.moire.ultrasonic.activity.DownloadActivity; -import org.moire.ultrasonic.activity.MainActivity; +import org.moire.ultrasonic.activity.NavigationActivity; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.*; import org.moire.ultrasonic.domain.MusicDirectory.Entry; @@ -1020,8 +1019,8 @@ public class Util public static void linkButtons(Context context, RemoteViews views, boolean playerActive) { - - Intent intent = new Intent(context, playerActive ? DownloadActivity.class : MainActivity.class); + // TODO: Navigate automatically to PlayerFragment if playerActive = true + Intent intent = new Intent(context, playerActive ? NavigationActivity.class : NavigationActivity.class); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.LAUNCHER"); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 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 12d4a745..d91a782e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -1,9 +1,13 @@ package org.moire.ultrasonic.activity import android.app.AlertDialog +import android.app.SearchManager +import android.content.Intent import android.content.res.Resources import android.os.Bundle import android.preference.PreferenceManager +import android.provider.MediaStore +import android.provider.SearchRecentSuggestions import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity @@ -21,6 +25,7 @@ import com.google.android.material.navigation.NavigationView import org.koin.android.ext.android.inject import org.koin.android.viewmodel.ext.android.viewModel import org.moire.ultrasonic.R +import org.moire.ultrasonic.provider.SearchSuggestionProvider import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport import org.moire.ultrasonic.subsonic.ImageLoaderProvider @@ -59,7 +64,7 @@ class NavigationActivity : AppCompatActivity() { setOf(R.id.mainFragment, R.id.selectArtistFragment, R.id.searchFragment, R.id.playlistsFragment, R.id.sharesFragment, R.id.bookmarksFragment, R.id.chatFragment, R.id.podcastFragment, R.id.settingsFragment, - R.id.aboutFragment), + R.id.aboutFragment, R.id.playerFragment), drawerLayout) setupActionBar(navController, appBarConfiguration) @@ -127,6 +132,25 @@ class NavigationActivity : AppCompatActivity() { return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration) } + // TODO: Test if this works with external Intents + // android.intent.action.SEARCH and android.media.action.MEDIA_PLAY_FROM_SEARCH calls here + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + + val query = intent?.getStringExtra(SearchManager.QUERY) + + if (query != null) { + val autoPlay = intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH + val suggestions = SearchRecentSuggestions(this, SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE) + suggestions.saveRecentQuery(query, null) + + val bundle = Bundle() + bundle.putString(Constants.INTENT_EXTRA_NAME_QUERY, query) + bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoPlay) + findNavController(R.id.nav_host_fragment).navigate(R.id.searchFragment, bundle) + } + } + private fun loadSettings() { PreferenceManager.setDefaultValues(this, R.xml.settings, false) val preferences = Util.getPreferences(this) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt index 1df73657..44f90750 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt @@ -6,6 +6,7 @@ import org.koin.core.context.startKoin import org.koin.core.logger.Level import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.di.appPermanentStorage +import org.moire.ultrasonic.di.applicationModule import org.moire.ultrasonic.di.baseNetworkModule import org.moire.ultrasonic.di.directoriesModule import org.moire.ultrasonic.di.featureFlagsModule @@ -35,6 +36,7 @@ class UApp : MultiDexApplication() { androidContext(this@UApp) // declare modules to use modules( + applicationModule, directoriesModule, appPermanentStorage, baseNetworkModule, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt index 2b7ce542..ef4deede 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt @@ -31,8 +31,4 @@ val appPermanentStorage = module { single { get().serverSettingDao() } viewModel { ServerSettingsModel(get(), get(), androidContext()) } - - single { ActiveServerProvider(get(), androidContext()) } - - single { ImageLoaderProvider(androidContext()) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt new file mode 100644 index 00000000..91342d99 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt @@ -0,0 +1,16 @@ +package org.moire.ultrasonic.di + +import org.koin.android.ext.koin.androidContext +import org.koin.dsl.bind +import org.koin.dsl.module +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.PermissionUtil + +val applicationModule = module { + single { ActiveServerProvider(get(), androidContext()) } + single { ImageLoaderProvider(androidContext()) } + single { PermissionUtil(androidContext()) } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt index 2df99798..43a9d508 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/DownloadHandler.kt @@ -33,9 +33,8 @@ class DownloadHandler( } if (autoPlay) { if (Util.getShouldTransitionOnPlaybackPreference(fragment.activity)) { - fragment.findNavController().popBackStack(R.id.downloadFragment, true) - fragment.findNavController().navigate(R.id.downloadFragment) - //startActivityForResultWithoutTransition(this@SubsonicTabActivity, DownloadActivity::class.java) + fragment.findNavController().popBackStack(R.id.playerFragment, true) + fragment.findNavController().navigate(R.id.playerFragment) } } else if (save) { Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size, songs.size)) @@ -190,9 +189,8 @@ class DownloadHandler( } else { mediaPlayerController.download(songs, save, autoPlay, playNext, shuffle, false) if (!append && Util.getShouldTransitionOnPlaybackPreference(activity)) { - fragment.findNavController().popBackStack(R.id.downloadFragment, true) - fragment.findNavController().navigate(R.id.downloadFragment) - //startActivityForResultWithoutTransition(activity, DownloadActivity::class.java) + fragment.findNavController().popBackStack(R.id.playerFragment, true) + fragment.findNavController().navigate(R.id.playerFragment) } } } else { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt index 09dfdd35..658755f9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt @@ -32,7 +32,7 @@ class ShareHandler(val context: Context) { private var saveAsDefaultsCheckBox: CheckBox? = null private val pattern = Pattern.compile(":") - fun createShare(fragment: Fragment, entries: List?, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) { + fun createShare(fragment: Fragment, entries: List?, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { val askForDetails = Util.getShouldAskForShareDetails(context) val shareDetails = ShareDetails() shareDetails.Entries = entries @@ -45,7 +45,7 @@ class ShareHandler(val context: Context) { } } - fun share(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) { + fun share(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { val task: BackgroundTask = object : TabActivityBackgroundTask(fragment.requireActivity(), true, swipe, cancellationToken) { @Throws(Throwable::class) override fun doInBackground(): Share { @@ -76,7 +76,7 @@ class ShareHandler(val context: Context) { task.execute() } - private fun showDialog(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) { + private fun showDialog(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { val layout = LayoutInflater.from(fragment.context).inflate(R.layout.share_details, null) if (layout != null) { diff --git a/ultrasonic/src/main/res/layout/lyrics.xml b/ultrasonic/src/main/res/layout/lyrics.xml index 83799344..4e5b22a2 100644 --- a/ultrasonic/src/main/res/layout/lyrics.xml +++ b/ultrasonic/src/main/res/layout/lyrics.xml @@ -4,53 +4,61 @@ a:layout_width="fill_parent" a:layout_height="fill_parent"> - + - - - - + + + a:paddingVertical="32dip"> + - + - + - + - + + diff --git a/ultrasonic/src/main/res/menu/navigation.xml b/ultrasonic/src/main/res/menu/navigation.xml index 17824f49..91770a5f 100644 --- a/ultrasonic/src/main/res/menu/navigation.xml +++ b/ultrasonic/src/main/res/menu/navigation.xml @@ -41,7 +41,7 @@ a:icon="?attr/chat" a:title="@string/button_bar.chat" /> diff --git a/ultrasonic/src/main/res/navigation/navigation_graph.xml b/ultrasonic/src/main/res/navigation/navigation_graph.xml index e928b056..2cf7360d 100644 --- a/ultrasonic/src/main/res/navigation/navigation_graph.xml +++ b/ultrasonic/src/main/res/navigation/navigation_graph.xml @@ -22,64 +22,64 @@ android:id="@+id/selectArtistToSelectAlbum" app:destination="@id/selectAlbumFragment" /> - + android:name="org.moire.ultrasonic.fragment.SelectAlbumFragment" > + android:name="org.moire.ultrasonic.fragment.SearchFragment" > + android:name="org.moire.ultrasonic.fragment.PlaylistsFragment" > + android:name="org.moire.ultrasonic.fragment.SharesFragment" > + android:name="org.moire.ultrasonic.fragment.BookmarksFragment" /> + android:name="org.moire.ultrasonic.fragment.ChatFragment" /> + android:name="org.moire.ultrasonic.fragment.PodcastFragment" > + android:name="org.moire.ultrasonic.fragment.SettingsFragment" /> + android:name="org.moire.ultrasonic.fragment.AboutFragment" /> + android:name="org.moire.ultrasonic.fragment.SelectGenreFragment" /> + + + + + \ No newline at end of file