From f9f30c838746c929daca6124391a054b53446741 Mon Sep 17 00:00:00 2001 From: h4h13 Date: Thu, 28 Mar 2019 19:02:53 +0530 Subject: [PATCH] Updates edittext views with corner rounded --- app/build.gradle | 3 +- app/src/main/assets/retro-changelog.html | 2 +- .../dialogs/CreatePlaylistDialog.kt | 15 +- .../dialogs/DeletePlaylistDialog.kt | 4 +- .../retromusic/dialogs/DeleteSongsDialog.kt | 4 +- .../dialogs/RemoveFromPlaylistDialog.kt | 13 +- .../dialogs/RenamePlaylistDialog.kt | 12 +- .../retromusic/exfab/FabIconAnimator.java | 143 --- .../exfab/TransientBarBehavior.java | 75 -- .../ui/activities/AlbumDetailsActivity.kt | 17 +- .../ui/activities/LyricsActivity.kt | 47 +- .../ui/activities/UserInfoActivity.kt | 2 +- .../tageditor/SongTagEditorActivity.kt | 2 +- .../fragments/player/normal/PlayerFragment.kt | 12 +- .../monkey/retromusic/util/PlaylistsUtil.java | 4 +- .../monkey/retromusic/views/LyricView.java | 890 ------------------ .../layout-land/activity_album_tag_editor.xml | 8 +- .../res/layout/activity_album_tag_editor.xml | 8 +- app/src/main/res/layout/activity_lyrics.xml | 5 +- .../res/layout/activity_song_tag_editor.xml | 23 +- .../main/res/layout/activity_user_info.xml | 10 +- .../res/layout/dialog_add_to_playlist.xml | 4 +- app/src/main/res/layout/dialog_delete.xml | 58 +- app/src/main/res/layout/dialog_playlist.xml | 65 +- app/src/main/res/layout/fragment_player.xml | 13 +- app/src/main/res/layout/fragment_synced.xml | 11 +- app/src/main/res/values/attrs.xml | 15 - app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/pref_general.xml | 3 +- .../appthemehelper/util/MaterialUtil.kt | 9 +- library/.gitignore | 1 + library/build.gradle | 41 + library/proguard-rules.pro | 21 + .../library/ExampleInstrumentedTest.java | 26 + library/src/main/AndroidManifest.xml | 2 + .../java/com/lauzy/freedom/library/Lrc.java | 30 + .../com/lauzy/freedom/library/LrcHelper.java | 137 +++ .../java/com/lauzy/freedom/library/LrcView.kt | 620 ++++++++++++ .../src/main/res/drawable-xhdpi/play_icon.png | Bin 0 -> 707 bytes library/src/main/res/font/circular.xml | 11 + .../src/main/res/font/circular_std_black.otf | Bin 0 -> 74500 bytes .../src/main/res/font/circular_std_book.otf | Bin 0 -> 68940 bytes library/src/main/res/values/attrs.xml | 24 + library/src/main/res/values/strings.xml | 3 + .../freedom/library/ExampleUnitTest.java | 17 + settings.gradle | 2 +- 46 files changed, 1127 insertions(+), 1286 deletions(-) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/exfab/FabIconAnimator.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/exfab/TransientBarBehavior.java delete mode 100644 app/src/main/java/code/name/monkey/retromusic/views/LyricView.java create mode 100644 library/.gitignore create mode 100644 library/build.gradle create mode 100644 library/proguard-rules.pro create mode 100644 library/src/androidTest/java/com/lauzy/freedom/library/ExampleInstrumentedTest.java create mode 100644 library/src/main/AndroidManifest.xml create mode 100644 library/src/main/java/com/lauzy/freedom/library/Lrc.java create mode 100644 library/src/main/java/com/lauzy/freedom/library/LrcHelper.java create mode 100644 library/src/main/java/com/lauzy/freedom/library/LrcView.kt create mode 100644 library/src/main/res/drawable-xhdpi/play_icon.png create mode 100644 library/src/main/res/font/circular.xml create mode 100755 library/src/main/res/font/circular_std_black.otf create mode 100755 library/src/main/res/font/circular_std_book.otf create mode 100644 library/src/main/res/values/attrs.xml create mode 100644 library/src/main/res/values/strings.xml create mode 100644 library/src/test/java/com/lauzy/freedom/library/ExampleUnitTest.java 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

v3.1.240

v3.1.200

v3.0.570

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

v3.1.240

v3.1.200

v3.0.570

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 0000000000000000000000000000000000000000..b8b879c6f28701c0d7f41091e014caf97530cb62 GIT binary patch literal 707 zcmV;!0zCbRP)ljA6bJBk%8+E?Q)IJ03ox>_QU}zbLP!-z2!vpvFww157lc?CD2yPcGLj|uE>UVl zq6;0m6sd|EC;p?W666KcTztMe`!f3!zu&!gPw&jA=08^R_{jtK45+z)FM*mTfIkF% z3HTDIxqvT$nkRssF3{<8Zp3l?+XY-LB@hI`d&bytk|a;#IKH$QLKnaxW9$}yVGZAfY&QENBC>>xP z^hK6skK66`ZnxV_O+uIgSj7BeI-RZ!27|6C1k(WWkjwz=CQ0&SG#Z_k4PhK0cQ*V2 za90TNu}lPO0CGVeN(j#RX&DeU0?4>!DLjaxXpe|8oe;JHEMopXo6Xkx{eGki!FB){ zw``NIbj=km09fDKZGiGxoCDbAobSs~VAWdx*LqtsptMU7s{mwc1F%)*wU`wE zMYpzP%EbnN!dsgY06JTn^#HnCo6P{Fv^HA-N^5Oa0RS-a#pk*R+PVM{-6W#@@p!y# z;+vmQfG`YSW?A-02yt}X7cupmmJBczjLQFd02Q?=t??O9rQKAC#b ps>I?mKt-)eYkUS&X*X43`2(i9dQftn1ML6+002ovPDHLkV1hraG4ucc literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c62b210c51fc6b0a7e4022ac9dd54d50cf5942a3 GIT binary patch literal 74500 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�$ENekd=fCSx(51l~{o+rz#-JDFw1p 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%0D3`R&67{=)GDS8?p!cah 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 DB}TqP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3a1f1ad82ef9ee891d7977cd5f6736404cb3afa1 GIT binary patch literal 68940 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 bm)u+K19{O;?hmegZf|)^1FfGBrz`&-FR{+x literal 0 HcmV?d00001 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