Fix animations, aligment
This commit is contained in:
parent
caed1ee47c
commit
a5e349380c
14 changed files with 858 additions and 26 deletions
|
@ -121,7 +121,8 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
|
||||||
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
|
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
|
||||||
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let {
|
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let {
|
||||||
artistDetailsPresenter.loadArtist(it)
|
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 {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
|
|
|
@ -8,11 +8,9 @@ import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
import code.name.monkey.retromusic.fragments.mainactivity.LibraryFragment
|
import code.name.monkey.retromusic.fragments.mainactivity.LibraryFragment
|
||||||
|
@ -251,7 +249,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val APP_INTRO_REQUEST = 2323
|
const val APP_INTRO_REQUEST = 2323
|
||||||
const val HOME = 0
|
const val HOME = 0
|
||||||
|
|
|
@ -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
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR_CARD
|
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR_CARD
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.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.COLOR
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FIT
|
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FIT
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FLAT
|
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.blur.BlurPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
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.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.color.ColorFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
||||||
|
@ -249,6 +251,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay
|
||||||
COLOR -> ColorFragment()
|
COLOR -> ColorFragment()
|
||||||
TINY -> TinyPlayerFragment()
|
TINY -> TinyPlayerFragment()
|
||||||
PEAK -> PeakPlayerFragment()
|
PEAK -> PeakPlayerFragment()
|
||||||
|
CIRCLE -> CirclePlayerFragment()
|
||||||
else -> PlayerFragment()
|
else -> PlayerFragment()
|
||||||
} // must implement AbsPlayerFragment
|
} // must implement AbsPlayerFragment
|
||||||
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)
|
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum class NowPlayingScreen constructor(
|
||||||
PEAK(R.string.peak, R.drawable.np_peak, 14),
|
PEAK(R.string.peak, R.drawable.np_peak, 14),
|
||||||
|
|
||||||
ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10),
|
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(R.string.blur, R.drawable.np_blur, 4),
|
||||||
BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9),
|
BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9),
|
||||||
CARD(R.string.card, R.drawable.np_card, 6),
|
CARD(R.string.card, R.drawable.np_card, 6),
|
||||||
|
|
|
@ -18,21 +18,31 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||||
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
|
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.extensions.hide
|
||||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.PaletteColorHolder
|
import code.name.monkey.retromusic.interfaces.PaletteColorHolder
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||||
import code.name.monkey.retromusic.util.*
|
import code.name.monkey.retromusic.util.LyricUtil
|
||||||
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
|
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
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
Toolbar.OnMenuItemClickListener,
|
Toolbar.OnMenuItemClickListener,
|
||||||
PaletteColorHolder,
|
PaletteColorHolder,
|
||||||
PlayerAlbumCoverFragment.Callbacks {
|
PlayerAlbumCoverFragment.Callbacks {
|
||||||
|
|
||||||
var callbacks: Callbacks? = null
|
var callbacks: Callbacks? = null
|
||||||
private set
|
private set
|
||||||
|
@ -41,7 +51,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
|
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
|
||||||
|
|
||||||
override fun onAttach(
|
override fun onAttach(
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
try {
|
try {
|
||||||
|
@ -57,7 +67,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemClick(
|
override fun onMenuItemClick(
|
||||||
item: MenuItem
|
item: MenuItem
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
|
@ -83,7 +93,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
}
|
}
|
||||||
R.id.action_save_playing_queue -> {
|
R.id.action_save_playing_queue -> {
|
||||||
CreatePlaylistDialog.create(MusicPlayerRemote.playingQueue)
|
CreatePlaylistDialog.create(MusicPlayerRemote.playingQueue)
|
||||||
.show(requireFragmentManager(), "ADD_TO_PLAYLIST")
|
.show(requireFragmentManager(), "ADD_TO_PLAYLIST")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_tag_editor -> {
|
R.id.action_tag_editor -> {
|
||||||
|
@ -130,7 +140,8 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
}
|
}
|
||||||
R.id.action_go_to_genre -> {
|
R.id.action_go_to_genre -> {
|
||||||
val retriever = MediaMetadataRetriever()
|
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)
|
retriever.setDataSource(activity, trackUri)
|
||||||
var genre: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE)
|
var genre: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE)
|
||||||
if (genre == null) {
|
if (genre == null) {
|
||||||
|
@ -147,7 +158,7 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
MusicUtil.toggleFavorite(requireActivity(), song)
|
MusicUtil.toggleFavorite(requireActivity(), song)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun playerToolbar(): Toolbar
|
abstract fun playerToolbar(): Toolbar?
|
||||||
|
|
||||||
abstract fun onShow()
|
abstract fun onShow()
|
||||||
|
|
||||||
|
@ -194,9 +205,9 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
R.drawable.ic_favorite_border_white_24dp
|
R.drawable.ic_favorite_border_white_24dp
|
||||||
|
|
||||||
val drawable = RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor())
|
val drawable = RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor())
|
||||||
if (playerToolbar().menu.findItem(R.id.action_toggle_favorite) != null)
|
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)
|
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)
|
}.execute(MusicPlayerRemote.currentSong)
|
||||||
}
|
}
|
||||||
|
@ -238,17 +249,18 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun setLyrics(l: Lyrics?) {
|
open fun setLyrics(l: Lyrics?) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
view.setBackgroundColor(ATHUtil.resolveColor(requireContext(), R.attr.colorSecondary))
|
view.setBackgroundColor(ATHUtil.resolveColor(requireContext(), R.attr.colorSecondary))
|
||||||
if (PreferenceUtil.getInstance(requireContext()).fullScreenMode &&
|
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
|
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)
|
playerAlbumCoverFragment?.setCallbacks(this)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||||
|
@ -269,8 +281,8 @@ abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||||
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||||
|
|
||||||
return MusicUtil.buildInfoString(
|
return MusicUtil.buildInfoString(
|
||||||
resources.getString(R.string.up_next),
|
resources.getString(R.string.up_next),
|
||||||
MusicUtil.getReadableDurationString(duration)
|
MusicUtil.getReadableDurationString(duration)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package code.name.monkey.retromusic.fragments.player.full
|
package code.name.monkey.retromusic.fragments.player.full
|
||||||
|
|
||||||
|
import android.app.ActivityOptions
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -155,7 +156,15 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca
|
||||||
|
|
||||||
private fun setupArtist() {
|
private fun setupArtist() {
|
||||||
artistImage.setOnClickListener {
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import android.content.Intent;
|
||||||
import android.media.audiofx.AudioEffect;
|
import android.media.audiofx.AudioEffect;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.activities.AboutActivity;
|
import code.name.monkey.retromusic.activities.AboutActivity;
|
||||||
|
@ -79,7 +78,7 @@ public class NavigationUtil {
|
||||||
ActivityCompat.startActivity(activity, intent, null);
|
ActivityCompat.startActivity(activity, intent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void goToArtistOptions(@NotNull AppCompatActivity activity,
|
public static void goToArtistOptions(@NotNull Activity activity,
|
||||||
int artistId,
|
int artistId,
|
||||||
@NonNull ActivityOptions options) {
|
@NonNull ActivityOptions options) {
|
||||||
|
|
||||||
|
|
577
app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java
Normal file
577
app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
119
app/src/main/res/layout/fragment_circle_player.xml
Normal file
119
app/src/main/res/layout/fragment_circle_player.xml
Normal 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>
|
|
@ -42,6 +42,8 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:freezesText="true"
|
android:freezesText="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="8dp"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
android:scrollHorizontally="true"
|
android:scrollHorizontally="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
@ -55,6 +57,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
tools:text="Text" />
|
tools:text="Text" />
|
||||||
|
|
|
@ -34,4 +34,8 @@
|
||||||
<color name="card_shadow_1">#d4d4d4</color>
|
<color name="card_shadow_1">#d4d4d4</color>
|
||||||
<color name="card_shadow_2">#dddddd</color>
|
<color name="card_shadow_2">#dddddd</color>
|
||||||
<color name="card_detailing">#eee</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>
|
</resources>
|
||||||
|
|
48
app/src/main/res/values/seekarc_attrs.xml
Normal file
48
app/src/main/res/values/seekarc_attrs.xml
Normal 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>
|
|
@ -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="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="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
||||||
|
<string name="circle">Circle</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue