From a5e349380c1b5d3c7739bc0b9524e2f151e91b75 Mon Sep 17 00:00:00 2001 From: h4h13 Date: Mon, 6 Jan 2020 23:37:02 +0530 Subject: [PATCH] Fix animations, aligment --- .../activities/ArtistDetailActivity.kt | 3 +- .../retromusic/activities/MainActivity.kt | 3 - .../base/AbsSlidingMusicPanelActivity.kt | 3 + .../retromusic/fragments/NowPlayingScreen.kt | 1 + .../fragments/base/AbsPlayerFragment.kt | 50 +- .../player/circle/CirclePlayerFragment.kt | 58 ++ .../player/full/FullPlayerFragment.kt | 11 +- .../retromusic/util/NavigationUtil.java | 3 +- .../name/monkey/retromusic/views/SeekArc.java | 577 ++++++++++++++++++ .../res/layout/fragment_circle_player.xml | 119 ++++ .../main/res/layout/fragment_plain_player.xml | 3 + app/src/main/res/values/colors.xml | 4 + app/src/main/res/values/seekarc_attrs.xml | 48 ++ app/src/main/res/values/strings.xml | 1 + 14 files changed, 858 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java create mode 100644 app/src/main/res/layout/fragment_circle_player.xml create mode 100644 app/src/main/res/values/seekarc_attrs.xml diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt index 319d6579..0873ad44 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt @@ -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() diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index 01ad928e..79562a3f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -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 diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index 2b4dd3c6..6f97b9f6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -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) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt index bde37820..209e8075 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt @@ -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), diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index 1189df52..e0b17496 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -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(R.id.status_bar) != null) { + view.findViewById(R.id.status_bar) != null + ) { view.findViewById(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) ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt new file mode 100644 index 00000000..32bb628c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt @@ -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() { + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index 1261781b..a86630ab 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -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 + ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java index b6318933..c07e83d5 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java @@ -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) { diff --git a/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java b/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java new file mode 100644 index 00000000..38128504 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java @@ -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; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_circle_player.xml b/app/src/main/res/layout/fragment_circle_player.xml new file mode 100644 index 00000000..ef8396cc --- /dev/null +++ b/app/src/main/res/layout/fragment_circle_player.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_plain_player.xml b/app/src/main/res/layout/fragment_plain_player.xml index 28e75a69..921ef638 100644 --- a/app/src/main/res/layout/fragment_plain_player.xml +++ b/app/src/main/res/layout/fragment_plain_player.xml @@ -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" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 51e143a1..f07ca089 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -34,4 +34,8 @@ #d4d4d4 #dddddd #eee + + #FFD8D8D8 + #FF383838 + #ff33b5e5 diff --git a/app/src/main/res/values/seekarc_attrs.xml b/app/src/main/res/values/seekarc_attrs.xml new file mode 100644 index 00000000..d8e6e351 --- /dev/null +++ b/app/src/main/res/values/seekarc_attrs.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc830cfb..e3ec9629 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -817,4 +817,5 @@ You will be forwarded to the issue tracker website. Your account data is only used for authentication. + Circle