summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLinus Lee <llee@cyngn.com>2015-07-27 17:07:05 -0700
committerLinus Lee <llee@cyngn.com>2015-08-11 18:25:59 -0700
commit9f3fa0b5a03e69b1896f86ceff88357fb7cdab48 (patch)
tree2c7d0ec4c9e79fdb54189b069157bd4f9716d16c /src
parentdd33b6ea6558d6934766337e3e6e7f709b63d403 (diff)
downloadandroid_packages_apps_Eleven-9f3fa0b5a03e69b1896f86ceff88357fb7cdab48.tar.gz
android_packages_apps_Eleven-9f3fa0b5a03e69b1896f86ceff88357fb7cdab48.tar.bz2
android_packages_apps_Eleven-9f3fa0b5a03e69b1896f86ceff88357fb7cdab48.zip
Add ability to seek through track with circular motion
With this patch, if you do a circular gesture motion on the large play button, you can seek the track instead of using the long press of the next/previous buttons Also change the progress bar to 1000 to allow for smaller updates Change-Id: I38512b2439227686bc6363e77b1858dbb5764a9f
Diffstat (limited to 'src')
-rw-r--r--src/com/cyanogenmod/eleven/ui/activities/HomeActivity.java7
-rw-r--r--src/com/cyanogenmod/eleven/ui/fragments/AudioPlayerFragment.java34
-rw-r--r--src/com/cyanogenmod/eleven/utils/MusicUtils.java3
-rw-r--r--src/com/cyanogenmod/eleven/widgets/PlayPauseProgressButton.java200
4 files changed, 211 insertions, 33 deletions
diff --git a/src/com/cyanogenmod/eleven/ui/activities/HomeActivity.java b/src/com/cyanogenmod/eleven/ui/activities/HomeActivity.java
index d946039..a66c6c0 100644
--- a/src/com/cyanogenmod/eleven/ui/activities/HomeActivity.java
+++ b/src/com/cyanogenmod/eleven/ui/activities/HomeActivity.java
@@ -38,6 +38,7 @@ import com.cyanogenmod.eleven.R;
import com.cyanogenmod.eleven.cache.ImageFetcher;
import com.cyanogenmod.eleven.ui.fragments.AlbumDetailFragment;
import com.cyanogenmod.eleven.ui.fragments.ArtistDetailFragment;
+import com.cyanogenmod.eleven.ui.fragments.AudioPlayerFragment;
import com.cyanogenmod.eleven.ui.fragments.IChildFragment;
import com.cyanogenmod.eleven.ui.fragments.ISetupActionBar;
import com.cyanogenmod.eleven.ui.fragments.PlaylistDetailFragment;
@@ -215,7 +216,11 @@ public class HomeActivity extends SlidingPanelActivity implements
color = getResources().getColor(R.color.visualizer_fill_color);
}
- getAudioPlayerFragment().setVisualizerColor(color);
+ // check for null since updatestatusBarColor is a async task
+ AudioPlayerFragment fragment = getAudioPlayerFragment();
+ if (fragment != null) {
+ fragment.setVisualizerColor(color);
+ }
}
private void updateStatusBarColor(int color) {
diff --git a/src/com/cyanogenmod/eleven/ui/fragments/AudioPlayerFragment.java b/src/com/cyanogenmod/eleven/ui/fragments/AudioPlayerFragment.java
index efeaa62..e54eabd 100644
--- a/src/com/cyanogenmod/eleven/ui/fragments/AudioPlayerFragment.java
+++ b/src/com/cyanogenmod/eleven/ui/fragments/AudioPlayerFragment.java
@@ -156,8 +156,6 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection {
private boolean mIsPaused = false;
- private boolean mFromTouch = false;
-
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -352,6 +350,7 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection {
*/
private void initPlaybackControls() {
mPlayPauseProgressButton = (PlayPauseProgressButton)mRootView.findViewById(R.id.playPauseProgressButton);
+ mPlayPauseProgressButton.setDragEnabled(true);
mShuffleButton = (ShuffleButton)mRootView.findViewById(R.id.action_button_shuffle);
mRepeatButton = (RepeatButton)mRootView.findViewById(R.id.action_button_repeat);
mPreviousButton = (RepeatingImageButton)mRootView.findViewById(R.id.action_button_previous);
@@ -538,49 +537,48 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection {
}
private void refreshCurrentTimeText(final long pos) {
- mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(), pos / 1000));
+ if (mPlayPauseProgressButton.isDragging()) {
+ mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(),
+ mPlayPauseProgressButton.getDragProgressInMs() / 1000));
+ } else {
+ mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(), pos / 1000));
+ }
}
/* Used to update the current time string */
private long refreshCurrentTime() {
if (mService == null) {
- return 500;
+ return MusicUtils.UPDATE_FREQUENCY_MS;
}
try {
final long pos = MusicUtils.position();
if (pos >= 0 && MusicUtils.duration() > 0) {
refreshCurrentTimeText(pos);
- if (mFromTouch) {
- return 500;
+ if (mPlayPauseProgressButton.isDragging()) {
+ mCurrentTime.setVisibility(View.VISIBLE);
+ return MusicUtils.UPDATE_FREQUENCY_FAST_MS;
} else if (MusicUtils.isPlaying()) {
mCurrentTime.setVisibility(View.VISIBLE);
+
+ // calculate the number of milliseconds until the next full second,
+ // so the counter can be updated at just the right time
+ return Math.max(20, 1000 - pos % 1000);
} else {
// blink the counter
final int vis = mCurrentTime.getVisibility();
mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE
: View.INVISIBLE);
- return 500;
}
} else {
mCurrentTime.setText("--:--");
}
-
- // calculate the number of milliseconds until the next full second,
- // so
- // the counter can be updated at just the right time
- final long remaining = 1000 - pos % 1000;
- if (remaining < 20) {
- return 20;
- }
-
- return remaining;
} catch (final Exception ignored) {
if (ignored.getMessage() != null) {
Log.e(TAG, ignored.getMessage());
}
}
- return 500;
+ return MusicUtils.UPDATE_FREQUENCY_MS;
}
/**
diff --git a/src/com/cyanogenmod/eleven/utils/MusicUtils.java b/src/com/cyanogenmod/eleven/utils/MusicUtils.java
index 6279a78..38e9ac0 100644
--- a/src/com/cyanogenmod/eleven/utils/MusicUtils.java
+++ b/src/com/cyanogenmod/eleven/utils/MusicUtils.java
@@ -87,6 +87,9 @@ public final class MusicUtils {
public static final String MUSIC_ONLY_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1"
+ " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"; //$NON-NLS-2$
+ public static final long UPDATE_FREQUENCY_MS = 500;
+ public static final long UPDATE_FREQUENCY_FAST_MS = 30;
+
static {
mConnectionMap = new WeakHashMap<Context, ServiceBinder>();
sEmptyList = new long[0];
diff --git a/src/com/cyanogenmod/eleven/widgets/PlayPauseProgressButton.java b/src/com/cyanogenmod/eleven/widgets/PlayPauseProgressButton.java
index 5c086be..8b8788f 100644
--- a/src/com/cyanogenmod/eleven/widgets/PlayPauseProgressButton.java
+++ b/src/com/cyanogenmod/eleven/widgets/PlayPauseProgressButton.java
@@ -1,5 +1,6 @@
/*
-* Copyright (C) 2014 The CyanogenMod Project
+* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+* Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +18,9 @@ package com.cyanogenmod.eleven.widgets;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
@@ -30,12 +34,28 @@ import com.cyanogenmod.eleven.utils.MusicUtils;
* updates while the activity/fragment is not visible
*/
public class PlayPauseProgressButton extends FrameLayout {
- private static final long UPDATE_FREQUENCY_MS = 500;
+ private static String TAG = PlayPauseProgressButton.class.getSimpleName();
+ private static boolean DEBUG = false;
+ private static final int REVOLUTION_IN_DEGREES = 360;
+ private static final int HALF_REVOLUTION_IN_DEGREES = REVOLUTION_IN_DEGREES / 2;
+
private ProgressBar mProgressBar;
private PlayPauseButton mPlayPauseButton;
private Runnable mUpdateProgress;
private boolean mPaused;
+ private final int mSmallDistance;
+ private float mDragPercentage = 0.0f;
+ private boolean mDragEnabled = false;
+ private boolean mDragging = false;
+ private float mDownAngle;
+ private float mDragAngle;
+ private float mDownX;
+ private float mDownY;
+ private int mWidth;
+ private long mCurrentSongDuration;
+ private long mCurrentSongProgress;
+
public PlayPauseProgressButton(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -44,6 +64,8 @@ public class PlayPauseProgressButton extends FrameLayout {
// set paused to false since we shouldn't be typically created while not visible
mPaused = false;
+
+ mSmallDistance = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
@@ -94,6 +116,27 @@ public class PlayPauseProgressButton extends FrameLayout {
setVisibility(GONE);
}
+ /**
+ * Sets whether the user can drag the progress in a circular motion to seek the track
+ */
+ public void setDragEnabled(boolean enabled) {
+ mDragEnabled = enabled;
+ }
+
+ /**
+ * @return true if the user is actively dragging to seek
+ */
+ public boolean isDragging() {
+ return mDragEnabled && mDragging;
+ }
+
+ /**
+ * @return how far the user has dragged in the track in ms
+ */
+ public long getDragProgressInMs() {
+ return (long)(mDragPercentage * mCurrentSongDuration);
+ }
+
@Override
public void setEnabled(boolean enabled) {
// if the enabled state isn't changed, quit
@@ -158,18 +201,17 @@ public class PlayPauseProgressButton extends FrameLayout {
* Updates the state of the progress bar and the play pause button
*/
private void updateState() {
- final long duration = MusicUtils.duration();
-
- if (duration > 0) {
- final long pos = MusicUtils.position();
-
- int progress = (int) (mProgressBar.getMax() * pos / duration);
- mProgressBar.setProgress(progress);
- } else {
- // this is when there are no tracks loaded or some kind of error condition
- mProgressBar.setProgress(0);
+ mCurrentSongDuration = MusicUtils.duration();
+ mCurrentSongProgress = MusicUtils.position();
+
+ int progress = 0;
+ if (isDragging()) {
+ progress = (int) (mDragPercentage * mProgressBar.getMax());
+ } else if (mCurrentSongDuration > 0) {
+ progress = (int) (mProgressBar.getMax() * mCurrentSongProgress / mCurrentSongDuration);
}
+ mProgressBar.setProgress(progress);
mPlayPauseButton.updateState();
}
@@ -182,7 +224,8 @@ public class PlayPauseProgressButton extends FrameLayout {
@Override
public void run() {
updateState();
- postDelayed(mUpdateProgress, UPDATE_FREQUENCY_MS);
+ postDelayed(mUpdateProgress, isDragging() ? MusicUtils.UPDATE_FREQUENCY_FAST_MS
+ : MusicUtils.UPDATE_FREQUENCY_MS);
}
};
}
@@ -191,7 +234,7 @@ public class PlayPauseProgressButton extends FrameLayout {
removeCallbacks(mUpdateProgress);
// post ourselves as a delayed
- postDelayed(mUpdateProgress, UPDATE_FREQUENCY_MS);
+ post(mUpdateProgress);
}
/**
@@ -202,4 +245,133 @@ public class PlayPauseProgressButton extends FrameLayout {
removeCallbacks(mUpdateProgress);
}
}
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldW, int oldH) {
+ mWidth = Math.min(w, h);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!mDragEnabled) {
+ return false;
+ }
+
+ return onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final float x = event.getX();
+ final float y = event.getY();
+
+ if (!mDragEnabled || mCurrentSongDuration <= 0) {
+ return false;
+ }
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownX = event.getX();
+ mDownY = event.getY();
+ mDownAngle = angle(mDownX, mDownY);
+ mDragAngle = REVOLUTION_IN_DEGREES
+ * (mCurrentSongProgress / (float) mCurrentSongDuration);
+ mDragPercentage = mDragAngle / REVOLUTION_IN_DEGREES;
+ mDragging = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // if the user has moved a certain distance
+ if (Math.sqrt(Math.pow(event.getX() - mDownX, 2)
+ + Math.pow(event.getY() - mDownY, 2)) < mSmallDistance) {
+ return false;
+ }
+
+ // if we weren't previously dragging, immediately kick off an update to reflect
+ // the change faster
+ if (!mDragging) {
+ postUpdate();
+ }
+
+ mDragging = true;
+ getParent().requestDisallowInterceptTouchEvent(true);
+
+ // calculate the amount of angle we've moved
+ final float deltaAngle = getDelta(x, y);
+ mDragAngle = cropAngle(mDragAngle + deltaAngle);
+ mDragPercentage = mDragAngle / REVOLUTION_IN_DEGREES;
+
+ if (DEBUG) {
+ Log.d(TAG, "Delta Angle: " + deltaAngle + ", Target Angle: " + mDownAngle);
+ }
+
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ // if we were dragging, seek to where we dragged to
+ if (mDragging) {
+ MusicUtils.seek((long)(mDragPercentage * mCurrentSongDuration));
+ }
+ mDragging = false;
+ default:
+ break;
+ }
+ return mDragging;
+ }
+
+ /**
+ * Crops the angle between 0 and 360 - if the angle is < 0, it will return 0, if it is more than
+ * 360 it will return 360
+ */
+ private static float cropAngle(float angle) {
+ return Math.min(REVOLUTION_IN_DEGREES, Math.max(0.0f, angle));
+ }
+
+ /**
+ * Wraps the angle between -180 and 180. This assumes that the passed in
+ * angle is >= -360 and <= 360
+ */
+ private static float wrapHalfRevolution(float angle) {
+ if (angle < -HALF_REVOLUTION_IN_DEGREES) {
+ return angle + REVOLUTION_IN_DEGREES;
+ } else if (angle > HALF_REVOLUTION_IN_DEGREES) {
+ return angle - REVOLUTION_IN_DEGREES;
+ }
+
+ return angle;
+ }
+
+ /**
+ * Gets the change in angle from the down angle and updates the down angle to the current angle
+ */
+ private float getDelta(float x, float y) {
+ float angle = angle(x, y);
+ float deltaAngle = wrapHalfRevolution(angle - mDownAngle);
+ mDownAngle = angle;
+ return deltaAngle;
+ }
+
+ /**
+ * Calculates the angle at the point passed in based on the center of the button
+ */
+ private float angle(float x, float y) {
+ float center = mWidth / 2.0f;
+ x -= center;
+ y -= center;
+
+ if (x == 0.0f) {
+ if (y > 0.0f) {
+ return 180.0f;
+ } else {
+ return 0.0f;
+ }
+ }
+
+ float angle = (float) (Math.atan(y / x) / Math.PI * 180.0);
+ if (x > 0.0f) {
+ angle += 90;
+ } else {
+ angle += 270;
+ }
+ return angle;
+ }
}