aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/github/daneren2005
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2016-07-26 22:08:28 -0700
committerScott Jackson <daneren2005@gmail.com>2016-07-26 22:08:28 -0700
commit6377612ce4a3d9de8509b53338f3c72cf8885d37 (patch)
treee93f2caf9bdfe72e32e2b917192b63fca2e831ab /app/src/main/java/github/daneren2005
parentadd777d1d1a2406f90a079ae2d78766c414171be (diff)
parentaeb5cf0c40a177e6a4ecfbbf0f4e059618311242 (diff)
downloaddsub-6377612ce4a3d9de8509b53338f3c72cf8885d37.tar.gz
dsub-6377612ce4a3d9de8509b53338f3c72cf8885d37.tar.bz2
dsub-6377612ce4a3d9de8509b53338f3c72cf8885d37.zip
Merge branch 'playback_speed'
Diffstat (limited to 'app/src/main/java/github/daneren2005')
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java82
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java8
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java60
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java12
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteChooserDialogFragment.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteControllerDialogFragment.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteDialogFactory.java17
9 files changed, 207 insertions, 5 deletions
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 06559456..9f73f865 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -192,7 +192,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
protected void createCustomActionBarView() {
actionBarSpinner = (Spinner) getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
if((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity) && (Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true) || Util.getThemeRes(this) != R.style.Theme_DSub_Light_No_Color)) {
- actionBarSpinner.setBackgroundResource(R.drawable.abc_spinner_mtrl_am_alpha);
+ actionBarSpinner.setBackgroundDrawable(DrawableTint.getTintedDrawableFromColor(this, R.drawable.abc_spinner_mtrl_am_alpha, android.R.color.white));
}
spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 8a6df2ab..7c20dc28 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -51,6 +51,7 @@ import android.view.animation.AnimationUtils;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.PopupMenu;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.ViewFlipper;
@@ -59,7 +60,6 @@ import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.audiofx.EqualizerController;
import github.daneren2005.dsub.domain.Bookmark;
-import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.domain.RepeatMode;
import github.daneren2005.dsub.domain.ServerInfo;
@@ -73,6 +73,7 @@ import github.daneren2005.dsub.service.ServerTooOldException;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.adapter.DownloadFileAdapter;
+import github.daneren2005.dsub.view.compat.CustomMediaRouteDialogFactory;
import github.daneren2005.dsub.view.FadeOutAnimation;
import github.daneren2005.dsub.view.FastScroller;
import github.daneren2005.dsub.view.UpdateView;
@@ -116,6 +117,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private ImageButton bookmarkButton;
private ImageButton rateBadButton;
private ImageButton rateGoodButton;
+ private ImageButton playbackSpeedButton;
private ScheduledExecutorService executorService;
private DownloadFile currentPlaying;
@@ -182,6 +184,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
bookmarkButton = (ImageButton) rootView.findViewById(R.id.download_bookmark);
rateBadButton = (ImageButton) rootView.findViewById(R.id.download_rating_bad);
rateGoodButton = (ImageButton) rootView.findViewById(R.id.download_rating_good);
+ playbackSpeedButton = (ImageButton) rootView.findViewById(R.id.download_playback_speed);
toggleListButton =rootView.findViewById(R.id.download_toggle_list);
playlistView = (RecyclerView)rootView.findViewById(R.id.download_list);
@@ -216,6 +219,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
bookmarkButton.setOnTouchListener(touchListener);
rateBadButton.setOnTouchListener(touchListener);
rateGoodButton.setOnTouchListener(touchListener);
+ playbackSpeedButton.setOnTouchListener(touchListener);
emptyTextView.setOnTouchListener(touchListener);
albumArtImageView.setOnTouchListener(new View.OnTouchListener() {
@Override
@@ -386,6 +390,49 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
});
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ playbackSpeedButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PopupMenu popup = new PopupMenu(context, v);
+ popup.getMenuInflater().inflate(R.menu.playback_speed_options, popup.getMenu());
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return false;
+ }
+
+ float playbackSpeed = 1.0f;
+ switch (menuItem.getItemId()) {
+ case R.id.playback_speed_half:
+ playbackSpeed = 0.5f;
+ break;
+ case R.id.playback_speed_one_half:
+ playbackSpeed = 1.5f;
+ break;
+ case R.id.playback_speed_double:
+ playbackSpeed = 2.0f;
+ break;
+ case R.id.playback_speed_tripple:
+ playbackSpeed = 3.0f;
+ break;
+ }
+
+ downloadService.setPlaybackSpeed(playbackSpeed);
+ updateTitle();
+ return true;
+ }
+ });
+ popup.show();
+ }
+ });
+ } else {
+ playbackSpeedButton.setVisibility(View.GONE);
+ }
+
toggleListButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -465,7 +512,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
boolean equalizerAvailable = downloadService != null && downloadService.getEqualizerAvailable();
- if(equalizerAvailable && !downloadService.isRemoteEnabled()) {
+ boolean isRemoteEnabled = downloadService != null && downloadService.isRemoteEnabled();
+ if(equalizerAvailable && !isRemoteEnabled) {
SharedPreferences prefs = Util.getPreferences(context);
boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false);
if (equalizerOn && downloadService != null) {
@@ -477,10 +525,17 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
menu.removeItem(R.id.menu_equalizer);
}
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isRemoteEnabled) {
+ playbackSpeedButton.setVisibility(View.GONE);
+ } else {
+ playbackSpeedButton.setVisibility(View.VISIBLE);
+ }
+
if(downloadService != null) {
MenuItem mediaRouteItem = menu.findItem(R.id.menu_mediaroute);
if(mediaRouteItem != null) {
MediaRouteButton mediaRouteButton = (MediaRouteButton) MenuItemCompat.getActionView(mediaRouteItem);
+ mediaRouteButton.setDialogFactory(new CustomMediaRouteDialogFactory());
mediaRouteButton.setRouteSelector(downloadService.getRemoteSelector());
}
}
@@ -741,6 +796,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
downloadService.addOnSongChangedListener(NowPlayingFragment.this, true);
}
updateRepeatButton();
+ updateTitle();
}
});
}
@@ -1202,6 +1258,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
rewindButton.setVisibility(View.GONE);
fastforwardButton.setVisibility(View.GONE);
}
+ updateTitle();
}
private void setupSubtitle(int currentPlayingIndex) {
@@ -1412,6 +1469,27 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
break;
}
}
+ private void updateTitle() {
+ DownloadService downloadService = getDownloadService();
+ float playbackSpeed = downloadService.getPlaybackSpeed();
+
+ String title = context.getResources().getString(R.string.button_bar_now_playing);
+ int stringRes = -1;
+ if(playbackSpeed == 0.5f) {
+ stringRes = R.string.download_playback_speed_half;
+ } else if(playbackSpeed == 1.5f) {
+ stringRes = R.string.download_playback_speed_one_half;
+ } else if(playbackSpeed == 2.0f) {
+ stringRes = R.string.download_playback_speed_double;
+ } else if(playbackSpeed == 3.0f) {
+ stringRes = R.string.download_playback_speed_tripple;
+ }
+
+ if(stringRes != -1) {
+ title += " (" + context.getResources().getString(stringRes) + ")";
+ }
+ setTitle(title);
+ }
@Override
protected List<Entry> getSelectedEntries() {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 9cc27128..ab3db50f 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -1008,6 +1008,14 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
protected void addToPlaylist(final List<Entry> songs) {
+ Iterator<Entry> it = songs.iterator();
+ while(it.hasNext()) {
+ Entry entry = it.next();
+ if(entry.isDirectory()) {
+ it.remove();
+ }
+ }
+
if(songs.isEmpty()) {
Util.toast(context, "No songs selected");
return;
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
index 3d3a23b8..7168083c 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
@@ -76,6 +76,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.media.PlaybackParams;
import android.media.audiofx.AudioEffect;
import android.net.wifi.WifiManager;
import android.os.Build;
@@ -106,6 +107,7 @@ public class DownloadService extends Service {
public static final String START_PLAY = "github.daneren2005.dsub.START_PLAYING";
public static final int FAST_FORWARD = 30000;
public static final int REWIND = 10000;
+ private static final long DEFAULT_DELAY_UPDATE_PROGRESS = 1000L;
private static final double DELETE_CUTOFF = 0.84;
private static final int REQUIRED_ALBUM_MATCHES = 4;
private static final int REMOTE_PLAYLIST_TOTAL = 3;
@@ -162,6 +164,7 @@ public class DownloadService extends Service {
private int cachedPosition = 0;
private boolean downloadOngoing = false;
private float volume = 1.0f;
+ private long delayUpdateProgress = DEFAULT_DELAY_UPDATE_PROGRESS;
private AudioEffectsController effectsController;
private RemoteControlState remoteState = LOCAL;
@@ -853,6 +856,9 @@ public class DownloadService extends Service {
if(this.currentPlaying != null) {
this.currentPlaying.setPlaying(false);
}
+ if(delayUpdateProgress != DEFAULT_DELAY_UPDATE_PROGRESS && !isNextPlayingSameAlbum(currentPlaying, this.currentPlaying)) {
+ resetPlaybackSpeed();
+ }
this.currentPlaying = currentPlaying;
if(currentPlaying == null) {
currentPlayingIndex = -1;
@@ -1507,7 +1513,7 @@ public class DownloadService extends Service {
while(isRunning) {
try {
onSongProgress();
- Thread.sleep(1000L);
+ Thread.sleep(delayUpdateProgress);
}
catch(Exception e) {
isRunning = false;
@@ -1549,7 +1555,7 @@ public class DownloadService extends Service {
}
}
onSongProgress(cachedPosition < 2000 ? true: false);
- Thread.sleep(1000L);
+ Thread.sleep(delayUpdateProgress);
}
catch(Exception e) {
Log.w(TAG, "Crashed getting current position", e);
@@ -1881,6 +1887,7 @@ public class DownloadService extends Service {
cachedPosition = position;
applyReplayGain(mediaPlayer, downloadFile);
+ applyPlaybackParams(mediaPlayer);
if (start || autoPlayStart) {
mediaPlayer.start();
@@ -1951,6 +1958,7 @@ public class DownloadService extends Service {
}
applyReplayGain(nextMediaPlayer, downloadFile);
+ applyPlaybackParamsNext();
} catch (Exception x) {
handleErrorNext(x);
}
@@ -2604,6 +2612,54 @@ public class DownloadService extends Service {
}
}
+ public void setPlaybackSpeed(float playbackSpeed) {
+ Util.getPreferences(this).edit().putFloat(Constants.PREFERENCES_KEY_PLAYBACK_SPEED, playbackSpeed).commit();
+ applyPlaybackParamsMain();
+ if(nextMediaPlayer != null && nextPlayerState == PREPARED) {
+ applyPlaybackParamsNext();
+ }
+
+ delayUpdateProgress = Math.round(DEFAULT_DELAY_UPDATE_PROGRESS / playbackSpeed);
+ }
+ private void resetPlaybackSpeed() {
+ Util.getPreferences(this).edit().remove(Constants.PREFERENCES_KEY_PLAYBACK_SPEED).commit();
+ }
+
+ public float getPlaybackSpeed() {
+ return Util.getPreferences(this).getFloat(Constants.PREFERENCES_KEY_PLAYBACK_SPEED, 1.0f);
+ }
+
+ private synchronized void applyPlaybackParamsMain() {
+ applyPlaybackParams(mediaPlayer);
+ }
+ private synchronized void applyPlaybackParamsNext() {
+ if(isNextPlayingSameAlbum()) {
+ applyPlaybackParams(nextMediaPlayer);
+ }
+ }
+ private synchronized boolean isNextPlayingSameAlbum() {
+ return isNextPlayingSameAlbum(currentPlaying, nextPlaying);
+ }
+ private synchronized boolean isNextPlayingSameAlbum(DownloadFile currentPlaying, DownloadFile nextPlaying) {
+ if(currentPlaying == null || nextPlaying == null) {
+ return false;
+ } else {
+ return currentPlaying.getSong().getAlbum().equals(nextPlaying.getSong().getAlbum());
+ }
+ }
+
+ private synchronized void applyPlaybackParams(MediaPlayer mediaPlayer) {
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ float playbackSpeed = getPlaybackSpeed();
+
+ if(playbackSpeed != 1.0f || mediaPlayer.getPlaybackParams() != null) {
+ PlaybackParams playbackParams = new PlaybackParams();
+ playbackParams.setSpeed(playbackSpeed);
+ mediaPlayer.setPlaybackParams(playbackParams);
+ }
+ }
+ }
+
public void toggleStarred() {
final DownloadFile currentPlaying = this.currentPlaying;
if(currentPlaying == null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index 216db54b..c8c580ff 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
@@ -174,6 +174,7 @@ public final class Constants {
public static final String PREFERENCES_KEY_CAST_STREAM_ORIGINAL = "castStreamOriginal";
public static final String PREFERENCES_KEY_HEADS_UP_NOTIFICATION = "headsUpNotification";
public static final String PREFERENCES_KEY_CAST_CACHE = "castCache";
+ public static final String PREFERENCES_KEY_PLAYBACK_SPEED = "playbackSpeed";
public static final String OFFLINE_SCROBBLE_COUNT = "scrobbleCount";
public static final String OFFLINE_SCROBBLE_ID = "scrobbleID";
diff --git a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
index 2da72579..cc8e241d 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
@@ -21,6 +21,7 @@ import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.AttrRes;
+import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.TypedValue;
@@ -48,6 +49,17 @@ public class DrawableTint {
tintedDrawables.put(drawableRes, background);
return background;
}
+ public static Drawable getTintedDrawableFromColor(Context context, @DrawableRes int drawableRes, @ColorRes int colorRes) {
+ if(tintedDrawables.containsKey(drawableRes)) {
+ return tintedDrawables.get(drawableRes);
+ }
+
+ int color = context.getResources().getColor(colorRes);
+ Drawable background = context.getResources().getDrawable(drawableRes);
+ background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ tintedDrawables.put(drawableRes, background);
+ return background;
+ }
public static int getColorRes(Context context, @AttrRes int colorAttr) {
int color;
if(attrMap.containsKey(colorAttr)) {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteChooserDialogFragment.java b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteChooserDialogFragment.java
new file mode 100644
index 00000000..da9b135f
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteChooserDialogFragment.java
@@ -0,0 +1,15 @@
+package github.daneren2005.dsub.view.compat;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.MediaRouteChooserDialog;
+import android.support.v7.app.MediaRouteChooserDialogFragment;
+
+import github.daneren2005.dsub.util.Util;
+
+public class CustomMediaRouteChooserDialogFragment extends MediaRouteChooserDialogFragment {
+ @Override
+ public MediaRouteChooserDialog onCreateChooserDialog(Context context, Bundle savedInstanceState) {
+ return new MediaRouteChooserDialog(context, Util.getThemeRes(context));
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteControllerDialogFragment.java b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteControllerDialogFragment.java
new file mode 100644
index 00000000..7fd54142
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteControllerDialogFragment.java
@@ -0,0 +1,15 @@
+package github.daneren2005.dsub.view.compat;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.MediaRouteControllerDialog;
+import android.support.v7.app.MediaRouteControllerDialogFragment;
+
+import github.daneren2005.dsub.util.Util;
+
+public class CustomMediaRouteControllerDialogFragment extends MediaRouteControllerDialogFragment {
+ @Override
+ public MediaRouteControllerDialog onCreateControllerDialog(Context context, Bundle savedInstanceState) {
+ return new MediaRouteControllerDialog(context, Util.getThemeRes(context));
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteDialogFactory.java b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteDialogFactory.java
new file mode 100644
index 00000000..8bc890cb
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/compat/CustomMediaRouteDialogFactory.java
@@ -0,0 +1,17 @@
+package github.daneren2005.dsub.view.compat;
+
+import android.support.v7.app.MediaRouteChooserDialogFragment;
+import android.support.v7.app.MediaRouteControllerDialogFragment;
+import android.support.v7.app.MediaRouteDialogFactory;
+
+public class CustomMediaRouteDialogFactory extends MediaRouteDialogFactory {
+ @Override
+ public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
+ return new CustomMediaRouteChooserDialogFragment();
+ }
+
+ @Override
+ public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
+ return new CustomMediaRouteControllerDialogFragment();
+ }
+}