Last active
March 17, 2021 21:12
-
-
Save BadDaemon/9060da3a83f6072ae799b6dbb0aed5b5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/org/lineageos/eleven/widgets/PlayPauseProgressButton.java b/src/org/lineageos/eleven/widgets/PlayPauseProgressButton.java | |
index 3821eef..9f4eb26 100644 | |
--- a/src/org/lineageos/eleven/widgets/PlayPauseProgressButton.java | |
+++ b/src/org/lineageos/eleven/widgets/PlayPauseProgressButton.java | |
@@ -1,26 +1,26 @@ | |
/* | |
- * 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. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- | |
- * Note: This file was re-added only to provide a progress bar to the Bottom Action Bar, the main music player ui is unaffected by this file. | |
- | |
- */ | |
+* 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. | |
+* You may obtain a copy of the License at | |
+* | |
+* http://www.apache.org/licenses/LICENSE-2.0 | |
+* | |
+* Unless required by applicable law or agreed to in writing, software | |
+* distributed under the License is distributed on an "AS IS" BASIS, | |
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+* See the License for the specific language governing permissions and | |
+* limitations under the License. | |
+*/ | |
package org.lineageos.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; | |
@@ -28,17 +28,34 @@ import org.lineageos.eleven.R; | |
import org.lineageos.eleven.utils.MusicUtils; | |
/** | |
- * This class handles the play-pause button as well as the circular progress bar | |
+ * This class handles the playpause button as well as the circular progress bar | |
* it self-updates the progress bar but the containing activity/fragment | |
* needs to add code to pause/resume this button to prevent unnecessary | |
* updates while the activity/fragment is not visible | |
*/ | |
public class PlayPauseProgressButton extends FrameLayout { | |
+ 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); | |
@@ -47,14 +64,16 @@ 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 | |
protected void onFinishInflate() { | |
super.onFinishInflate(); | |
- mPlayPauseButton = (PlayPauseButton) findViewById(R.id.action_button_play); | |
- mProgressBar = (ProgressBar) findViewById(R.id.circularProgressBarAlt); | |
+ mPlayPauseButton = (PlayPauseButton)findViewById(R.id.action_button_play); | |
+ mProgressBar = (ProgressBar)findViewById(R.id.circularProgressBar); | |
} | |
@Override | |
@@ -70,8 +89,8 @@ public class PlayPauseProgressButton extends FrameLayout { | |
// rotate the progress bar 90 degrees counter clockwise so that the | |
// starting position is at the top | |
- mProgressBar.setPivotX(mProgressBar.getMeasuredWidth() / 2f); | |
- mProgressBar.setPivotY(mProgressBar.getMeasuredHeight() / 2f); | |
+ mProgressBar.setPivotX(mProgressBar.getMeasuredWidth() / 2); | |
+ mProgressBar.setPivotY(mProgressBar.getMeasuredHeight() / 2); | |
mProgressBar.setRotation(-90); | |
} | |
@@ -86,14 +105,45 @@ public class PlayPauseProgressButton extends FrameLayout { | |
setVisibility(VISIBLE); | |
} | |
+ /** | |
+ * Disables and sets the visibility to gone for the container | |
+ */ | |
+ public void disableAndHide() { | |
+ // disable | |
+ setEnabled(false); | |
+ | |
+ // hide our view | |
+ 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 | |
- if (enabled == isEnabled()) { | |
- return; | |
- } | |
+ if (enabled == isEnabled()) return; | |
super.setEnabled(enabled); | |
+ | |
// signal our state has changed | |
onStateChanged(); | |
} | |
@@ -102,19 +152,36 @@ public class PlayPauseProgressButton extends FrameLayout { | |
* Pauses the progress bar periodic update logic | |
*/ | |
public void pause() { | |
+ if (!mPaused) { | |
+ mPaused = true; | |
+ | |
+ // signal our state has changed | |
+ onStateChanged(); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Resumes the progress bar periodic update logic | |
+ */ | |
+ public void resume() { | |
if (mPaused) { | |
- return; | |
+ mPaused = false; | |
+ | |
+ // signal our state has changed | |
+ onStateChanged(); | |
} | |
+ } | |
- mPaused = true; | |
- // signal our state has changed | |
- onStateChanged(); | |
+ /** | |
+ * @return play pause button | |
+ */ | |
+ public PlayPauseButton getPlayPauseButton() { | |
+ return mPlayPauseButton; | |
} | |
/** | |
* Signaled if the state has changed (either the enabled or paused flag) | |
- * When the state changes, we either kick off the updates or remove them | |
- * based on those flags | |
+ * When the state changes, we either kick off the updates or remove them based on those flags | |
*/ | |
private void onStateChanged() { | |
// if we are enabled and not paused | |
@@ -133,13 +200,15 @@ public class PlayPauseProgressButton extends FrameLayout { | |
/** | |
* Updates the state of the progress bar and the play pause button | |
*/ | |
- public void updateState() { | |
- long currentSongDuration = MusicUtils.duration(); | |
- long currentSongProgress = MusicUtils.position(); | |
+ private void updateState() { | |
+ mCurrentSongDuration = MusicUtils.duration(); | |
+ mCurrentSongProgress = MusicUtils.position(); | |
int progress = 0; | |
- if (currentSongDuration > 0) { | |
- progress = (int) (mProgressBar.getMax() * currentSongProgress / currentSongDuration); | |
+ if (isDragging()) { | |
+ progress = (int) (mDragPercentage * mProgressBar.getMax()); | |
+ } else if (mCurrentSongDuration > 0) { | |
+ progress = (int) (mProgressBar.getMax() * mCurrentSongProgress / mCurrentSongDuration); | |
} | |
mProgressBar.setProgress(progress); | |
@@ -151,9 +220,13 @@ public class PlayPauseProgressButton extends FrameLayout { | |
*/ | |
private void postUpdate() { | |
if (mUpdateProgress == null) { | |
- mUpdateProgress = () -> { | |
- updateState(); | |
- postDelayed(mUpdateProgress, MusicUtils.UPDATE_FREQUENCY_MS); | |
+ mUpdateProgress = new Runnable() { | |
+ @Override | |
+ public void run() { | |
+ updateState(); | |
+ postDelayed(mUpdateProgress, isDragging() ? MusicUtils.UPDATE_FREQUENCY_FAST_MS | |
+ : MusicUtils.UPDATE_FREQUENCY_MS); | |
+ } | |
}; | |
} | |
@@ -172,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; | |
+ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment