Fix animations, aligment

This commit is contained in:
h4h13 2020-01-06 23:37:02 +05:30
parent caed1ee47c
commit a5e349380c
14 changed files with 858 additions and 26 deletions

View file

@ -121,7 +121,8 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let {
artistDetailsPresenter.loadArtist(it)
artistCoverContainer?.transitionName = "${getString(R.string.transition_artist_image)}_$it"
val name = "${getString(R.string.transition_artist_image)}_$it"
artistCoverContainer?.transitionName = name
}
} else {
finish()

View file

@ -8,11 +8,9 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.Menu
import android.view.View
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.fragments.mainactivity.LibraryFragment
@ -251,7 +249,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
}
}
companion object {
const val APP_INTRO_REQUEST = 2323
const val HOME = 0

View file

@ -19,6 +19,7 @@ import code.name.monkey.retromusic.fragments.NowPlayingScreen.ADAPTIVE
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR_CARD
import code.name.monkey.retromusic.fragments.NowPlayingScreen.CARD
import code.name.monkey.retromusic.fragments.NowPlayingScreen.CIRCLE
import code.name.monkey.retromusic.fragments.NowPlayingScreen.COLOR
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FIT
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FLAT
@ -34,6 +35,7 @@ import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
import code.name.monkey.retromusic.fragments.player.card.CardFragment
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
import code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
@ -249,6 +251,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay
COLOR -> ColorFragment()
TINY -> TinyPlayerFragment()
PEAK -> PeakPlayerFragment()
CIRCLE -> CirclePlayerFragment()
else -> PlayerFragment()
} // must implement AbsPlayerFragment
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)

View file

@ -18,6 +18,7 @@ enum class NowPlayingScreen constructor(
PEAK(R.string.peak, R.drawable.np_peak, 14),
ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10),
CIRCLE(R.string.circle, R.drawable.np_adaptive, 15),
BLUR(R.string.blur, R.drawable.np_blur, 4),
BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9),
CARD(R.string.card, R.drawable.np_card, 6),

View file

@ -18,21 +18,31 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
import code.name.monkey.retromusic.dialogs.*
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
import code.name.monkey.retromusic.dialogs.SleepTimerDialog
import code.name.monkey.retromusic.dialogs.SongDetailDialog
import code.name.monkey.retromusic.dialogs.SongShareDialog
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.PaletteColorHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.util.*
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.RingtoneManager
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.statusBarShadow
import java.io.FileNotFoundException
abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
Toolbar.OnMenuItemClickListener,
PaletteColorHolder,
PlayerAlbumCoverFragment.Callbacks {
Toolbar.OnMenuItemClickListener,
PaletteColorHolder,
PlayerAlbumCoverFragment.Callbacks {
var callbacks: Callbacks? = null
private set
@ -41,7 +51,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
override fun onAttach(
context: Context
context: Context
) {
super.onAttach(context)
try {
@ -57,7 +67,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
}
override fun onMenuItemClick(
item: MenuItem
item: MenuItem
): Boolean {
val song = MusicPlayerRemote.currentSong
when (item.itemId) {
@ -83,7 +93,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
}
R.id.action_save_playing_queue -> {
CreatePlaylistDialog.create(MusicPlayerRemote.playingQueue)
.show(requireFragmentManager(), "ADD_TO_PLAYLIST")
.show(requireFragmentManager(), "ADD_TO_PLAYLIST")
return true
}
R.id.action_tag_editor -> {
@ -130,7 +140,8 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
}
R.id.action_go_to_genre -> {
val retriever = MediaMetadataRetriever()
val trackUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, song.id.toLong())
val trackUri =
ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, song.id.toLong())
retriever.setDataSource(activity, trackUri)
var genre: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE)
if (genre == null) {
@ -147,7 +158,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
MusicUtil.toggleFavorite(requireActivity(), song)
}
abstract fun playerToolbar(): Toolbar
abstract fun playerToolbar(): Toolbar?
abstract fun onShow()
@ -194,9 +205,9 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
R.drawable.ic_favorite_border_white_24dp
val drawable = RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor())
if (playerToolbar().menu.findItem(R.id.action_toggle_favorite) != null)
playerToolbar().menu.findItem(R.id.action_toggle_favorite).setIcon(drawable).title = if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(R.string.action_add_to_favorites)
if (playerToolbar() != null && playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite) != null)
playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable).title =
if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(R.string.action_add_to_favorites)
}
}.execute(MusicPlayerRemote.currentSong)
}
@ -238,17 +249,18 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
}
open fun setLyrics(l: Lyrics?) {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.setBackgroundColor(ATHUtil.resolveColor(requireContext(), R.attr.colorSecondary))
if (PreferenceUtil.getInstance(requireContext()).fullScreenMode &&
view.findViewById<View>(R.id.status_bar) != null) {
view.findViewById<View>(R.id.status_bar) != null
) {
view.findViewById<View>(R.id.status_bar).visibility = View.GONE
}
playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment?
playerAlbumCoverFragment =
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment?
playerAlbumCoverFragment?.setCallbacks(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
@ -269,8 +281,8 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
return MusicUtil.buildInfoString(
resources.getString(R.string.up_next),
MusicUtil.getReadableDurationString(duration)
resources.getString(R.string.up_next),
MusicUtil.getReadableDurationString(duration)
)
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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.
*
* This software 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.
*/
package code.name.monkey.retromusic.fragments.player.circle
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
/**
* Created by hemanths on 2020-01-06.
*/
class CirclePlayerFragment : AbsPlayerFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_circle_player, container, false)
}
override fun playerToolbar(): Toolbar? {
return null
}
override fun onShow() {
}
override fun onHide() {
}
override fun onBackPressed(): Boolean = false
override fun toolbarIconColor(): Int = Color.RED
override val paletteColor: Int
get() = Color.BLACK
override fun onColorChanged(color: Int) {
}
override fun onFavoriteToggled() {
}
}

View file

@ -1,5 +1,6 @@
package code.name.monkey.retromusic.fragments.player.full
import android.app.ActivityOptions
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
@ -155,7 +156,15 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca
private fun setupArtist() {
artistImage.setOnClickListener {
NavigationUtil.goToArtist(requireActivity(), MusicPlayerRemote.currentSong.artistId)
val transitionName =
"${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}"
val activityOptions =
ActivityOptions.makeSceneTransitionAnimation(requireActivity(), artistImage, transitionName)
NavigationUtil.goToArtistOptions(
requireActivity(),
MusicPlayerRemote.currentSong.artistId,
activityOptions
)
}
}

View file

@ -25,7 +25,6 @@ import android.content.Intent;
import android.media.audiofx.AudioEffect;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.activities.AboutActivity;
@ -79,7 +78,7 @@ public class NavigationUtil {
ActivityCompat.startActivity(activity, intent, null);
}
public static void goToArtistOptions(@NotNull AppCompatActivity activity,
public static void goToArtistOptions(@NotNull Activity activity,
int artistId,
@NonNull ActivityOptions options) {

View file

@ -0,0 +1,577 @@
/*
* Copyright (c) 2020 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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.
*
* This software 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.
*/
package code.name.monkey.retromusic.views;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import code.name.monkey.retromusic.R;
/**
* SeekArc.java
*
* This is a class that functions much like a SeekBar but
* follows a circle path instead of a straight line.
*
* @author Neil Davies
*/
public class SeekArc extends View {
public interface OnSeekArcChangeListener {
/**
* Notification that the progress level has changed. Clients can use the
* fromUser parameter to distinguish user-initiated changes from those
* that occurred programmatically.
*
* @param seekArc The SeekArc whose progress has changed
* @param progress The current progress level. This will be in the range
* 0..max where max was set by
* {@link ProgressArc#setMax(int)}. (The default value for
* max is 100.)
* @param fromUser True if the progress change was initiated by the user.
*/
void onProgressChanged(SeekArc seekArc, int progress, boolean fromUser);
/**
* Notification that the user has started a touch gesture. Clients may
* want to use this to disable advancing the seekbar.
*
* @param seekArc The SeekArc in which the touch gesture began
*/
void onStartTrackingTouch(SeekArc seekArc);
/**
* Notification that the user has finished a touch gesture. Clients may
* want to use this to re-enable advancing the seekarc.
*
* @param seekArc The SeekArc in which the touch gesture began
*/
void onStopTrackingTouch(SeekArc seekArc);
}
private static final String TAG = SeekArc.class.getSimpleName();
private static int INVALID_PROGRESS_VALUE = -1;
// The initial rotational offset -90 means we start at 12 o'clock
private final int mAngleOffset = -90;
private Paint mArcPaint;
// Internal variables
private int mArcRadius = 0;
private RectF mArcRect = new RectF();
/**
* The Width of the background arc for the SeekArc
*/
private int mArcWidth = 2;
/**
* Will the progress increase clockwise or anti-clockwise
*/
private boolean mClockwise = true;
/**
* is the control enabled/touchable
*/
private boolean mEnabled = true;
/**
* The Maximum value that this SeekArc can be set to
*/
private int mMax = 100;
private OnSeekArcChangeListener mOnSeekArcChangeListener;
/**
* The Current value that the SeekArc is set to
*/
private int mProgress = 0;
private Paint mProgressPaint;
private float mProgressSweep = 0;
/**
* The width of the progress line for this SeekArc
*/
private int mProgressWidth = 4;
/**
* The rotation of the SeekArc- 0 is twelve o'clock
*/
private int mRotation = 0;
/**
* Give the SeekArc rounded edges
*/
private boolean mRoundedEdges = false;
/**
* The Angle to start drawing this Arc from
*/
private int mStartAngle = 0;
/**
* The Angle through which to draw the arc (Max is 360)
*/
private int mSweepAngle = 360;
/**
* The Drawable for the seek arc thumbnail
*/
private Drawable mThumb;
private int mThumbXPos;
private int mThumbYPos;
private double mTouchAngle;
private float mTouchIgnoreRadius;
/**
* Enable touch inside the SeekArc
*/
private boolean mTouchInside = true;
private int mTranslateX;
private int mTranslateY;
public SeekArc(Context context) {
super(context);
init(context, null, 0);
}
public SeekArc(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, R.attr.seekArcStyle);
}
public SeekArc(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
public int getArcColor() {
return mArcPaint.getColor();
}
public void setArcColor(int color) {
mArcPaint.setColor(color);
invalidate();
}
public int getArcRotation() {
return mRotation;
}
public void setArcRotation(int mRotation) {
this.mRotation = mRotation;
updateThumbPosition();
}
public int getArcWidth() {
return mArcWidth;
}
public void setArcWidth(int mArcWidth) {
this.mArcWidth = mArcWidth;
mArcPaint.setStrokeWidth(mArcWidth);
}
public int getMax() {
return mMax;
}
public void setMax(int mMax) {
this.mMax = mMax;
}
public int getProgress() {
return mProgress;
}
public void setProgress(int progress) {
updateProgress(progress, false);
}
public int getProgressColor() {
return mProgressPaint.getColor();
}
public void setProgressColor(int color) {
mProgressPaint.setColor(color);
invalidate();
}
public int getProgressWidth() {
return mProgressWidth;
}
public void setProgressWidth(int mProgressWidth) {
this.mProgressWidth = mProgressWidth;
mProgressPaint.setStrokeWidth(mProgressWidth);
}
public int getStartAngle() {
return mStartAngle;
}
public void setStartAngle(int mStartAngle) {
this.mStartAngle = mStartAngle;
updateThumbPosition();
}
public int getSweepAngle() {
return mSweepAngle;
}
public void setSweepAngle(int mSweepAngle) {
this.mSweepAngle = mSweepAngle;
updateThumbPosition();
}
public boolean isClockwise() {
return mClockwise;
}
public void setClockwise(boolean isClockwise) {
mClockwise = isClockwise;
}
public boolean isEnabled() {
return mEnabled;
}
public void setEnabled(boolean enabled) {
this.mEnabled = enabled;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mEnabled) {
this.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onStartTrackingTouch();
updateOnTouch(event);
break;
case MotionEvent.ACTION_MOVE:
updateOnTouch(event);
break;
case MotionEvent.ACTION_UP:
onStopTrackingTouch();
setPressed(false);
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_CANCEL:
onStopTrackingTouch();
setPressed(false);
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return true;
}
return false;
}
/**
* Sets a listener to receive notifications of changes to the SeekArc's
* progress level. Also provides notifications of when the user starts and
* stops a touch gesture within the SeekArc.
*
* @param l The seek bar notification listener
* @see SeekArc.OnSeekBarChangeListener
*/
public void setOnSeekArcChangeListener(OnSeekArcChangeListener l) {
mOnSeekArcChangeListener = l;
}
public void setRoundedEdges(boolean isEnabled) {
mRoundedEdges = isEnabled;
if (mRoundedEdges) {
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
} else {
mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
mProgressPaint.setStrokeCap(Paint.Cap.SQUARE);
}
}
public void setTouchInSide(boolean isEnabled) {
int thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2;
int thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2;
mTouchInside = isEnabled;
if (mTouchInside) {
mTouchIgnoreRadius = (float) mArcRadius / 4;
} else {
// Don't use the exact radius makes interaction too tricky
mTouchIgnoreRadius = mArcRadius
- Math.min(thumbHalfWidth, thumbHalfheight);
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mThumb != null && mThumb.isStateful()) {
int[] state = getDrawableState();
mThumb.setState(state);
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (!mClockwise) {
canvas.scale(-1, 1, mArcRect.centerX(), mArcRect.centerY());
}
// Draw the arcs
final int arcStart = mStartAngle + mAngleOffset + mRotation;
final int arcSweep = mSweepAngle;
canvas.drawArc(mArcRect, arcStart, arcSweep, false, mArcPaint);
canvas.drawArc(mArcRect, arcStart, mProgressSweep, false,
mProgressPaint);
if (mEnabled) {
// Draw the thumb nail
canvas.translate(mTranslateX - mThumbXPos, mTranslateY - mThumbYPos);
mThumb.draw(canvas);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int height = getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
final int width = getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
final int min = Math.min(width, height);
float top = 0;
float left = 0;
int arcDiameter = 0;
mTranslateX = (int) (width * 0.5f);
mTranslateY = (int) (height * 0.5f);
arcDiameter = min - getPaddingLeft();
mArcRadius = arcDiameter / 2;
top = height / 2 - (arcDiameter / 2);
left = width / 2 - (arcDiameter / 2);
mArcRect.set(left, top, left + arcDiameter, top + arcDiameter);
int arcStart = (int) mProgressSweep + mStartAngle + mRotation + 90;
mThumbXPos = (int) (mArcRadius * Math.cos(Math.toRadians(arcStart)));
mThumbYPos = (int) (mArcRadius * Math.sin(Math.toRadians(arcStart)));
setTouchInSide(mTouchInside);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int getProgressForAngle(double angle) {
int touchProgress = (int) Math.round(valuePerDegree() * angle);
touchProgress = (touchProgress < 0) ? INVALID_PROGRESS_VALUE
: touchProgress;
touchProgress = (touchProgress > mMax) ? INVALID_PROGRESS_VALUE
: touchProgress;
return touchProgress;
}
private double getTouchDegrees(float xPos, float yPos) {
float x = xPos - mTranslateX;
float y = yPos - mTranslateY;
//invert the x-coord if we are rotating anti-clockwise
x = (mClockwise) ? x : -x;
// convert to arc Angle
double angle = Math.toDegrees(Math.atan2(y, x) + (Math.PI / 2)
- Math.toRadians(mRotation));
if (angle < 0) {
angle = 360 + angle;
}
angle -= mStartAngle;
return angle;
}
private boolean ignoreTouch(float xPos, float yPos) {
boolean ignore = false;
float x = xPos - mTranslateX;
float y = yPos - mTranslateY;
float touchRadius = (float) Math.sqrt(((x * x) + (y * y)));
if (touchRadius < mTouchIgnoreRadius) {
ignore = true;
}
return ignore;
}
private void init(Context context, AttributeSet attrs, int defStyle) {
Log.d(TAG, "Initialising SeekArc");
final Resources res = getResources();
float density = context.getResources().getDisplayMetrics().density;
// Defaults, may need to link this into theme settings
int arcColor = res.getColor(R.color.progress_gray);
int progressColor = res.getColor(R.color.default_blue_light);
int thumbHalfheight = 0;
int thumbHalfWidth = 0;
mThumb = res.getDrawable(R.drawable.switch_thumb_material);
// Convert progress width to pixels for current density
mProgressWidth = (int) (mProgressWidth * density);
if (attrs != null) {
// Attribute initialization
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SeekArc, defStyle, 0);
Drawable thumb = a.getDrawable(R.styleable.SeekArc_thumb);
if (thumb != null) {
mThumb = thumb;
}
thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2;
thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2;
mThumb.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth,
thumbHalfheight);
mMax = a.getInteger(R.styleable.SeekArc_max, mMax);
mProgress = a.getInteger(R.styleable.SeekArc_progress, mProgress);
mProgressWidth = (int) a.getDimension(
R.styleable.SeekArc_progressWidth, mProgressWidth);
mArcWidth = (int) a.getDimension(R.styleable.SeekArc_arcWidth,
mArcWidth);
mStartAngle = a.getInt(R.styleable.SeekArc_startAngle, mStartAngle);
mSweepAngle = a.getInt(R.styleable.SeekArc_sweepAngle, mSweepAngle);
mRotation = a.getInt(R.styleable.SeekArc_rotation, mRotation);
mRoundedEdges = a.getBoolean(R.styleable.SeekArc_roundEdges,
mRoundedEdges);
mTouchInside = a.getBoolean(R.styleable.SeekArc_touchInside,
mTouchInside);
mClockwise = a.getBoolean(R.styleable.SeekArc_clockwise,
mClockwise);
mEnabled = a.getBoolean(R.styleable.SeekArc_enabled, mEnabled);
arcColor = a.getColor(R.styleable.SeekArc_arcColor, arcColor);
progressColor = a.getColor(R.styleable.SeekArc_progressColor,
progressColor);
a.recycle();
}
mProgress = (mProgress > mMax) ? mMax : mProgress;
mProgress = (mProgress < 0) ? 0 : mProgress;
mSweepAngle = (mSweepAngle > 360) ? 360 : mSweepAngle;
mSweepAngle = (mSweepAngle < 0) ? 0 : mSweepAngle;
mProgressSweep = (float) mProgress / mMax * mSweepAngle;
mStartAngle = (mStartAngle > 360) ? 0 : mStartAngle;
mStartAngle = (mStartAngle < 0) ? 0 : mStartAngle;
mArcPaint = new Paint();
mArcPaint.setColor(arcColor);
mArcPaint.setAntiAlias(true);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(mArcWidth);
//mArcPaint.setAlpha(45);
mProgressPaint = new Paint();
mProgressPaint.setColor(progressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeWidth(mProgressWidth);
if (mRoundedEdges) {
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
}
}
private void onProgressRefresh(int progress, boolean fromUser) {
updateProgress(progress, fromUser);
}
private void onStartTrackingTouch() {
if (mOnSeekArcChangeListener != null) {
mOnSeekArcChangeListener.onStartTrackingTouch(this);
}
}
private void onStopTrackingTouch() {
if (mOnSeekArcChangeListener != null) {
mOnSeekArcChangeListener.onStopTrackingTouch(this);
}
}
private void updateOnTouch(MotionEvent event) {
boolean ignoreTouch = ignoreTouch(event.getX(), event.getY());
if (ignoreTouch) {
return;
}
setPressed(true);
mTouchAngle = getTouchDegrees(event.getX(), event.getY());
int progress = getProgressForAngle(mTouchAngle);
onProgressRefresh(progress, true);
}
private void updateProgress(int progress, boolean fromUser) {
if (progress == INVALID_PROGRESS_VALUE) {
return;
}
progress = (progress > mMax) ? mMax : progress;
progress = (progress < 0) ? 0 : progress;
mProgress = progress;
if (mOnSeekArcChangeListener != null) {
mOnSeekArcChangeListener
.onProgressChanged(this, progress, fromUser);
}
mProgressSweep = (float) progress / mMax * mSweepAngle;
updateThumbPosition();
invalidate();
}
private void updateThumbPosition() {
int thumbAngle = (int) (mStartAngle + mProgressSweep + mRotation + 90);
mThumbXPos = (int) (mArcRadius * Math.cos(Math.toRadians(thumbAngle)));
mThumbYPos = (int) (mArcRadius * Math.sin(Math.toRadians(thumbAngle)));
}
private float valuePerDegree() {
return (float) mMax / mSweepAngle;
}
}

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Hemanth Savarala.
~
~ Licensed under the GNU General Public License v3
~
~ This 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.
~
~ This software 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/titleContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="1"
android:textAppearance="@style/TextViewHeadline5"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="2"
android:padding="16dp"
android:textAppearance="@style/TextViewBody1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleContainer"
tools:text="@tools:sample/lorem/random" />
<code.name.monkey.retromusic.views.SeekArc
android:id="@+id/progressSlider"
android:layout_width="match_parent"
android:layout_height="280dp"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text"
app:rotation="180"
app:startAngle="30"
app:sweepAngle="300"
app:touchInside="true" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/playPauseButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/progressSlider"
app:layout_constraintEnd_toEndOf="@+id/progressSlider"
app:layout_constraintStart_toStartOf="@+id/progressSlider"
app:layout_constraintTop_toBottomOf="@+id/text" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/nextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:padding="16dp"
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
app:layout_constraintStart_toEndOf="@+id/playPauseButton"
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
app:srcCompat="@drawable/ic_skip_next_round_white_32dp"
app:tint="@color/md_green_500" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/previousButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:padding="16dp"
app:layout_constraintBottom_toBottomOf="@+id/playPauseButton"
app:layout_constraintEnd_toStartOf="@+id/playPauseButton"
app:layout_constraintTop_toTopOf="@+id/playPauseButton"
app:srcCompat="@drawable/ic_skip_previous_round_white_32dp"
app:tint="@color/md_green_500" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/volumeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Volume"
app:layout_constraintBottom_toTopOf="@+id/volume"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/volume" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/volume"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextViewHeadline6"
app:layout_constraintBottom_toBottomOf="@+id/progressSlider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/progressSlider"
tools:text="00:00" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -42,6 +42,8 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:freezesText="true"
android:gravity="center"
android:paddingTop="8dp"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
@ -55,6 +57,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingTop="8dp"
tools:text="Text" />

View file

@ -34,4 +34,8 @@
<color name="card_shadow_1">#d4d4d4</color>
<color name="card_shadow_2">#dddddd</color>
<color name="card_detailing">#eee</color>
<color name="progress_gray">#FFD8D8D8</color>
<color name="progress_gray_dark">#FF383838</color>
<color name="default_blue_light">#ff33b5e5</color>
</resources>

View file

@ -0,0 +1,48 @@
<!--
The MIT License (MIT)
Copyright (c) 2013 Triggertrap Ltd
Author Neil Davies
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
<resources>
<declare-styleable name="SeekArc">
<attr name="thumb" format="reference" />
<attr name="thumbOffset" format="dimension" />
<attr name="max" format="integer" />
<attr name="progressWidth" format="dimension" />
<attr name="arcWidth" format="dimension" />
<attr name="progress" format="integer" />
<attr name="rotation" format="integer" />
<attr name="startAngle" format="integer" />
<attr name="sweepAngle" format="integer" />
<attr name="arcColor" format="color" />
<attr name="progressColor" format="color" />
<attr name="roundEdges" format="boolean" />
<attr name="touchInside" format="boolean" />
<attr name="clockwise" format="boolean" />
<attr name="enabled" format="boolean" />
</declare-styleable>
<declare-styleable name="SeekArcTheme">
<attr name="seekArcStyle" format="reference"></attr>
</declare-styleable>
</resources>

View file

@ -817,4 +817,5 @@
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
<string name="circle">Circle</string>
</resources>