Removed suggestions
This commit is contained in:
parent
43cb27ae5f
commit
b46175e20b
28 changed files with 145 additions and 440 deletions
|
@ -153,6 +153,6 @@ class ArtistImageLoader(private val context: Context, private val lastFMClient:
|
|||
|
||||
companion object {
|
||||
// we need these very low values to make sure our artist image loading calls doesn't block the image loading queue
|
||||
private const val TIMEOUT = 500
|
||||
private const val TIMEOUT = 700
|
||||
}
|
||||
}
|
|
@ -12,33 +12,33 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util.schedulers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
package code.name.monkey.retromusic.lyrics;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 12/08/17.
|
||||
* Desc : 歌词实体
|
||||
* Author : Lauzy
|
||||
* Date : 2017/10/13
|
||||
* Blog : http://www.jianshu.com/u/e76853f863a9
|
||||
* Email : freedompaladin@gmail.com
|
||||
*/
|
||||
public class Lrc {
|
||||
private long time;
|
||||
private String text;
|
||||
|
||||
public class ImmediateScheduler implements BaseSchedulerProvider {
|
||||
@NonNull
|
||||
@Override
|
||||
public Scheduler computation() {
|
||||
return Schedulers.trampoline();
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Scheduler io() {
|
||||
return Schedulers.trampoline();
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Scheduler ui() {
|
||||
return Schedulers.trampoline();
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.lyrics;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Desc : 歌词解析
|
||||
* Author : Lauzy
|
||||
* Date : 2017/10/13
|
||||
* Blog : http://www.jianshu.com/u/e76853f863a9
|
||||
* Email : freedompaladin@gmail.com
|
||||
*/
|
||||
public class LrcHelper {
|
||||
|
||||
private static final String CHARSET = "utf-8";
|
||||
//[03:56.00][03:18.00][02:06.00][01:07.00]原谅我这一生不羁放纵爱自由
|
||||
private static final String LINE_REGEX = "((\\[\\d{2}:\\d{2}\\.\\d{2}])+)(.*)";
|
||||
private static final String TIME_REGEX = "\\[(\\d{2}):(\\d{2})\\.(\\d{2})]";
|
||||
|
||||
public static List<Lrc> parseLrcFromAssets(Context context, String fileName) {
|
||||
try {
|
||||
return parseInputStream(context.getResources().getAssets().open(fileName));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Lrc> parseLrcFromFile(File file) {
|
||||
try {
|
||||
return parseInputStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<Lrc> parseInputStream(InputStream inputStream) {
|
||||
List<Lrc> lrcs = new ArrayList<>();
|
||||
InputStreamReader isr = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
isr = new InputStreamReader(inputStream, CHARSET);
|
||||
br = new BufferedReader(isr);
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
List<Lrc> lrcList = parseLrc(line);
|
||||
if (lrcList != null && lrcList.size() != 0) {
|
||||
lrcs.addAll(lrcList);
|
||||
}
|
||||
}
|
||||
sortLrcs(lrcs);
|
||||
return lrcs;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (isr != null) {
|
||||
isr.close();
|
||||
}
|
||||
if (br != null) {
|
||||
br.close();
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
return lrcs;
|
||||
}
|
||||
|
||||
private static void sortLrcs(List<Lrc> lrcs) {
|
||||
Collections.sort(lrcs, new Comparator<Lrc>() {
|
||||
@Override
|
||||
public int compare(Lrc o1, Lrc o2) {
|
||||
return (int) (o1.getTime() - o2.getTime());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Lrc> parseLrc(String lrcLine) {
|
||||
if (lrcLine.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<Lrc> lrcs = new ArrayList<>();
|
||||
Matcher matcher = Pattern.compile(LINE_REGEX).matcher(lrcLine);
|
||||
if (!matcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String time = matcher.group(1);
|
||||
String content = matcher.group(3);
|
||||
Matcher timeMatcher = Pattern.compile(TIME_REGEX).matcher(time);
|
||||
|
||||
while (timeMatcher.find()) {
|
||||
String min = timeMatcher.group(1);
|
||||
String sec = timeMatcher.group(2);
|
||||
String mil = timeMatcher.group(3);
|
||||
Lrc lrc = new Lrc();
|
||||
if (content != null && content.length() != 0) {
|
||||
lrc.setTime(Long.parseLong(min) * 60 * 1000 + Long.parseLong(sec) * 1000
|
||||
+ Long.parseLong(mil) * 10);
|
||||
lrc.setText(content);
|
||||
lrcs.add(lrc);
|
||||
}
|
||||
}
|
||||
return lrcs;
|
||||
}
|
||||
|
||||
public static String formatTime(long time) {
|
||||
int min = (int) (time / 60000);
|
||||
int sec = (int) (time / 1000 % 60);
|
||||
return adjustFormat(min) + ":" + adjustFormat(sec);
|
||||
}
|
||||
|
||||
private static String adjustFormat(int time) {
|
||||
if (time < 10) {
|
||||
return "0" + time;
|
||||
}
|
||||
return time + "";
|
||||
}
|
||||
}
|
635
app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.kt
Normal file
635
app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.kt
Normal file
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.lyrics
|
||||
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Looper
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.OverScroller
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Desc : 歌词
|
||||
* Author : Lauzy
|
||||
* Date : 2017/10/13
|
||||
* Blog : http://www.jianshu.com/u/e76853f863a9
|
||||
* Email : freedompaladin@gmail.com
|
||||
*/
|
||||
class LrcView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
|
||||
private var mLrcData: MutableList<Lrc>? = null
|
||||
private var mTextPaint: TextPaint? = null
|
||||
private var mDefaultContent: String? = null
|
||||
private var mCurrentLine: Int = 0
|
||||
private var mOffset: Float = 0.toFloat()
|
||||
private var mLastMotionX: Float = 0.toFloat()
|
||||
private var mLastMotionY: Float = 0.toFloat()
|
||||
private var mScaledTouchSlop: Int = 0
|
||||
private var mOverScroller: OverScroller? = null
|
||||
private var mVelocityTracker: VelocityTracker? = null
|
||||
private var mMaximumFlingVelocity: Int = 0
|
||||
private var mMinimumFlingVelocity: Int = 0
|
||||
private var mLrcTextSize: Float = 0.toFloat()
|
||||
private var mLrcLineSpaceHeight: Float = 0.toFloat()
|
||||
private var mTouchDelay: Int = 0
|
||||
private var mNormalColor: Int = 0
|
||||
private var mCurrentPlayLineColor: Int = 0
|
||||
private var mNoLrcTextSize: Float = 0.toFloat()
|
||||
private var mNoLrcTextColor: Int = 0
|
||||
//是否拖拽中,否的话响应onClick事件
|
||||
private var isDragging: Boolean = false
|
||||
//用户开始操作
|
||||
private var isUserScroll: Boolean = false
|
||||
private var isAutoAdjustPosition = true
|
||||
private var mPlayDrawable: Drawable? = null
|
||||
private var isShowTimeIndicator: Boolean = false
|
||||
private var mPlayRect: Rect? = null
|
||||
private var mIndicatorPaint: Paint? = null
|
||||
private var mIndicatorLineWidth: Float = 0.toFloat()
|
||||
private var mIndicatorTextSize: Float = 0.toFloat()
|
||||
private var mCurrentIndicateLineTextColor: Int = 0
|
||||
private var mIndicatorLineColor: Int = 0
|
||||
private var mIndicatorMargin: Float = 0.toFloat()
|
||||
private var mIconLineGap: Float = 0.toFloat()
|
||||
private var mIconWidth: Float = 0.toFloat()
|
||||
private var mIconHeight: Float = 0.toFloat()
|
||||
private var isEnableShowIndicator = true
|
||||
private var mIndicatorTextColor: Int = 0
|
||||
private var mIndicatorTouchDelay: Int = 0
|
||||
private val mLrcMap = HashMap<String, StaticLayout>()
|
||||
private val mHideIndicatorRunnable = Runnable {
|
||||
isShowTimeIndicator = false
|
||||
invalidateView()
|
||||
}
|
||||
private val mStaticLayoutHashMap = HashMap<String, StaticLayout>()
|
||||
private val mScrollRunnable = Runnable {
|
||||
isUserScroll = false
|
||||
scrollToPosition(mCurrentLine)
|
||||
}
|
||||
private var mOnPlayIndicatorLineListener: OnPlayIndicatorLineListener? = null
|
||||
|
||||
private val lrcWidth: Int
|
||||
get() = width - paddingLeft - paddingRight
|
||||
|
||||
private val lrcHeight: Int
|
||||
get() = height
|
||||
|
||||
private val isLrcEmpty: Boolean
|
||||
get() = mLrcData == null || lrcCount == 0
|
||||
|
||||
private val lrcCount: Int
|
||||
get() = mLrcData!!.size
|
||||
|
||||
//itemOffset 和 mOffset 最小即当前位置
|
||||
val indicatePosition: Int
|
||||
get() {
|
||||
var pos = 0
|
||||
var min = java.lang.Float.MAX_VALUE
|
||||
for (i in mLrcData!!.indices) {
|
||||
val offsetY = getItemOffsetY(i)
|
||||
val abs = Math.abs(offsetY - mOffset)
|
||||
if (abs < min) {
|
||||
min = abs
|
||||
pos = i
|
||||
}
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
var playDrawable: Drawable?
|
||||
get() = mPlayDrawable
|
||||
set(playDrawable) {
|
||||
mPlayDrawable = playDrawable
|
||||
mPlayDrawable!!.bounds = mPlayRect!!
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
init {
|
||||
init(context, attrs)
|
||||
}
|
||||
|
||||
private fun init(context: Context, attrs: AttributeSet?) {
|
||||
|
||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.LrcView)
|
||||
mLrcTextSize = typedArray.getDimension(R.styleable.LrcView_lrcTextSize, sp2px(context, 15f).toFloat())
|
||||
mLrcLineSpaceHeight = typedArray.getDimension(R.styleable.LrcView_lrcLineSpaceSize, dp2px(context, 20f).toFloat())
|
||||
mTouchDelay = typedArray.getInt(R.styleable.LrcView_lrcTouchDelay, 3500)
|
||||
mIndicatorTouchDelay = typedArray.getInt(R.styleable.LrcView_indicatorTouchDelay, 2500)
|
||||
mNormalColor = typedArray.getColor(R.styleable.LrcView_lrcNormalTextColor, Color.GRAY)
|
||||
mCurrentPlayLineColor = typedArray.getColor(R.styleable.LrcView_lrcCurrentTextColor, Color.BLUE)
|
||||
mNoLrcTextSize = typedArray.getDimension(R.styleable.LrcView_noLrcTextSize, dp2px(context, 20f).toFloat())
|
||||
mNoLrcTextColor = typedArray.getColor(R.styleable.LrcView_noLrcTextColor, Color.BLACK)
|
||||
mIndicatorLineWidth = typedArray.getDimension(R.styleable.LrcView_indicatorLineHeight, dp2px(context, 0.5f).toFloat())
|
||||
mIndicatorTextSize = typedArray.getDimension(R.styleable.LrcView_indicatorTextSize, sp2px(context, 13f).toFloat())
|
||||
mIndicatorTextColor = typedArray.getColor(R.styleable.LrcView_indicatorTextColor, Color.GRAY)
|
||||
mCurrentIndicateLineTextColor = typedArray.getColor(R.styleable.LrcView_currentIndicateLrcColor, Color.GRAY)
|
||||
mIndicatorLineColor = typedArray.getColor(R.styleable.LrcView_indicatorLineColor, Color.GRAY)
|
||||
mIndicatorMargin = typedArray.getDimension(R.styleable.LrcView_indicatorStartEndMargin, dp2px(context, 5f).toFloat())
|
||||
mIconLineGap = typedArray.getDimension(R.styleable.LrcView_iconLineGap, dp2px(context, 3f).toFloat())
|
||||
mIconWidth = typedArray.getDimension(R.styleable.LrcView_playIconWidth, dp2px(context, 20f).toFloat())
|
||||
mIconHeight = typedArray.getDimension(R.styleable.LrcView_playIconHeight, dp2px(context, 20f).toFloat())
|
||||
mPlayDrawable = typedArray.getDrawable(R.styleable.LrcView_playIcon)
|
||||
mPlayDrawable = if (mPlayDrawable == null) ContextCompat.getDrawable(context, R.drawable.play_icon) else mPlayDrawable
|
||||
typedArray.recycle()
|
||||
|
||||
setupConfigs(context)
|
||||
}
|
||||
|
||||
private fun setupConfigs(context: Context) {
|
||||
mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||
mMaximumFlingVelocity = ViewConfiguration.get(context).scaledMaximumFlingVelocity
|
||||
mMinimumFlingVelocity = ViewConfiguration.get(context).scaledMinimumFlingVelocity
|
||||
mOverScroller = OverScroller(context, DecelerateInterpolator())
|
||||
mOverScroller!!.setFriction(0.1f)
|
||||
// ViewConfiguration.getScrollFriction(); 默认摩擦力 0.015f
|
||||
|
||||
mTextPaint = TextPaint()
|
||||
mTextPaint!!.apply {
|
||||
isAntiAlias = true
|
||||
textAlign = Paint.Align.LEFT
|
||||
textSize = mLrcTextSize
|
||||
typeface = ResourcesCompat.getFont(context, R.font.circular)
|
||||
}
|
||||
mDefaultContent = DEFAULT_CONTENT
|
||||
|
||||
mIndicatorPaint = Paint()
|
||||
mIndicatorPaint!!.isAntiAlias = true
|
||||
mIndicatorPaint!!.strokeWidth = mIndicatorLineWidth
|
||||
mIndicatorPaint!!.color = mIndicatorLineColor
|
||||
mPlayRect = Rect()
|
||||
mIndicatorPaint!!.textSize = mIndicatorTextSize
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
if (changed) {
|
||||
mPlayRect!!.left = mIndicatorMargin.toInt()
|
||||
mPlayRect!!.top = (height / 2 - mIconHeight / 2).toInt()
|
||||
mPlayRect!!.right = (mPlayRect!!.left + mIconWidth).toInt()
|
||||
mPlayRect!!.bottom = (mPlayRect!!.top + mIconHeight).toInt()
|
||||
mPlayDrawable!!.bounds = mPlayRect!!
|
||||
}
|
||||
}
|
||||
|
||||
fun setLrcData(lrcData: MutableList<Lrc>) {
|
||||
resetView(DEFAULT_CONTENT)
|
||||
mLrcData = lrcData
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
if (isLrcEmpty) {
|
||||
drawEmptyText(canvas)
|
||||
return
|
||||
}
|
||||
val indicatePosition = indicatePosition
|
||||
mTextPaint!!.textSize = mLrcTextSize
|
||||
mTextPaint!!.textAlign = Paint.Align.LEFT
|
||||
var y = (lrcHeight / 2).toFloat()
|
||||
val x = dip2px(context, 16f).toFloat()
|
||||
for (i in 0 until lrcCount) {
|
||||
if (i > 0) {
|
||||
y += (getTextHeight(i - 1) + getTextHeight(i)) / 2 + mLrcLineSpaceHeight
|
||||
}
|
||||
if (mCurrentLine == i) {
|
||||
mTextPaint!!.color = mCurrentPlayLineColor
|
||||
} else if (indicatePosition == i && isShowTimeIndicator) {
|
||||
mTextPaint!!.color = mCurrentIndicateLineTextColor
|
||||
} else {
|
||||
mTextPaint!!.color = mNormalColor
|
||||
}
|
||||
drawLrc(canvas, x, y, i)
|
||||
}
|
||||
|
||||
if (isShowTimeIndicator) {
|
||||
mPlayDrawable!!.draw(canvas)
|
||||
val time = mLrcData!![indicatePosition].time
|
||||
val timeWidth = mIndicatorPaint!!.measureText(LrcHelper.formatTime(time))
|
||||
mIndicatorPaint!!.color = mIndicatorLineColor
|
||||
canvas.drawLine(mPlayRect!!.right + mIconLineGap, (height / 2).toFloat(),
|
||||
width - timeWidth * 1.3f, (height / 2).toFloat(), mIndicatorPaint!!)
|
||||
val baseX = (width - timeWidth * 1.1f).toInt()
|
||||
val baseline = (height / 2).toFloat() - (mIndicatorPaint!!.descent() - mIndicatorPaint!!.ascent()) / 2 - mIndicatorPaint!!.ascent()
|
||||
mIndicatorPaint!!.color = mIndicatorTextColor
|
||||
canvas.drawText(LrcHelper.formatTime(time), baseX.toFloat(), baseline, mIndicatorPaint!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dip2px(context: Context, dpVale: Float): Int {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return (dpVale * scale + 0.5f).toInt()
|
||||
}
|
||||
|
||||
private fun drawLrc(canvas: Canvas, x: Float, y: Float, i: Int) {
|
||||
val text = mLrcData!![i].text
|
||||
var staticLayout: StaticLayout? = mLrcMap[text]
|
||||
if (staticLayout == null) {
|
||||
mTextPaint!!.textSize = mLrcTextSize
|
||||
staticLayout = StaticLayout(text, mTextPaint, lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
|
||||
mLrcMap[text] = staticLayout
|
||||
}
|
||||
canvas.save()
|
||||
canvas.translate(x, y - (staticLayout.height / 2).toFloat() - mOffset)
|
||||
staticLayout.draw(canvas)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
//中间空文字
|
||||
private fun drawEmptyText(canvas: Canvas) {
|
||||
mTextPaint!!.textAlign = Paint.Align.LEFT
|
||||
mTextPaint!!.color = mNoLrcTextColor
|
||||
mTextPaint!!.textSize = mNoLrcTextSize
|
||||
canvas.save()
|
||||
val staticLayout = StaticLayout(mDefaultContent, mTextPaint,
|
||||
lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
|
||||
val margin = dip2px(context, 16f).toFloat();
|
||||
canvas.translate(margin, margin)
|
||||
staticLayout.draw(canvas)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
fun updateTime(time: Long) {
|
||||
if (isLrcEmpty) {
|
||||
return
|
||||
}
|
||||
val linePosition = getUpdateTimeLinePosition(time)
|
||||
if (mCurrentLine != linePosition) {
|
||||
mCurrentLine = linePosition
|
||||
if (isUserScroll) {
|
||||
invalidateView()
|
||||
return
|
||||
}
|
||||
ViewCompat.postOnAnimation(this@LrcView, mScrollRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUpdateTimeLinePosition(time: Long): Int {
|
||||
var linePos = 0
|
||||
for (i in 0 until lrcCount) {
|
||||
val lrc = mLrcData!![i]
|
||||
if (time >= lrc.time) {
|
||||
if (i == lrcCount - 1) {
|
||||
linePos = lrcCount - 1
|
||||
} else if (time < mLrcData!![i + 1].time) {
|
||||
linePos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return linePos
|
||||
}
|
||||
|
||||
private fun scrollToPosition(linePosition: Int) {
|
||||
val scrollY = getItemOffsetY(linePosition)
|
||||
val animator = ValueAnimator.ofFloat(mOffset, scrollY)
|
||||
animator.addUpdateListener { animation ->
|
||||
mOffset = animation.animatedValue as Float
|
||||
invalidateView()
|
||||
}
|
||||
animator.duration = 300
|
||||
animator.start()
|
||||
}
|
||||
|
||||
private fun getItemOffsetY(linePosition: Int): Float {
|
||||
var tempY = 0f
|
||||
for (i in 1..linePosition) {
|
||||
tempY += (getTextHeight(i - 1) + getTextHeight(i)) / 2 + mLrcLineSpaceHeight
|
||||
}
|
||||
return tempY
|
||||
}
|
||||
|
||||
private fun getTextHeight(linePosition: Int): Float {
|
||||
val text = mLrcData!![linePosition].text
|
||||
var staticLayout: StaticLayout? = mStaticLayoutHashMap[text]
|
||||
if (staticLayout == null) {
|
||||
mTextPaint!!.textSize = mLrcTextSize
|
||||
staticLayout = StaticLayout(text, mTextPaint,
|
||||
lrcWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
|
||||
mStaticLayoutHashMap[text] = staticLayout
|
||||
}
|
||||
return staticLayout.height.toFloat()
|
||||
}
|
||||
|
||||
private fun overScrolled(): Boolean {
|
||||
return mOffset > getItemOffsetY(lrcCount - 1) || mOffset < 0
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (isLrcEmpty) {
|
||||
return super.onTouchEvent(event)
|
||||
}
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain()
|
||||
}
|
||||
mVelocityTracker!!.addMovement(event)
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
removeCallbacks(mScrollRunnable)
|
||||
removeCallbacks(mHideIndicatorRunnable)
|
||||
if (!mOverScroller!!.isFinished) {
|
||||
mOverScroller!!.abortAnimation()
|
||||
}
|
||||
mLastMotionX = event.x
|
||||
mLastMotionY = event.y
|
||||
isUserScroll = true
|
||||
isDragging = false
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
var moveY = event.y - mLastMotionY
|
||||
if (Math.abs(moveY) > mScaledTouchSlop) {
|
||||
isDragging = true
|
||||
isShowTimeIndicator = isEnableShowIndicator
|
||||
}
|
||||
if (isDragging) {
|
||||
|
||||
// if (mOffset < 0) {
|
||||
// mOffset = Math.max(mOffset, -getTextHeight(0) - mLrcLineSpaceHeight);
|
||||
// }
|
||||
val maxHeight = getItemOffsetY(lrcCount - 1)
|
||||
// if (mOffset > maxHeight) {
|
||||
// mOffset = Math.min(mOffset, maxHeight + getTextHeight(getLrcCount() - 1) + mLrcLineSpaceHeight);
|
||||
// }
|
||||
if (mOffset < 0 || mOffset > maxHeight) {
|
||||
moveY /= 3.5f
|
||||
}
|
||||
mOffset -= moveY
|
||||
mLastMotionY = event.y
|
||||
invalidateView()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
|
||||
if (!isDragging && (!isShowTimeIndicator || !onClickPlayButton(event))) {
|
||||
isShowTimeIndicator = false
|
||||
invalidateView()
|
||||
performClick()
|
||||
}
|
||||
handleActionUp(event)
|
||||
}
|
||||
}
|
||||
// return isDragging || super.onTouchEvent(event);
|
||||
return true
|
||||
}
|
||||
|
||||
private fun handleActionUp(event: MotionEvent) {
|
||||
if (isEnableShowIndicator) {
|
||||
ViewCompat.postOnAnimationDelayed(this@LrcView, mHideIndicatorRunnable, mIndicatorTouchDelay.toLong())
|
||||
}
|
||||
if (isShowTimeIndicator && mPlayRect != null && onClickPlayButton(event)) {
|
||||
isShowTimeIndicator = false
|
||||
invalidateView()
|
||||
if (mOnPlayIndicatorLineListener != null) {
|
||||
mOnPlayIndicatorLineListener!!.onPlay(mLrcData!![indicatePosition].time,
|
||||
mLrcData!![indicatePosition].text)
|
||||
}
|
||||
}
|
||||
if (overScrolled() && mOffset < 0) {
|
||||
scrollToPosition(0)
|
||||
if (isAutoAdjustPosition) {
|
||||
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (overScrolled() && mOffset > getItemOffsetY(lrcCount - 1)) {
|
||||
scrollToPosition(lrcCount - 1)
|
||||
if (isAutoAdjustPosition) {
|
||||
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mVelocityTracker!!.computeCurrentVelocity(1000, mMaximumFlingVelocity.toFloat())
|
||||
val YVelocity = mVelocityTracker!!.yVelocity
|
||||
val absYVelocity = Math.abs(YVelocity)
|
||||
if (absYVelocity > mMinimumFlingVelocity) {
|
||||
mOverScroller!!.fling(0, mOffset.toInt(), 0, (-YVelocity).toInt(), 0,
|
||||
0, 0, getItemOffsetY(lrcCount - 1).toInt(),
|
||||
0, getTextHeight(0).toInt())
|
||||
invalidateView()
|
||||
}
|
||||
releaseVelocityTracker()
|
||||
if (isAutoAdjustPosition) {
|
||||
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickPlayButton(event: MotionEvent): Boolean {
|
||||
val left = mPlayRect!!.left.toFloat()
|
||||
val right = mPlayRect!!.right.toFloat()
|
||||
val top = mPlayRect!!.top.toFloat()
|
||||
val bottom = mPlayRect!!.bottom.toFloat()
|
||||
val x = event.x
|
||||
val y = event.y
|
||||
return (mLastMotionX > left && mLastMotionX < right && mLastMotionY > top
|
||||
&& mLastMotionY < bottom && x > left && x < right && y > top && y < bottom)
|
||||
}
|
||||
|
||||
override fun computeScroll() {
|
||||
super.computeScroll()
|
||||
if (mOverScroller!!.computeScrollOffset()) {
|
||||
mOffset = mOverScroller!!.currY.toFloat()
|
||||
invalidateView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun releaseVelocityTracker() {
|
||||
if (null != mVelocityTracker) {
|
||||
mVelocityTracker!!.clear()
|
||||
mVelocityTracker!!.recycle()
|
||||
mVelocityTracker = null
|
||||
}
|
||||
}
|
||||
|
||||
fun resetView(defaultContent: String) {
|
||||
if (mLrcData != null) {
|
||||
mLrcData!!.clear()
|
||||
}
|
||||
mLrcMap.clear()
|
||||
mStaticLayoutHashMap.clear()
|
||||
mCurrentLine = 0
|
||||
mOffset = 0f
|
||||
isUserScroll = false
|
||||
isDragging = false
|
||||
mDefaultContent = defaultContent
|
||||
removeCallbacks(mScrollRunnable)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun performClick(): Boolean {
|
||||
return super.performClick()
|
||||
}
|
||||
|
||||
fun dp2px(context: Context, dpVal: Float): Int {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||
dpVal, context.resources.displayMetrics).toInt()
|
||||
}
|
||||
|
||||
fun sp2px(context: Context, spVal: Float): Int {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
|
||||
spVal, context.resources.displayMetrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停(手动滑动歌词后,不再自动回滚至当前播放位置)
|
||||
*/
|
||||
fun pause() {
|
||||
isAutoAdjustPosition = false
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复(继续自动回滚)
|
||||
*/
|
||||
fun resume() {
|
||||
isAutoAdjustPosition = true
|
||||
ViewCompat.postOnAnimationDelayed(this@LrcView, mScrollRunnable, mTouchDelay.toLong())
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
|
||||
/*------------------Config-------------------*/
|
||||
|
||||
private fun invalidateView() {
|
||||
if (Looper.getMainLooper().thread === Thread.currentThread()) {
|
||||
invalidate()
|
||||
} else {
|
||||
postInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnPlayIndicatorLineListener(onPlayIndicatorLineListener: OnPlayIndicatorLineListener) {
|
||||
mOnPlayIndicatorLineListener = onPlayIndicatorLineListener
|
||||
}
|
||||
|
||||
fun setEmptyContent(defaultContent: String) {
|
||||
mDefaultContent = defaultContent
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setLrcTextSize(lrcTextSize: Float) {
|
||||
mLrcTextSize = lrcTextSize
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setLrcLineSpaceHeight(lrcLineSpaceHeight: Float) {
|
||||
mLrcLineSpaceHeight = lrcLineSpaceHeight
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setTouchDelay(touchDelay: Int) {
|
||||
mTouchDelay = touchDelay
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setNormalColor(@ColorInt normalColor: Int) {
|
||||
mNormalColor = normalColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setCurrentPlayLineColor(@ColorInt currentPlayLineColor: Int) {
|
||||
mCurrentPlayLineColor = currentPlayLineColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setNoLrcTextSize(noLrcTextSize: Float) {
|
||||
mNoLrcTextSize = noLrcTextSize
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setNoLrcTextColor(@ColorInt noLrcTextColor: Int) {
|
||||
mNoLrcTextColor = noLrcTextColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIndicatorLineWidth(indicatorLineWidth: Float) {
|
||||
mIndicatorLineWidth = indicatorLineWidth
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIndicatorTextSize(indicatorTextSize: Float) {
|
||||
// mIndicatorTextSize = indicatorTextSize;
|
||||
mIndicatorPaint!!.textSize = indicatorTextSize
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setCurrentIndicateLineTextColor(currentIndicateLineTextColor: Int) {
|
||||
mCurrentIndicateLineTextColor = currentIndicateLineTextColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIndicatorLineColor(indicatorLineColor: Int) {
|
||||
mIndicatorLineColor = indicatorLineColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIndicatorMargin(indicatorMargin: Float) {
|
||||
mIndicatorMargin = indicatorMargin
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIconLineGap(iconLineGap: Float) {
|
||||
mIconLineGap = iconLineGap
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIconWidth(iconWidth: Float) {
|
||||
mIconWidth = iconWidth
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIconHeight(iconHeight: Float) {
|
||||
mIconHeight = iconHeight
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setEnableShowIndicator(enableShowIndicator: Boolean) {
|
||||
isEnableShowIndicator = enableShowIndicator
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
fun setIndicatorTextColor(indicatorTextColor: Int) {
|
||||
mIndicatorTextColor = indicatorTextColor
|
||||
invalidateView()
|
||||
}
|
||||
|
||||
interface OnPlayIndicatorLineListener {
|
||||
fun onPlay(time: Long, content: String)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_CONTENT = "Empty"
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.GENRES
|
|||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.PLAYLISTS
|
||||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.RECENT_ALBUMS
|
||||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.RECENT_ARTISTS
|
||||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.SUGGESTIONS
|
||||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.TOP_ALBUMS
|
||||
import code.name.monkey.retromusic.ui.adapter.HomeAdapter.Companion.TOP_ARTISTS
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
@ -80,7 +79,7 @@ class HomePresenter(private val view: HomeContract.HomeView) : Presenter(), Home
|
|||
disposable += repository.topAlbums
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
if (it.isNotEmpty()) hashSet.add(Home(2, R.string.top_albums, 0, it, TOP_ALBUMS, R.drawable.ic_album_white_24dp))
|
||||
if (it.isNotEmpty()) hashSet.add(Home(3, R.string.top_albums, 0, it, TOP_ALBUMS, R.drawable.ic_album_white_24dp))
|
||||
view.showData(ArrayList(hashSet))
|
||||
}, {
|
||||
view.showEmpty()
|
||||
|
@ -91,7 +90,7 @@ class HomePresenter(private val view: HomeContract.HomeView) : Presenter(), Home
|
|||
disposable += repository.topArtists
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
if (it.isNotEmpty()) hashSet.add(Home(3, R.string.top_artists, 0, it, TOP_ARTISTS, R.drawable.ic_artist_white_24dp))
|
||||
if (it.isNotEmpty()) hashSet.add(Home(2, R.string.top_artists, 0, it, TOP_ARTISTS, R.drawable.ic_artist_white_24dp))
|
||||
view.showData(ArrayList(hashSet))
|
||||
}, {
|
||||
view.showEmpty()
|
||||
|
|
|
@ -10,6 +10,8 @@ import android.widget.ArrayAdapter
|
|||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
|
@ -24,17 +26,7 @@ import kotlinx.android.synthetic.main.activity_equalizer.*
|
|||
|
||||
class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedListener {
|
||||
|
||||
|
||||
/*private val mListener = { buttonView, isChecked ->
|
||||
when (buttonView.getId()) {
|
||||
R.id.equalizerSwitch -> {
|
||||
EqualizerHelper.instance!!.equalizer.enabled = isChecked
|
||||
TransitionManager.beginDelayedTransition(content)
|
||||
content.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}*/
|
||||
private val mSeekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
|
||||
private val seekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (seekBar === bassBoostStrength) {
|
||||
|
@ -58,14 +50,12 @@ class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedL
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private var mPresetsNamesAdapter: ArrayAdapter<String>? = null
|
||||
private var presetsNamesAdapter: ArrayAdapter<String>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_equalizer)
|
||||
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
@ -74,7 +64,10 @@ class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedL
|
|||
setupToolbar()
|
||||
|
||||
equalizerSwitch.isChecked = EqualizerHelper.instance!!.equalizer.enabled
|
||||
TintHelper.setTintAuto(equalizerSwitch, ThemeStore.accentColor(this), false)
|
||||
equalizerSwitch.setBackgroundColor(ThemeStore.accentColor(this))
|
||||
val widgetColor = MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))
|
||||
equalizerSwitch.setTextColor(widgetColor)
|
||||
TintHelper.setTintAuto(equalizerSwitch, widgetColor, false)
|
||||
equalizerSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
when (buttonView.id) {
|
||||
R.id.equalizerSwitch -> {
|
||||
|
@ -85,17 +78,17 @@ class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedL
|
|||
}
|
||||
}
|
||||
|
||||
mPresetsNamesAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1)
|
||||
presets.adapter = mPresetsNamesAdapter
|
||||
presetsNamesAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1)
|
||||
presets.adapter = presetsNamesAdapter
|
||||
presets.onItemSelectedListener = this
|
||||
|
||||
bassBoostStrength.progress = EqualizerHelper.instance!!.bassBoostStrength
|
||||
ViewUtil.setProgressDrawable(bassBoostStrength, ThemeStore.accentColor(this))
|
||||
bassBoostStrength.setOnSeekBarChangeListener(mSeekBarChangeListener)
|
||||
bassBoostStrength.setOnSeekBarChangeListener(seekBarChangeListener)
|
||||
|
||||
virtualizerStrength.progress = EqualizerHelper.instance!!.virtualizerStrength
|
||||
ViewUtil.setProgressDrawable(virtualizerStrength, ThemeStore.accentColor(this))
|
||||
virtualizerStrength.setOnSeekBarChangeListener(mSeekBarChangeListener)
|
||||
virtualizerStrength.setOnSeekBarChangeListener(seekBarChangeListener)
|
||||
|
||||
setupUI()
|
||||
addPresets()
|
||||
|
@ -124,12 +117,12 @@ class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedL
|
|||
}
|
||||
|
||||
private fun addPresets() {
|
||||
mPresetsNamesAdapter!!.clear()
|
||||
mPresetsNamesAdapter!!.add("Custom")
|
||||
presetsNamesAdapter!!.clear()
|
||||
presetsNamesAdapter!!.add("Custom")
|
||||
for (j in 0 until EqualizerHelper.instance!!.equalizer.numberOfPresets) {
|
||||
mPresetsNamesAdapter!!
|
||||
presetsNamesAdapter!!
|
||||
.add(EqualizerHelper.instance!!.equalizer.getPresetName(j.toShort()))
|
||||
mPresetsNamesAdapter!!.notifyDataSetChanged()
|
||||
presetsNamesAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
presets.setSelection(EqualizerHelper.instance!!.equalizer.currentPreset.toInt() + 1)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import code.name.monkey.retromusic.App
|
|||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcView
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity
|
||||
|
@ -33,8 +35,6 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
|||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.input.input
|
||||
import com.lauzy.freedom.library.LrcHelper
|
||||
import com.lauzy.freedom.library.LrcView
|
||||
import kotlinx.android.synthetic.main.activity_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_synced.*
|
||||
|
|
|
@ -41,7 +41,6 @@ class HomeAdapter(private val activity: AppCompatActivity, private var homes: Li
|
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
|
||||
return when (viewType) {
|
||||
SUGGESTIONS -> SuggestionViewHolder(LayoutInflater.from(activity).inflate(R.layout.section_item_collage, parent, false))
|
||||
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
||||
GENRES -> GenreViewHolder(layout)
|
||||
PLAYLISTS -> PlaylistViewHolder(layout)
|
||||
|
@ -54,10 +53,7 @@ class HomeAdapter(private val activity: AppCompatActivity, private var homes: Li
|
|||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val home = homes[position]
|
||||
when (getItemViewType(position)) {
|
||||
SUGGESTIONS -> {
|
||||
val viewHolder = holder as SuggestionViewHolder
|
||||
viewHolder.bindView(home)
|
||||
}
|
||||
|
||||
RECENT_ALBUMS, TOP_ALBUMS -> {
|
||||
val viewHolder = holder as AlbumViewHolder
|
||||
viewHolder.bindView(home)
|
||||
|
@ -88,17 +84,16 @@ class HomeAdapter(private val activity: AppCompatActivity, private var homes: Li
|
|||
|
||||
companion object {
|
||||
|
||||
@IntDef(SUGGESTIONS, RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, GENRES, PLAYLISTS)
|
||||
@IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, GENRES, PLAYLISTS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class HomeSection
|
||||
|
||||
const val SUGGESTIONS = 0
|
||||
const val RECENT_ALBUMS = 1
|
||||
const val TOP_ALBUMS = 2
|
||||
const val RECENT_ARTISTS = 3
|
||||
const val TOP_ARTISTS = 4
|
||||
const val GENRES = 5
|
||||
const val PLAYLISTS = 6
|
||||
const val RECENT_ALBUMS = 0
|
||||
const val TOP_ALBUMS = 1
|
||||
const val RECENT_ARTISTS = 2
|
||||
const val TOP_ARTISTS = 3
|
||||
const val GENRES = 4
|
||||
const val PLAYLISTS = 5
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.util.color;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import code.name.monkey.retromusic.util.ImageUtil;
|
||||
|
||||
/**
|
||||
* Helper class to process legacy (Holo) notifications to make them look like quantum
|
||||
* notifications.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NotificationColorUtil {
|
||||
|
||||
private static final String TAG = "NotificationColorUtil";
|
||||
private static final Object sLock = new Object();
|
||||
private static NotificationColorUtil sInstance;
|
||||
|
||||
private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
|
||||
new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
|
||||
|
||||
public static NotificationColorUtil getInstance() {
|
||||
synchronized (sLock) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new NotificationColorUtil();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect gray".
|
||||
*
|
||||
* @param bitmap The bitmap to test.
|
||||
* @return Whether the bitmap is grayscale.
|
||||
*/
|
||||
public boolean isGrayscale(Bitmap bitmap) {
|
||||
synchronized (sLock) {
|
||||
Pair<Boolean, Integer> cached = mGrayscaleBitmapCache.get(bitmap);
|
||||
if (cached != null) {
|
||||
if (cached.second == bitmap.getGenerationId()) {
|
||||
return cached.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean result;
|
||||
int generationId;
|
||||
|
||||
result = ImageUtil.isGrayscale(bitmap);
|
||||
// generationId and the check whether the Bitmap is grayscale can't be read atomically
|
||||
// here. However, since the thread is in the process of posting the notification, we can
|
||||
// assume that it doesn't modify the bitmap while we are checking the pixels.
|
||||
generationId = bitmap.getGenerationId();
|
||||
|
||||
synchronized (sLock) {
|
||||
mGrayscaleBitmapCache.put(bitmap, Pair.create(result, generationId));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect gray".
|
||||
*
|
||||
* @param d The drawable to test.
|
||||
* @return Whether the drawable is grayscale.
|
||||
*/
|
||||
public boolean isGrayscale(Drawable d) {
|
||||
if (d == null) {
|
||||
return false;
|
||||
} else if (d instanceof BitmapDrawable) {
|
||||
BitmapDrawable bd = (BitmapDrawable) d;
|
||||
return bd.getBitmap() != null && isGrayscale(bd.getBitmap());
|
||||
} else if (d instanceof AnimationDrawable) {
|
||||
AnimationDrawable ad = (AnimationDrawable) d;
|
||||
int count = ad.getNumberOfFrames();
|
||||
return count > 0 && isGrayscale(ad.getFrame(0));
|
||||
} else if (d instanceof VectorDrawable) {
|
||||
// We just assume you're doing the right thing if using vectors
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close to a
|
||||
* perfect gray".
|
||||
*
|
||||
* @param context The context to load the drawable from.
|
||||
* @return Whether the drawable is grayscale.
|
||||
*/
|
||||
public boolean isGrayscale(Context context, int drawableResId) {
|
||||
if (drawableResId != 0) {
|
||||
try {
|
||||
return isGrayscale(context.getDrawable(drawableResId));
|
||||
} catch (Resources.NotFoundException ex) {
|
||||
Log.e(TAG, "Drawable not found: " + drawableResId);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts all the grayscale colors set by {@link android.text.style.TextAppearanceSpan}s on the
|
||||
* text.
|
||||
*
|
||||
* @param charSequence The text to process.
|
||||
* @return The color inverted text.
|
||||
*/
|
||||
|
||||
|
||||
private int processColor(int color) {
|
||||
return Color.argb(Color.alpha(color),
|
||||
255 - Color.red(color),
|
||||
255 - Color.green(color),
|
||||
255 - Color.blue(color));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue