diff --git a/app/build.gradle b/app/build.gradle
index 11c7b2de..f3ef42da 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -32,7 +32,7 @@ android {
vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic"
- versionCode 308
+ versionCode 311
versionName '3.1.300'
multiDexEnabled true
@@ -164,6 +164,7 @@ dependencies {
implementation 'com.github.takahirom.downloadable.calligraphy:downloadable-calligraphy:0.1.3'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
implementation project(':appthemehelper')
+ implementation project(':library')
}
repositories {
mavenCentral()
diff --git a/app/src/main/assets/retro-changelog.html b/app/src/main/assets/retro-changelog.html
index d9967f9d..ce928b00 100644
--- a/app/src/main/assets/retro-changelog.html
+++ b/app/src/main/assets/retro-changelog.html
@@ -1 +1 @@
-
v3.1.300
- Improved home sections loading
- Removed library options which are duplicated (it's available from profile menu)
- Replaced collapsing Fab with Android Floating Extended Fab
- Replaced home with for you
- Fixed profile image not loading in about
- Improved selecting user profile image
- Added bio to enter custom message
- Improved some UI screens
v3.1.240
- Fix Search not showing from home screen
- Fix Volume controls color issue
- Fix Seek bar alignment
- Added tiny theme
- Improved full theme appearances
- Now playing theme preview updated
- Fix composer error
- Bottom Options improved(internal)
v3.1.200
- Added composer sort and editing
- Fix Crash in Album tag editor while selecting options
- Added Filter song length
- Added Favourites playlist icon will be accent color
- Added Colorful settings icons
- Added Corners for dialog
v3.0.570
- Fix Album/Artist square image
- Fix Delete dialog text format
- Fix Profile picture not showing after coming back from folders
- Fix Play button color i Simple and Plain themes
- Fix Sleep timer dialog crashing
- Fix Share song dialog title and text
If you see entire app white or dark or black select same theme in settings to fix
FAQ's
*If you face any UI related issues you clear app data and cache, if its
not working try to
uninstall and install again.
\ No newline at end of file
+
v3.1.300
- Fix rename playlist text color
- Fix same album showing in details page
- Fix lyrics text alignment on sync and lyrics reading improved
- Improved home sections loading
- Removed library options which are duplicated (it's available from profile menu)
- Replaced collapsing Fab with Android Floating Extended Fab
- Replaced home with for you
- Fixed profile image not loading in about
- Improved selecting user profile image
- Added bio to enter custom message
- Improved some UI screens
v3.1.240
- Fix Search not showing from home screen
- Fix Volume controls color issue
- Fix Seek bar alignment
- Added tiny theme
- Improved full theme appearances
- Now playing theme preview updated
- Fix composer error
- Bottom Options improved(internal)
v3.1.200
- Added composer sort and editing
- Fix Crash in Album tag editor while selecting options
- Added Filter song length
- Added Favourites playlist icon will be accent color
- Added Colorful settings icons
- Added Corners for dialog
v3.0.570
- Fix Album/Artist square image
- Fix Delete dialog text format
- Fix Profile picture not showing after coming back from folders
- Fix Play button color i Simple and Plain themes
- Fix Sleep timer dialog crashing
- Fix Share song dialog title and text
If you see entire app white or dark or black select same theme in settings to fix
FAQ's
*If you face any UI related issues you clear app data and cache, if its
not working try to
uninstall and install again.
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt
index 9b59f661..a166cda8 100644
--- a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreatePlaylistDialog.kt
@@ -14,7 +14,6 @@
package code.name.monkey.retromusic.dialogs
-import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.view.LayoutInflater
@@ -22,28 +21,26 @@ import android.view.View
import android.view.ViewGroup
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.MaterialUtil
-
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PlaylistsUtil
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment
import kotlinx.android.synthetic.main.dialog_playlist.*
-import java.util.*
class CreatePlaylistDialog : RoundedBottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-
return inflater.inflate(R.layout.dialog_playlist, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- val accentColor = ThemeStore.accentColor(Objects.requireNonNull(context))
- val songs = arguments!!.getParcelableArrayList("songs")
+ bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
+
+ val accentColor = ThemeStore.accentColor(context!!)
MaterialUtil.setTint(actionCreate, true)
MaterialUtil.setTint(actionCancel, false)
@@ -51,17 +48,17 @@ class CreatePlaylistDialog : RoundedBottomSheetDialogFragment() {
actionNewPlaylist.setHintTextColor(ColorStateList.valueOf(accentColor))
actionNewPlaylist.setTextColor(ThemeStore.textColorPrimary(context!!))
- bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
+ val songs = arguments!!.getParcelableArrayList("songs")
+
actionCancel.setOnClickListener { dismiss() }
actionCreate.setOnClickListener {
if (activity == null) {
return@setOnClickListener
}
if (!actionNewPlaylist!!.text!!.toString().trim { it <= ' ' }.isEmpty()) {
- val playlistId = PlaylistsUtil
- .createPlaylist(activity!!, actionNewPlaylist!!.text!!.toString())
+ val playlistId = PlaylistsUtil.createPlaylist(activity!!, actionNewPlaylist!!.text!!.toString())
if (playlistId != -1 && activity != null) {
if (songs != null) {
PlaylistsUtil.addToPlaylist(activity!!, songs, playlistId, true)
diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt
index 3a7c87b0..e6864d5c 100644
--- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeletePlaylistDialog.kt
@@ -48,8 +48,8 @@ class DeletePlaylistDialog : RoundedBottomSheetDialogFragment() {
} else {
Html.fromHtml(getString(R.string.delete_playlist_x, playlists[0].name))
}
- dialogTitle.text = content
- dialogTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
+ bannerTitle.text = content
+ bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
actionDelete.apply {
setText(R.string.action_delete)
diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt
index 4cd0a45b..a424b674 100644
--- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.kt
@@ -34,7 +34,7 @@ class DeleteSongsDialog : RoundedBottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- dialogTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
+ bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
//noinspection unchecked,ConstantConditions
val songs = arguments!!.getParcelableArrayList("songs")
val content: CharSequence
@@ -44,7 +44,7 @@ class DeleteSongsDialog : RoundedBottomSheetDialogFragment() {
} else {
getString(R.string.delete_song_x, songs[0].title)
}
- dialogTitle.text = Html.fromHtml(content)
+ bannerTitle.text = Html.fromHtml(content)
}
actionDelete.apply {
setOnClickListener {
diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt
index 6bef42fc..2a6e87bc 100644
--- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveFromPlaylistDialog.kt
@@ -27,35 +27,33 @@ import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PlaylistsUtil
import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment
-import kotlinx.android.synthetic.main.dialog_remove_from_playlist.*
+import kotlinx.android.synthetic.main.dialog_delete.*
+
class RemoveFromPlaylistDialog : RoundedBottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.dialog_remove_from_playlist, container, false)
+ return inflater.inflate(R.layout.dialog_delete, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
-
val songs = arguments!!.getParcelableArrayList("songs")
- val title: Int
val content: CharSequence
if (songs!!.size > 1) {
- title = R.string.remove_songs_from_playlist_title
content = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size), Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(getString(R.string.remove_x_songs_from_playlist, songs.size))
}
} else {
- title = R.string.remove_song_from_playlist_title
content = Html.fromHtml(getString(R.string.remove_song_x_from_playlist, songs[0].title))
}
bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
bannerTitle.text = content;
actionDelete.apply {
- setText(title)
+ setIconResource(R.drawable.ic_delete_white_24dp)
+ setText(R.string.remove_action)
setTextColor(ThemeStore.textColorSecondary(context))
setOnClickListener {
val playlistSongs = ArrayList()
@@ -68,6 +66,7 @@ class RemoveFromPlaylistDialog : RoundedBottomSheetDialogFragment() {
actionCancel.apply {
+ setIconResource(R.drawable.ic_close_white_24dp)
setTextColor(ThemeStore.textColorSecondary(context))
setOnClickListener { dismiss() }
MaterialUtil.setTint(this, false)
diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt
index 741a7345..50edcb02 100644
--- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RenamePlaylistDialog.kt
@@ -36,10 +36,14 @@ class RenamePlaylistDialog : RoundedBottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- val accentColor = ThemeStore.accentColor(context!!)
+ bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
+ bannerTitle.setText(R.string.rename_playlist_title)
MaterialUtil.setTint(actionNewPlaylistContainer, false)
+ val accentColor = ThemeStore.accentColor(context!!)
+ actionNewPlaylist.setHintTextColor(ColorStateList.valueOf(accentColor))
+ actionNewPlaylist.setTextColor(ThemeStore.textColorPrimary(context!!))
actionNewPlaylist.apply {
var playlistId: Long = 0
@@ -47,14 +51,10 @@ class RenamePlaylistDialog : RoundedBottomSheetDialogFragment() {
playlistId = arguments!!.getLong("playlist_id")
}
setText(PlaylistsUtil.getNameForPlaylist(activity!!, playlistId))
- setHintTextColor(ColorStateList.valueOf(accentColor))
- setTextColor(ThemeStore.textColorPrimary(context!!))
}
- bannerTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
- bannerTitle.setText(R.string.rename_playlist_title)
actionCancel.apply {
- MaterialUtil.setTint(actionCancel, false)
+ MaterialUtil.setTint(this, false)
setOnClickListener { dismiss() }
icon = ContextCompat.getDrawable(context, R.drawable.ic_close_white_24dp)
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/exfab/FabIconAnimator.java b/app/src/main/java/code/name/monkey/retromusic/exfab/FabIconAnimator.java
deleted file mode 100644
index e3384af0..00000000
--- a/app/src/main/java/code/name/monkey/retromusic/exfab/FabIconAnimator.java
+++ /dev/null
@@ -1,143 +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.exfab;
-
-/**
- * Created by hemanths on 3/20/19
- */
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.transition.AutoTransition;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.view.View;
-
-import com.google.android.material.button.MaterialButton;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.constraintlayout.widget.ConstraintSet;
-import code.name.monkey.retromusic.R;
-
-public class FabIconAnimator {
-
- private static final String ROTATION_Y_PROPERTY = "rotationY";
-
- private static final float TWITCH_END = 20F;
- private static final float TWITCH_START = 0F;
- private static final int DURATION = 200;
- private final MaterialButton button;
- private final ConstraintLayout container;
- @DrawableRes
- private int currentIcon;
- @StringRes
- private int currentText;
- private boolean isAnimating;
- private final Transition.TransitionListener listener = new Transition.TransitionListener() {
- public void onTransitionStart(Transition transition) {
- isAnimating = true;
- }
-
- public void onTransitionEnd(Transition transition) {
- isAnimating = false;
- }
-
- public void onTransitionCancel(Transition transition) {
- isAnimating = false;
- }
-
- public void onTransitionPause(Transition transition) {
- }
-
- public void onTransitionResume(Transition transition) {
- }
- };
-
- public FabIconAnimator(ConstraintLayout container) {
- this.container = container;
- this.button = container.findViewById(R.id.fab);
- }
-
- public void update(@DrawableRes int icon, @StringRes int text) {
- boolean isSame = currentIcon == icon && currentText == text;
- currentIcon = icon;
- currentText = text;
- animateChange(icon, text, isSame);
- }
-
- public void setOnClickListener(@Nullable View.OnClickListener clickListener) {
- if (clickListener == null) {
- button.setOnClickListener(null);
- return;
- }
- AtomicBoolean flag = new AtomicBoolean(true);
- button.setOnClickListener(view -> {
- if (!flag.getAndSet(false)) return;
- clickListener.onClick(view);
- button.postDelayed(() -> flag.set(true), 2000);
- });
- }
-
- private boolean isExtended() { // R.dimen.triple_and_half_margin is 56 dp.
- return button.getLayoutParams().height != button.getResources().getDimensionPixelSize(R.dimen.triple_and_half_margin);
- }
-
- public void setExtended(boolean extended) {
- setExtended(extended, false);
- }
-
- private void animateChange(@DrawableRes int icon, @StringRes int text, boolean isSame) {
- boolean extended = isExtended();
- button.setText(text);
- button.setIconResource(icon);
- setExtended(extended, !isSame);
- if (!extended) twitch();
- }
-
- private void setExtended(boolean extended, boolean force) {
- if (isAnimating || (extended && isExtended() && !force)) return;
-
- ConstraintSet set = new ConstraintSet();
- set.clone(container.getContext(), extended ? R.layout.fab_extended : R.layout.fab_collapsed);
-
- TransitionManager.beginDelayedTransition(container, new AutoTransition()
- .addListener(listener).setDuration(150));
-
- if (extended) button.setText(currentText);
- else button.setText("");
-
- set.applyTo(container);
- }
-
- private void twitch() {
- AnimatorSet set = new AnimatorSet();
- ObjectAnimator twitchA = animateProperty(ROTATION_Y_PROPERTY, TWITCH_START, TWITCH_END);
- ObjectAnimator twitchB = animateProperty(ROTATION_Y_PROPERTY, TWITCH_END, TWITCH_START);
-
- set.play(twitchB).after(twitchA);
- set.start();
- }
-
- @NonNull
- private ObjectAnimator animateProperty(String property, float start, float end) {
- return ObjectAnimator.ofFloat(container, property, start, end).setDuration(DURATION);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/exfab/TransientBarBehavior.java b/app/src/main/java/code/name/monkey/retromusic/exfab/TransientBarBehavior.java
deleted file mode 100644
index 132ed5de..00000000
--- a/app/src/main/java/code/name/monkey/retromusic/exfab/TransientBarBehavior.java
+++ /dev/null
@@ -1,75 +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.exfab;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.google.android.material.snackbar.Snackbar;
-
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.core.view.ViewCompat;
-import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
-
-public class TransientBarBehavior extends CoordinatorLayout.Behavior {
-
- private static final Interpolator fastOutSlowInInterpolator = new FastOutSlowInInterpolator();
-
- public TransientBarBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
- return dependency instanceof Snackbar.SnackbarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
- if (child.getVisibility() == View.VISIBLE) {
- float translationY = this.getViewTranslationYForSnackbar(parent, child);
- child.setTranslationY(translationY);
- }
- return true;
- }
-
- @Override
- public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
- if (dependency instanceof Snackbar.SnackbarLayout && child.getTranslationY() != 0.0F) {
- ViewCompat.animate(child).translationY(0.0F).scaleX(1.0F).scaleY(1.0F).alpha(1.0F)
- .setInterpolator(fastOutSlowInInterpolator);
- }
- }
-
- private float getViewTranslationYForSnackbar(CoordinatorLayout parent, View child) {
- float minOffset = 0.0F;
- List dependencies = parent.getDependencies(child);
- int i = 0;
-
- for (int z = dependencies.size(); i < z; ++i) {
- View view = (View) dependencies.get(i);
- if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) {
- minOffset = Math.min(minOffset, view.getTranslationY() - (float) view.getHeight());
- }
- }
-
- return minOffset;
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt
index fa8edde0..07a63a4c 100644
--- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.kt
@@ -219,19 +219,20 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContrac
})
return@map it.albums!!
}
-
+ .map { it.filter { albumSearch -> albumSearch.id != album.id } }
.subscribe {
-
- it.remove(album)
- if (!it.isEmpty()) {
- moreTitle.visibility = View.VISIBLE
- moreRecyclerView.visibility = View.VISIBLE
- } else {
+ for (albumFinal in it) {
+ if (albumFinal.id == album.id)
+ println("$albumFinal -> $album")
+ }
+ if (it.isEmpty()) {
return@subscribe
}
+ moreTitle.visibility = View.VISIBLE
+ moreRecyclerView.visibility = View.VISIBLE
moreTitle.text = String.format("More from %s", album.artistName)
- val albumAdapter = HorizontalAlbumAdapter(this, it, false, null)
+ val albumAdapter = HorizontalAlbumAdapter(this, it as ArrayList, false, null)
moreRecyclerView.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)
moreRecyclerView.adapter = albumAdapter
diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.kt
index a95fcbf2..b06a407f 100644
--- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.kt
@@ -1,6 +1,7 @@
package code.name.monkey.retromusic.ui.activities
import android.annotation.SuppressLint
+import android.content.res.ColorStateList
import android.os.AsyncTask
import android.os.Bundle
import android.text.InputType
@@ -12,6 +13,8 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
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.retromusic.App
import code.name.monkey.retromusic.R
@@ -28,6 +31,8 @@ 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.*
@@ -37,15 +42,22 @@ import java.util.*
class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
-
+ when (state) {
+ ViewPager.SCROLL_STATE_IDLE ->
+ fab.show(true)
+ ViewPager.SCROLL_STATE_DRAGGING,
+ ViewPager.SCROLL_STATE_SETTLING ->
+ fab.hide(true)
+ }
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
-
}
override fun onPageSelected(position: Int) {
PreferenceUtil.getInstance().lyricsOptions = position
+ if (position == 0) fab.text = "Sync lyrics"
+ else if (position == 1) fab.text = "Lyrics"
}
override fun onClick(v: View?) {
@@ -83,7 +95,11 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
}
- TintHelper.setTintAuto(fab, ThemeStore.accentColor(this), true)
+ fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
+ ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
+ fab.setTextColor(this)
+ fab.iconTint = this
+ }
setupWakelock()
viewPager.apply {
@@ -258,8 +274,8 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
offlineLyrics?.setText(R.string.no_lyrics_found)
return
}
- (activity as LyricsActivity).lyricsString = l.data
- offlineLyrics?.text = l.data
+ (activity as LyricsActivity).lyricsString = l.text
+ offlineLyrics?.text = l.text
}
override fun onCancelled(s: Lyrics?) {
@@ -302,11 +318,14 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
private fun setupLyricsView() {
lyricsView.apply {
- setOnPlayerClickListener { progress, _ -> MusicPlayerRemote.seekTo(progress.toInt()) }
- setDefaultColor(ContextCompat.getColor(context, R.color.md_grey_400))
- setHintColor(ThemeStore.textColorPrimary(context))
- setHighLightColor(ThemeStore.textColorPrimary(context))
- setTextSize(RetroUtil.convertDpToPixel(18f, context).toInt())
+ setCurrentPlayLineColor(ThemeStore.accentColor(context))
+ setIndicatorTextColor(ThemeStore.accentColor(context))
+ setCurrentIndicateLineTextColor(ThemeStore.textColorPrimary(context))
+ setOnPlayIndicatorLineListener(object : LrcView.OnPlayIndicatorLineListener {
+ override fun onPlay(time: Long, content: String) {
+ MusicPlayerRemote.seekTo(time.toInt())
+ }
+ })
}
}
@@ -321,7 +340,7 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
- lyricsView.setCurrentTimeMillis(progress.toLong())
+ lyricsView.updateTime(progress.toLong())
}
private fun loadLRCLyrics() {
@@ -332,10 +351,8 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
}
private fun showLyricsLocal(file: File?) {
- if (file == null) {
- lyricsView.reset()
- } else {
- lyricsView.setLyricFile(file, "UTF-8")
+ if (file != null) {
+ lyricsView.setLrcData(LrcHelper.parseLrcFromFile(file))
}
}
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/UserInfoActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/UserInfoActivity.kt
index 5132d8ae..359ca05c 100644
--- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/UserInfoActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/UserInfoActivity.kt
@@ -107,7 +107,7 @@ class UserInfoActivity : AbsBaseActivity() {
toolbar.apply {
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
setBackgroundColor(primaryColor)
- ToolbarContentTintHelper.colorBackButton(this, ThemeStore.accentColor(this@UserInfoActivity))
+ ToolbarContentTintHelper.colorBackButton(this, ThemeStore.textColorSecondary(this@UserInfoActivity))
setSupportActionBar(this)
}
appBarLayout.setBackgroundColor(primaryColor)
diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.kt
index 81627036..4fbf4296 100755
--- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/SongTagEditorActivity.kt
@@ -44,7 +44,7 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
private fun setUpViews() {
fillViewsWithFileTags()
- MaterialUtil.setTint(songTextContainer)
+ MaterialUtil.setTint(songTextContainer,false)
MaterialUtil.setTint(composerContainer, false)
MaterialUtil.setTint(albumTextContainer, false)
MaterialUtil.setTint(artistContainer, false)
diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.kt
index 7197a39f..c01186de 100644
--- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/normal/PlayerFragment.kt
@@ -106,14 +106,14 @@ class PlayerFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks {
snowfall.visibility = if (PreferenceUtil.getInstance().isSnowFall) View.VISIBLE else View.GONE
- val display = activity?.windowManager?.defaultDisplay
- val outMetrics = DisplayMetrics()
- display?.getMetrics(outMetrics)
+ //val display = activity?.windowManager?.defaultDisplay
+ //val outMetrics = DisplayMetrics()
+ //display?.getMetrics(outMetrics)
- val density = resources.displayMetrics.density
- val dpWidth = outMetrics.widthPixels / density
+ //val density = resources.displayMetrics.density
+ //val dpWidth = outMetrics.widthPixels / density
- playerAlbumCoverContainer?.layoutParams?.height = RetroUtil.convertDpToPixel((dpWidth - getCutOff()), context!!).toInt()
+ //playerAlbumCoverContainer?.layoutParams?.height = RetroUtil.convertDpToPixel((dpWidth - getCutOff()), context!!).toInt()
}
diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java
index 20b4c1cd..00ab4dc1 100644
--- a/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java
+++ b/app/src/main/java/code/name/monkey/retromusic/util/PlaylistsUtil.java
@@ -121,9 +121,7 @@ public class PlaylistsUtil {
public static void addToPlaylist(@NonNull final Context context, @NonNull final List songs, final int playlistId, final boolean showToastOnFinish) {
final int size = songs.size();
final ContentResolver resolver = context.getContentResolver();
- final String[] projection = new String[]{
- "max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")",
- };
+ final String[] projection = new String[]{"max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")",};
final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
Cursor cursor = null;
int base = 0;
diff --git a/app/src/main/java/code/name/monkey/retromusic/views/LyricView.java b/app/src/main/java/code/name/monkey/retromusic/views/LyricView.java
deleted file mode 100644
index e83847ab..00000000
--- a/app/src/main/java/code/name/monkey/retromusic/views/LyricView.java
+++ /dev/null
@@ -1,890 +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.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-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.view.animation.LinearInterpolator;
-
-import androidx.annotation.IntDef;
-import androidx.core.content.res.ResourcesCompat;
-
-import org.mozilla.universalchardet.UniversalDetector;
-
-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.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import code.name.monkey.retromusic.R;
-
-/**
- * Created by zhengken.me on 2016/11/27.
- * ClassName : LyricView
- * Description :
- */
-public class LyricView extends View {
-
- public static final int LEFT = 0;
- public static final int CENTER = 1;
- public static final int RIGHT = 2;
- private static final String TAG = "LyricView";
- private static final float SLIDE_COEFFICIENT = 0.2f;
-
- private static final int UNITS_SECOND = 1000;
- private static final int UNITS_MILLISECOND = 1;
-
- private static final int FLING_ANIMATOR_DURATION = 500 * UNITS_MILLISECOND;
-
- private static final int THRESHOLD_Y_VELOCITY = 1600;
-
- private static final int INDICATOR_ICON_PLAY_MARGIN_LEFT = 7;//dp
- private static final int INDICATOR_ICON_PLAY_WIDTH = 15;//sp
- private static final int INDICATOR_LINE_MARGIN = 10;//dp
- private static final int INDICATOR_TIME_TEXT_SIZE = 10;//sp
- private static final int INDICATOR_TIME_MARGIN_RIGHT = 7;//dp
-
- private static final int DEFAULT_TEXT_SIZE = 16;//sp
- private static final int DEFAULT_MAX_LENGTH = 300;//dp
- private static final int DEFAULT_LINE_SPACE = 25;//dp
-
- private int mHintColor;
- private int mDefaultColor;
- private int mHighLightColor;
- private int mTextAlign;
-
-
- private int mLineCount;
- private int mTextSize;
- private float mLineHeight;
- private LyricInfo mLyricInfo;
- private String mDefaultHint;
- private int mMaxLength;
-
- private TextPaint mTextPaint;
- private Paint mBtnPlayPaint;
- private Paint mLinePaint;
- private Paint mTimerPaint;
-
- private boolean mFling = false;
- private ValueAnimator mFlingAnimator;
- private float mScrollY = 0;
- private float mLineSpace = 0;
- private boolean mIsShade;
- private float mShaderWidth = 0;
- private int mCurrentPlayLine = 0;
- private boolean mShowIndicator;
-
- private VelocityTracker mVelocityTracker;
- private float mVelocity = 0;
- private float mDownX;
- private float mDownY;
- private float mLastScrollY;
- private boolean mUserTouch = false;
- Runnable hideIndicator = () -> {
- setUserTouch(false);
- invalidateView();
- };
- private int maxVelocity;
- private int mLineNumberUnderIndicator = 0;
- private Rect mBtnPlayRect = new Rect();
- private Rect mTimerRect;
- private String mDefaultTime = "00:00";
- private int mLineColor = Color.parseColor("#EFEFEF");
- private int mBtnColor = Color.parseColor("#EFEFEF");
- private List mLineFeedRecord = new ArrayList<>();
- private boolean mEnableLineFeed = false;
- private int mExtraHeight = 0;
- private int mTextHeight;
- private String mCurrentLyricFilePath = null;
- private OnPlayerClickListener mClickListener;
-
- public LyricView(Context context) {
- super(context);
- initMyView(context);
- }
-
- public LyricView(Context context, AttributeSet attributeSet) {
- super(context, attributeSet);
- getAttrs(context, attributeSet);
- initMyView(context);
-
- }
-
- public LyricView(Context context, AttributeSet attributeSet, int i) {
- super(context, attributeSet, i);
- getAttrs(context, attributeSet);
- initMyView(context);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- return super.dispatchTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- switch (event.getAction()) {
- case MotionEvent.ACTION_CANCEL:
- actionCancel(event);
- break;
- case MotionEvent.ACTION_DOWN:
- actionDown(event);
- break;
- case MotionEvent.ACTION_MOVE:
- actionMove(event);
- break;
- case MotionEvent.ACTION_UP:
- actionUp(event);
- break;
- default:
- break;
- }
- invalidateView();
- return true;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mBtnPlayRect.set((int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_ICON_PLAY_MARGIN_LEFT),
- (int) (getHeight() * 0.5f - getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) * 0.5f),
- (int) (getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) + getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_ICON_PLAY_MARGIN_LEFT)),
- (int) (getHeight() * 0.5f + getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_ICON_PLAY_WIDTH) * 0.5f));
- mShaderWidth = getWidth() * 0.4f;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (scrollable()) {
- if (mShowIndicator) {
- drawIndicator(canvas);
- }
-
- for (int i = 0; i < mLineCount; i++) {
- float x = 0;
- switch (mTextAlign) {
- case LEFT:
- x = INDICATOR_ICON_PLAY_MARGIN_LEFT + INDICATOR_LINE_MARGIN + mBtnPlayRect.width();
- break;
- case CENTER:
- x = getWidth() * 0.5f;
- break;
- case RIGHT:
- x = getWidth() - INDICATOR_LINE_MARGIN * 2 - mTimerRect.width() - INDICATOR_ICON_PLAY_MARGIN_LEFT;
- break;
- }
-
- float y;
- if (mEnableLineFeed && i > 0) {
- y = getMeasuredHeight() * 0.5f + i * mLineHeight - mScrollY + mLineFeedRecord.get(i - 1);
- } else {
- y = getMeasuredHeight() * 0.5f + i * mLineHeight - mScrollY;
- }
-
-// float y = getHeight() * 0.5f + i * mLineHeight - mScrollY;
- if (y < 0) {
- continue;
- }
- if (y > getHeight()) {
- break;
- }
- if (i == mCurrentPlayLine - 1) {
- mTextPaint.setColor(mHighLightColor);
- } else if (i == mLineNumberUnderIndicator && mShowIndicator) {
- mTextPaint.setColor(Color.LTGRAY);
- } else {
- mTextPaint.setColor(mDefaultColor);
- }
- if (mIsShade && (y > getHeight() - mShaderWidth || y < mShaderWidth)) {
- if (y < mShaderWidth) {
- mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
- } else {
- mTextPaint.setAlpha(26 + (int) (23000.0f * (getHeight() - y) / mShaderWidth * 0.01f));
- }
- } else {
- mTextPaint.setAlpha(255);
- }
-
- if (mEnableLineFeed) {
- StaticLayout staticLayout = new StaticLayout(mLyricInfo.songLines.get(i).content, mTextPaint,
- mMaxLength,
- Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
- canvas.save();
- canvas.translate(x, y);
- staticLayout.draw(canvas);
- canvas.restore();
- } else {
- canvas.drawText(mLyricInfo.songLines.get(i).content, x, y, mTextPaint);
- }
- }
- } else {
- mTextPaint.setColor(mHintColor);
- canvas.drawText(mDefaultHint, getMeasuredWidth() / 2, getMeasuredHeight() / 2, mTextPaint);
- }
- }
-
- private void getAttrs(Context context, AttributeSet attrs) {
- TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LyricView);
- mIsShade = ta.getBoolean(R.styleable.LyricView_fadeInFadeOut, false);
- mDefaultHint = ta.getString(R.styleable.LyricView_hint) != null
- ? ta.getString(R.styleable.LyricView_hint)
- : getResources().getString(R.string.default_hint);
- mHintColor = ta.getColor(R.styleable.LyricView_hintColor, Color.parseColor("#FFFFFF"));
- mDefaultColor = ta.getColor(R.styleable.LyricView_textColor, Color.parseColor("#8D8D8D"));
- mHighLightColor = ta.getColor(R.styleable.LyricView_highlightColor, Color.parseColor("#FFFFFF"));
- mTextSize = ta.getDimensionPixelSize(R.styleable.LyricView_textSize, (int) getRawSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE));
- mTextAlign = ta.getInt(R.styleable.LyricView_textAlign, CENTER);
- mMaxLength = ta.getDimensionPixelSize(R.styleable.LyricView_maxLength, (int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_LENGTH));
- mLineSpace = ta.getDimensionPixelSize(R.styleable.LyricView_lineSpace, (int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_LINE_SPACE));
- ta.recycle();
- }
-
- public void setShade(boolean shade) {
- mIsShade = shade;
- invalidateView();
- }
-
- public void setHintColor(int hintColor) {
- mHintColor = hintColor;
- invalidateView();
- }
-
- public void setDefaultColor(int defaultColor) {
- mDefaultColor = defaultColor;
- invalidateView();
- }
-
- public void setHighLightColor(int highLightColor) {
- mHighLightColor = highLightColor;
- invalidateView();
- }
-
- public void setTextAlign(int textAlign) {
- mTextAlign = textAlign;
- invalidateView();
- }
-
- public void setLineCount(int lineCount) {
- mLineCount = lineCount;
- invalidateView();
- }
-
- public void setTextSize(int textSize) {
- mTextSize = textSize;
- invalidateView();
- }
-
- public void setDefaultHint(String defaultHint) {
- mDefaultHint = defaultHint;
- invalidateView();
- }
-
- public void setMaxLength(int maxLength) {
- mMaxLength = maxLength;
- invalidateView();
- }
-
- public void setOnPlayerClickListener(OnPlayerClickListener mClickListener) {
- this.mClickListener = mClickListener;
- }
-
- public void setAlignment(@Alignment int alignment) {
- mTextAlign = alignment;
- }
-
- public void setCurrentTimeMillis(long current) {
- scrollToCurrentTimeMillis(current);
- }
-
- public void setLyricFile(File file) {
-
- if (file == null || !file.exists()) {
- reset();
- mCurrentLyricFilePath = "";
- return;
- } else if (file.getPath().equals(mCurrentLyricFilePath)) {
- return;
- } else {
- mCurrentLyricFilePath = file.getPath();
- reset();
- }
- try {
-
- FileInputStream fis = new FileInputStream(file);
- byte[] buf = new byte[1024];
- UniversalDetector detector = new UniversalDetector(null);
- int nread;
- while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
- detector.handleData(buf, 0, nread);
- }
-
- detector.dataEnd();
- String encoding = detector.getDetectedCharset();
- if (encoding != null) {
- setLyricFile(file, encoding);
- } else {
- setLyricFile(file, "UTF-8");
- }
- detector.reset();
- fis.close();
-
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public void setLyricFile(File file, String charsetName) {
- if (file != null && file.exists()) {
- try {
- setupLyricResource(new FileInputStream(file), charsetName);
-
- for (int i = 0; i < mLyricInfo.songLines.size(); i++) {
-
- StaticLayout staticLayout = new StaticLayout(mLyricInfo.songLines.get(i).content, mTextPaint,
- (int) getRawSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_LENGTH),
- Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
-
- if (staticLayout.getLineCount() > 1) {
- mEnableLineFeed = true;
- mExtraHeight = mExtraHeight + (staticLayout.getLineCount() - 1) * mTextHeight;
- }
-
- mLineFeedRecord.add(i, mExtraHeight);
-
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- } else {
- invalidateView();
- }
- }
-
- private void setLineSpace(float lineSpace) {
- if (mLineSpace != lineSpace) {
- mLineSpace = getRawSize(TypedValue.COMPLEX_UNIT_DIP, lineSpace);
- measureLineHeight();
- mScrollY = measureCurrentScrollY(mCurrentPlayLine);
- invalidateView();
- }
- }
-
- public void reset() {
- resetView();
- }
-
- private void actionCancel(MotionEvent event) {
- releaseVelocityTracker();
- }
-
- private void actionDown(MotionEvent event) {
- removeCallbacks(hideIndicator);
- mLastScrollY = mScrollY;
- mDownX = event.getX();
- mDownY = event.getY();
- if (mFlingAnimator != null) {
- mFlingAnimator.cancel();
- mFlingAnimator = null;
- }
- setUserTouch(true);
- }
-
- private boolean overScrolled() {
-
- return scrollable() && (mScrollY > mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0) || mScrollY < 0);
- }
-
- private void actionMove(MotionEvent event) {
- if (scrollable()) {
- final VelocityTracker tracker = mVelocityTracker;
- tracker.computeCurrentVelocity(UNITS_SECOND, maxVelocity);
- mScrollY = mLastScrollY + mDownY - event.getY();
- mVelocity = tracker.getYVelocity();
- measureCurrentLine();
- }
- }
-
- private void actionUp(MotionEvent event) {
-
- postDelayed(hideIndicator, 3 * UNITS_SECOND);
-
- releaseVelocityTracker();
-
- if (scrollable()) {
- if (overScrolled() && mScrollY < 0) {
- smoothScrollTo(0);
- return;
- }
- if (overScrolled() && mScrollY > mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0)) {
- smoothScrollTo(mLineHeight * (mLineCount - 1) + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0));
- return;
- }
- if (Math.abs(mVelocity) > THRESHOLD_Y_VELOCITY) {
- doFlingAnimator(mVelocity);
- return;
- }
- if (mShowIndicator && clickPlayer(event)) {
- if (mLineNumberUnderIndicator != mCurrentPlayLine) {
- mShowIndicator = false;
- if (mClickListener != null) {
- setUserTouch(false);
- mClickListener.onPlayerClicked(mLyricInfo.songLines.get(mLineNumberUnderIndicator).start, mLyricInfo.songLines.get(mLineNumberUnderIndicator).content);
- }
- }
- }
- }
- }
-
- private String measureCurrentTime() {
- DecimalFormat format = new DecimalFormat("00");
- if (mLyricInfo != null && mLineCount > 0 && mLineNumberUnderIndicator - 1 < mLineCount && mLineNumberUnderIndicator > 0) {
- return format.format(mLyricInfo.songLines.get(mLineNumberUnderIndicator - 1).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(mLineNumberUnderIndicator - 1).start / 1000 % 60);
- }
- if (mLyricInfo != null && mLineCount > 0 && (mLineNumberUnderIndicator - 1) >= mLineCount) {
- return format.format(mLyricInfo.songLines.get(mLineCount - 1).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(mLineCount - 1).start / 1000 % 60);
- }
- if (mLyricInfo != null && mLineCount > 0 && mLineNumberUnderIndicator - 1 <= 0) {
- return format.format(mLyricInfo.songLines.get(0).start / 1000 / 60) + ":" + format.format(mLyricInfo.songLines.get(0).start / 1000 % 60);
- }
- return mDefaultTime;
- }
-
- private void drawIndicator(Canvas canvas) {
-
- //绘制 播放 按钮
- Path pathPlay = new Path();
- float yCoordinate = mBtnPlayRect.left + (float) Math.sqrt(Math.pow(mBtnPlayRect.width(), 2) - Math.pow(mBtnPlayRect.width() * 0.5f, 2));
- float remainWidth = mBtnPlayRect.right - yCoordinate;
-
- pathPlay.moveTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() - mBtnPlayRect.height() * 0.5f);
- pathPlay.lineTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() + mBtnPlayRect.height() * 0.5f);
- pathPlay.lineTo(yCoordinate, mBtnPlayRect.centerY());
- pathPlay.lineTo(mBtnPlayRect.centerX() - mBtnPlayRect.width() * 0.5f, mBtnPlayRect.centerY() - mBtnPlayRect.height() * 0.5f);
-
- canvas.drawPath(pathPlay, mBtnPlayPaint);
-
- //绘制 分割线
- Path pathLine = new Path();
- pathLine.moveTo(mBtnPlayRect.right + getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_LINE_MARGIN) - remainWidth, getMeasuredHeight() * 0.5f);
- pathLine.lineTo(getWidth() - mTimerRect.width() - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_TIME_MARGIN_RIGHT) - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_LINE_MARGIN), getHeight() * 0.5f);
- canvas.drawPath(pathLine, mLinePaint);
-
- //绘制 时间
- canvas.drawText(measureCurrentTime(), getWidth() - getRawSize(TypedValue.COMPLEX_UNIT_DIP, INDICATOR_TIME_MARGIN_RIGHT), (getHeight() + mTimerRect.height()) * 0.5f, mTimerPaint);
- }
-
- private boolean clickPlayer(MotionEvent event) {
- if (mBtnPlayRect != null && mDownX > (mBtnPlayRect.left - INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownX < (mBtnPlayRect.right + INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownY > (mBtnPlayRect.top - INDICATOR_ICON_PLAY_MARGIN_LEFT) && mDownY < (mBtnPlayRect.bottom + INDICATOR_ICON_PLAY_MARGIN_LEFT)) {
- float upX = event.getX();
- float upY = event.getY();
- return upX > (mBtnPlayRect.left - INDICATOR_ICON_PLAY_MARGIN_LEFT) && upX < (mBtnPlayRect.right + INDICATOR_ICON_PLAY_MARGIN_LEFT) && upY > (mBtnPlayRect.top - INDICATOR_ICON_PLAY_MARGIN_LEFT) && upY < (mBtnPlayRect.bottom + INDICATOR_ICON_PLAY_MARGIN_LEFT);
- }
- return false;
- }
-
- private void doFlingAnimator(float velocity) {
-
- float distance = (velocity / Math.abs(velocity) * (Math.abs(velocity) * SLIDE_COEFFICIENT));
- float to = Math.min(Math.max(0, (mScrollY - distance)), (mLineCount - 1) * mLineHeight + mLineFeedRecord.get(mLineCount - 1) + (mEnableLineFeed ? mTextHeight : 0));
-
- mFlingAnimator = ValueAnimator.ofFloat(mScrollY, to);
- mFlingAnimator.addUpdateListener(animation -> {
- mScrollY = (float) animation.getAnimatedValue();
- measureCurrentLine();
- invalidateView();
- });
-
- mFlingAnimator.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mVelocity = 0;
- mFling = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mFling = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- }
- });
-
- mFlingAnimator.setDuration(FLING_ANIMATOR_DURATION);
- mFlingAnimator.setInterpolator(new DecelerateInterpolator());
- mFlingAnimator.start();
- }
-
- private void setUserTouch(boolean isUserTouch) {
- if (isUserTouch) {
- mUserTouch = true;
- mShowIndicator = true;
- } else {
- mUserTouch = false;
- mShowIndicator = false;
- }
- }
-
- private void releaseVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private void initMyView(Context context) {
- maxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
- initPaint();
- initAllBounds();
- }
-
- private void initAllBounds() {
- setRawTextSize(mTextSize);
-
- setLineSpace(mLineSpace);
- measureLineHeight();
-
- mTimerRect = new Rect();
- mTimerPaint.getTextBounds(mDefaultTime, 0, mDefaultTime.length(), mTimerRect);
-
-
- }
-
- private void initPaint() {
- mTextPaint = new TextPaint();
- mTextPaint.setDither(true);
- mTextPaint.setAntiAlias(true);
-
- switch (mTextAlign) {
- case LEFT:
- mTextPaint.setTextAlign(Paint.Align.LEFT);
- break;
- case CENTER:
- mTextPaint.setTextAlign(Paint.Align.CENTER);
- break;
- case RIGHT:
- mTextPaint.setTextAlign(Paint.Align.RIGHT);
- break;
- }
-
- mBtnPlayPaint = new Paint();
- mBtnPlayPaint.setDither(true);
- mBtnPlayPaint.setAntiAlias(true);
- mBtnPlayPaint.setColor(mBtnColor);
- mBtnPlayPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- mBtnPlayPaint.setAlpha(128);
-
- mLinePaint = new Paint();
- mLinePaint.setDither(true);
- mLinePaint.setAntiAlias(true);
- mLinePaint.setColor(mLineColor);
- mLinePaint.setAlpha(64);
- mLinePaint.setStrokeWidth(1.0f);
- mLinePaint.setStyle(Paint.Style.STROKE);
-
- mTimerPaint = new Paint();
- mTimerPaint.setDither(true);
- mTimerPaint.setAntiAlias(true);
- mTimerPaint.setColor(Color.WHITE);
- mTimerPaint.setTextAlign(Paint.Align.RIGHT);
- mTimerPaint.setTextSize(getRawSize(TypedValue.COMPLEX_UNIT_SP, INDICATOR_TIME_TEXT_SIZE));
-
-
- }
-
- private float measureCurrentScrollY(int line) {
- if (mEnableLineFeed && line > 1) {
- return (line - 1) * mLineHeight + mLineFeedRecord.get(line - 1);
- }
- return (line - 1) * mLineHeight;
- }
-
- private void invalidateView() {
- if (Looper.getMainLooper() == Looper.myLooper()) {
- // 当前线程是主UI线程,直接刷新。
- invalidate();
- } else {
- // 当前线程是非UI线程,post刷新。
- postInvalidate();
- }
- }
-
- private void measureLineHeight() {
- Rect lineBound = new Rect();
- mTextPaint.getTextBounds(mDefaultHint, 0, mDefaultHint.length(), lineBound);
- mTextHeight = lineBound.height();
- mLineHeight = mTextHeight + mLineSpace;
- }
-
- /**
- * To measure current showing line number based on the view's scroll Y
- */
- private void measureCurrentLine() {
- float baseScrollY = mScrollY + mLineHeight * 0.5f;
-
- if (mEnableLineFeed) {
- for (int i = mLyricInfo.songLines.size(); i >= 0; i--) {
- if (baseScrollY > measureCurrentScrollY(i) + mLineSpace * 0.2) {
- mLineNumberUnderIndicator = i - 1;
- break;
- }
- }
- } else {
- mLineNumberUnderIndicator = (int) (baseScrollY / mLineHeight);
- }
-
-
- }
-
- private void smoothScrollTo(float toY) {
- final ValueAnimator animator = ValueAnimator.ofFloat(mScrollY, toY);
- animator.addUpdateListener(valueAnimator -> {
- mScrollY = (Float) valueAnimator.getAnimatedValue();
- invalidateView();
- });
-
- animator.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationCancel(Animator animator) {
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- mFling = false;
- measureCurrentLine();
- invalidateView();
- }
-
- @Override
- public void onAnimationRepeat(Animator animator) {
- }
-
- @Override
- public void onAnimationStart(Animator animator) {
- mFling = true;
- }
- });
- animator.setDuration(640);
- animator.setInterpolator(new LinearInterpolator());
-
- animator.start();
- }
-
- private boolean scrollable() {
- return mLyricInfo != null && mLyricInfo.songLines != null && mLyricInfo.songLines.size() > 0;
- }
-
- private void scrollToCurrentTimeMillis(long time) {
-
- int position = 0;
- if (scrollable()) {
- for (int i = 0, size = mLineCount; i < size; i++) {
- LineInfo lineInfo = mLyricInfo.songLines.get(i);
- if (lineInfo != null && lineInfo.start >= time) {
- position = i;
- break;
- }
- if (i == mLineCount - 1) {
- position = mLineCount;
- }
- }
- }
- if (mCurrentPlayLine != position) {
- mCurrentPlayLine = position;
- if (!mFling && !mUserTouch) {
- smoothScrollTo(measureCurrentScrollY(position));
- }
- }
- }
-
- private void setupLyricResource(InputStream inputStream, String charsetName) {
- if (inputStream != null) {
- try {
- LyricInfo lyricInfo = new LyricInfo();
- lyricInfo.songLines = new ArrayList<>();
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charsetName);
- BufferedReader reader = new BufferedReader(inputStreamReader);
- String line;
- while ((line = reader.readLine()) != null) {
- analyzeLyric(lyricInfo, line);
- }
- reader.close();
- inputStream.close();
- inputStreamReader.close();
-
- mLyricInfo = lyricInfo;
- mLineCount = mLyricInfo.songLines.size();
- invalidateView();
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else {
- invalidateView();
- }
- }
-
- /**
- * 逐行解析歌词内容
- */
- private void analyzeLyric(LyricInfo lyricInfo, String line) {
- int index = line.lastIndexOf("]");
- if (line.startsWith("[offset:")) {
- // time offset
- lyricInfo.songOffset = Long.parseLong(line.substring(8, index).trim());
- return;
- }
- if (line.startsWith("[ti:")) {
- // title
- lyricInfo.songTitle = line.substring(4, index).trim();
- return;
- }
- if (line.startsWith("[ar:")) {
- // artist
- lyricInfo.songArtist = line.substring(4, index).trim();
- return;
- }
- if (line.startsWith("[al:")) {
- // album
- lyricInfo.songAlbum = line.substring(4, index).trim();
- return;
- }
- if (line.startsWith("[by:")) {
- return;
- }
- if (index >= 9 && line.trim().length() > index + 1) {
- // lyrics
- LineInfo lineInfo = new LineInfo();
- lineInfo.content = line.substring(10, line.length());
- lineInfo.start = measureStartTimeMillis(line.substring(0, index));
- lyricInfo.songLines.add(lineInfo);
- }
- }
-
- /**
- * 从字符串中获得时间值
- */
- private long measureStartTimeMillis(String str) {
- long minute = Long.parseLong(str.substring(1, 3));
- long second = Long.parseLong(str.substring(4, 6));
- long millisecond = Long.parseLong(str.substring(7, 9));
- return millisecond + second * 1000 + minute * 60 * 1000;
- }
-
- private void resetLyricInfo() {
- if (mLyricInfo != null) {
- if (mLyricInfo.songLines != null) {
- mLyricInfo.songLines.clear();
- mLyricInfo.songLines = null;
- }
- mLyricInfo = null;
- }
- }
-
- private void resetView() {
- mCurrentPlayLine = 0;
- resetLyricInfo();
- invalidateView();
- mLineCount = 0;
- mScrollY = 0;
- mEnableLineFeed = false;
- mLineFeedRecord.clear();
- mExtraHeight = 0;
- }
-
- private float getRawSize(int unit, float size) {
- Context context = getContext();
- Resources resources;
- if (context == null) {
- resources = Resources.getSystem();
- } else {
- resources = context.getResources();
- }
- return TypedValue.applyDimension(unit, size, resources.getDisplayMetrics());
- }
-
- private void setRawTextSize(float size) {
- if (size != mTextPaint.getTextSize()) {
- mTextPaint.setTextSize(size);
- measureLineHeight();
- mScrollY = measureCurrentScrollY(mCurrentPlayLine);
- invalidateView();
- }
- }
-
- @IntDef({LEFT, CENTER, RIGHT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Alignment {
- }
-
- public interface OnPlayerClickListener {
- void onPlayerClicked(long progress, String content);
- }
-
- private class LyricInfo {
- List songLines;
-
- String songArtist;
- String songTitle;
- String songAlbum;
-
- long songOffset;
- }
-
- private class LineInfo {
- String content;
- long start;
- }
-}
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_album_tag_editor.xml b/app/src/main/res/layout-land/activity_album_tag_editor.xml
index 1cf47696..a96e211f 100644
--- a/app/src/main/res/layout-land/activity_album_tag_editor.xml
+++ b/app/src/main/res/layout-land/activity_album_tag_editor.xml
@@ -79,7 +79,7 @@
android:id="@+id/albumTitleContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:boxBackgroundMode="filled"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:hintEnabled="true">
-
+ android:text="@string/edit"
+ app:icon="@drawable/ic_edit_white_24dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_song_tag_editor.xml b/app/src/main/res/layout/activity_song_tag_editor.xml
index 1e2437b3..08659be6 100755
--- a/app/src/main/res/layout/activity_song_tag_editor.xml
+++ b/app/src/main/res/layout/activity_song_tag_editor.xml
@@ -69,12 +69,11 @@
+ android:layout_height="wrap_content">
-
@@ -144,10 +143,10 @@
@@ -192,12 +191,12 @@
@@ -216,10 +215,10 @@
@@ -237,10 +236,10 @@
diff --git a/app/src/main/res/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml
index 2ff57181..4773c572 100644
--- a/app/src/main/res/layout/activity_user_info.xml
+++ b/app/src/main/res/layout/activity_user_info.xml
@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
+ app:hintAnimationEnabled="true">
+ app:hintAnimationEnabled="true">
+ android:orientation="vertical"
+ android:paddingBottom="16dp">
-
+ android:gravity="end"
+ android:orientation="horizontal">
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_playlist.xml b/app/src/main/res/layout/dialog_playlist.xml
index bffbbd38..3e053b00 100644
--- a/app/src/main/res/layout/dialog_playlist.xml
+++ b/app/src/main/res/layout/dialog_playlist.xml
@@ -16,18 +16,11 @@
-
-
-
+ android:gravity="end"
+ android:orientation="horizontal">
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml
index 950cbffc..02be2931 100644
--- a/app/src/main/res/layout/fragment_player.xml
+++ b/app/src/main/res/layout/fragment_player.xml
@@ -19,7 +19,7 @@
-
@@ -30,6 +30,13 @@
android:orientation="vertical">
+
+
+
+
+
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_synced.xml b/app/src/main/res/layout/fragment_synced.xml
index 00fae9f4..617f838b 100644
--- a/app/src/main/res/layout/fragment_synced.xml
+++ b/app/src/main/res/layout/fragment_synced.xml
@@ -6,15 +6,8 @@
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
-
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9e80d635..da64b5fc 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -24,21 +24,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b4327aa9..7e369d2e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -606,5 +606,6 @@
Save
Pick image
Set a profile photo
+ Edit
diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml
index 0d6c4aef..373f5475 100755
--- a/app/src/main/res/xml/pref_general.xml
+++ b/app/src/main/res/xml/pref_general.xml
@@ -19,7 +19,8 @@
android:persistent="false"
android:summary="@string/primary_color_desc"
android:title="@string/primary_color"
- app:iconSpaceReserved="false" />
+ app:iconSpaceReserved="false"
+ app:isPreferenceVisible="false" />
Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.lauzy.freedom.library.test", appContext.getPackageName());
+ }
+}
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..9b5ce4b3
--- /dev/null
+++ b/library/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/src/main/java/com/lauzy/freedom/library/Lrc.java b/library/src/main/java/com/lauzy/freedom/library/Lrc.java
new file mode 100644
index 00000000..c4dd229a
--- /dev/null
+++ b/library/src/main/java/com/lauzy/freedom/library/Lrc.java
@@ -0,0 +1,30 @@
+package com.lauzy.freedom.library;
+
+/**
+ * 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 void setTime(long time) {
+ this.time = time;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/lauzy/freedom/library/LrcHelper.java b/library/src/main/java/com/lauzy/freedom/library/LrcHelper.java
new file mode 100644
index 00000000..b2eae73e
--- /dev/null
+++ b/library/src/main/java/com/lauzy/freedom/library/LrcHelper.java
@@ -0,0 +1,137 @@
+package com.lauzy.freedom.library;
+
+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 parseLrcFromAssets(Context context, String fileName) {
+ try {
+ return parseInputStream(context.getResources().getAssets().open(fileName));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static List parseLrcFromFile(File file) {
+ try {
+ return parseInputStream(new FileInputStream(file));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static List parseInputStream(InputStream inputStream) {
+ List 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 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 lrcs) {
+ Collections.sort(lrcs, new Comparator() {
+ @Override
+ public int compare(Lrc o1, Lrc o2) {
+ return (int) (o1.getTime() - o2.getTime());
+ }
+ });
+ }
+
+ private static List parseLrc(String lrcLine) {
+ if (lrcLine.trim().isEmpty()) {
+ return null;
+ }
+ List 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 + "";
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/java/com/lauzy/freedom/library/LrcView.kt b/library/src/main/java/com/lauzy/freedom/library/LrcView.kt
new file mode 100644
index 00000000..d6d11449
--- /dev/null
+++ b/library/src/main/java/com/lauzy/freedom/library/LrcView.kt
@@ -0,0 +1,620 @@
+package com.lauzy.freedom.library
+
+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 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? = 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()
+ private val mHideIndicatorRunnable = Runnable {
+ isShowTimeIndicator = false
+ invalidateView()
+ }
+ private val mStaticLayoutHashMap = HashMap()
+ 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) {
+ 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"
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/res/drawable-xhdpi/play_icon.png b/library/src/main/res/drawable-xhdpi/play_icon.png
new file mode 100644
index 00000000..b8b879c6
Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/play_icon.png differ
diff --git a/library/src/main/res/font/circular.xml b/library/src/main/res/font/circular.xml
new file mode 100644
index 00000000..6f20e75b
--- /dev/null
+++ b/library/src/main/res/font/circular.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/font/circular_std_black.otf b/library/src/main/res/font/circular_std_black.otf
new file mode 100755
index 00000000..c62b210c
Binary files /dev/null and b/library/src/main/res/font/circular_std_black.otf differ
diff --git a/library/src/main/res/font/circular_std_book.otf b/library/src/main/res/font/circular_std_book.otf
new file mode 100755
index 00000000..3a1f1ad8
Binary files /dev/null and b/library/src/main/res/font/circular_std_book.otf differ
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..1b45e273
--- /dev/null
+++ b/library/src/main/res/values/attrs.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml
new file mode 100644
index 00000000..49fc91e1
--- /dev/null
+++ b/library/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ library
+
diff --git a/library/src/test/java/com/lauzy/freedom/library/ExampleUnitTest.java b/library/src/test/java/com/lauzy/freedom/library/ExampleUnitTest.java
new file mode 100644
index 00000000..94a3670b
--- /dev/null
+++ b/library/src/test/java/com/lauzy/freedom/library/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.lauzy.freedom.library;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 91441848..8a3bd3f4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':appthemehelper'
\ No newline at end of file
+include ':app', ':appthemehelper', ':library'
\ No newline at end of file