From c42e9cb0df1cec897fa52d649ca39ec7ab362ecf Mon Sep 17 00:00:00 2001 From: h4h13 Date: Tue, 25 Feb 2020 04:03:05 +0530 Subject: [PATCH] Improved slider --- .github/stale.yml | 61 -- app/build.gradle | 10 +- .../retromusic/activities/MainActivity.java | 4 +- .../retromusic/dagger/MusicComponent.kt | 2 +- .../retromusic/extensions/ViewExtensions.kt | 8 +- .../retromusic/fragments/NowPlayingScreen.kt | 13 +- .../retromusic/fragments/VolumeFragment.kt | 52 +- .../{home => }/BannerHomeFragment.kt | 16 +- .../{folders => }/FoldersFragment.java | 16 +- .../AdaptivePlaybackControlsFragment.kt | 34 +- .../blur/BlurPlaybackControlsFragment.kt | 42 +- .../CardBlurPlaybackControlsFragment.kt | 42 +- .../player/circle/CirclePlayerFragment.kt | 55 +- .../color/ColorPlaybackControlsFragment.kt | 67 +- .../player/fit/FitPlaybackControlsFragment.kt | 28 +- .../flat/FlatPlaybackControlsFragment.kt | 73 +- .../full/FullPlaybackControlsFragment.kt | 69 +- .../material/MaterialControlsFragment.kt | 74 +- .../normal/PlayerPlaybackControlsFragment.kt | 86 ++- .../player/peak/PeakPlayerControlFragment.kt | 66 +- .../plain/PlainPlaybackControlsFragment.kt | 64 +- .../player/tiny/TinyPlayerFragment.kt | 19 +- .../helper/MusicProgressViewUpdateHelper.kt | 5 +- .../retromusic/util/PreferenceUtil.java | 2 +- .../name/monkey/retromusic/util/ViewUtil.kt | 11 + .../views/RetroShapeableImageView.kt | 9 +- .../volume/AudioVolumeContentObserver.java | 13 +- ...r.java => OnAudioVolumeChangedListener.kt} | 8 +- .../layout-land/fragment_circle_player.xml | 153 ++--- .../fragment_player_playback_controls.xml | 9 +- ...ment_adaptive_player_playback_controls.xml | 28 +- ...fragment_blur_player_playback_controls.xml | 20 +- ...ent_card_blur_player_playback_controls.xml | 15 +- .../res/layout/fragment_circle_player.xml | 91 ++- ...ragment_color_player_playback_controls.xml | 11 +- ...fragment_flat_player_playback_controls.xml | 8 +- .../layout/fragment_full_player_controls.xml | 13 +- .../fragment_material_playback_controls.xml | 11 +- .../layout/fragment_peak_control_player.xml | 11 +- .../fragment_plain_controls_fragment.xml | 11 +- .../fragment_player_playback_controls.xml | 12 +- .../main/res/layout/fragment_tiny_player.xml | 1 - app/src/main/res/layout/fragment_volume.xml | 10 +- appthemehelper/build.gradle | 2 +- .../res/values/colors_material_design.xml | 1 + gradle.properties | 2 - gradle/wrapper/gradle-wrapper.properties | 4 +- liboverscroll/.gitignore | 1 - liboverscroll/build.gradle | 49 -- liboverscroll/src/main/AndroidManifest.xml | 5 - ...zontalOverScrollBounceEffectDecorator.java | 99 --- .../ui/overscroll/IOverScrollDecor.java | 33 - .../ui/overscroll/IOverScrollState.java | 19 - .../overscroll/IOverScrollStateListener.java | 25 - .../overscroll/IOverScrollUpdateListener.java | 22 - .../android/ui/overscroll/ListenerStubs.java | 17 - .../OverScrollBounceEffectDecoratorBase.java | 483 ------------- .../overscroll/OverScrollDecoratorHelper.java | 78 --- ...rticalOverScrollBounceEffectDecorator.java | 100 --- .../AbsListViewOverScrollDecorAdapter.java | 57 -- ...ontalScrollViewOverScrollDecorAdapter.java | 41 -- .../adapters/IOverScrollDecoratorAdapter.java | 33 - ...estedScrollViewOverScrollDecorAdapter.java | 40 -- .../RecyclerViewOverScrollDecorAdapter.java | 237 ------- .../ScrollViewOverScrollDecorAdapter.java | 43 -- .../StaticOverScrollDecorAdapter.java | 38 -- ...alOverScrollBounceEffectDecoratorTest.java | 640 ------------------ ...alOverScrollBounceEffectDecoratorTest.java | 600 ---------------- 68 files changed, 621 insertions(+), 3401 deletions(-) delete mode 100644 .github/stale.yml rename app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/{home => }/BannerHomeFragment.kt (91%) rename app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/{folders => }/FoldersFragment.java (97%) rename app/src/main/java/code/name/monkey/retromusic/volume/{OnAudioVolumeChangedListener.java => OnAudioVolumeChangedListener.kt} (78%) delete mode 100755 liboverscroll/.gitignore delete mode 100755 liboverscroll/build.gradle delete mode 100755 liboverscroll/src/main/AndroidManifest.xml delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecorator.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollDecor.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollState.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollStateListener.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollUpdateListener.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/ListenerStubs.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollBounceEffectDecoratorBase.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollDecoratorHelper.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecorator.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/AbsListViewOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/HorizontalScrollViewOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/IOverScrollDecoratorAdapter.java delete mode 100644 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/NestedScrollViewOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/RecyclerViewOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ScrollViewOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/StaticOverScrollDecorAdapter.java delete mode 100755 liboverscroll/src/test/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecoratorTest.java delete mode 100755 liboverscroll/src/test/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecoratorTest.java diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index ad3996c5..00000000 --- a/.github/stale.yml +++ /dev/null @@ -1,61 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 60 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 7 - -# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) -onlyLabels: [] - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: - - pinned - - security - - "[Status] Maybe Later" - -# Set to true to ignore issues in a project (defaults to false) -exemptProjects: false - -# Set to true to ignore issues in a milestone (defaults to false) -exemptMilestones: false - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: false - -# Label to use when marking as stale -staleLabel: stale - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. - -# Comment to post when removing the stale label. -# unmarkComment: > -# Your comment here. - -# Comment to post when closing a stale Issue or Pull Request. -# closeComment: > -# Your comment here. - -# Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 30 - -# Limit to only `issues` or `pulls` -# only: issues - -# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': -# pulls: -# daysUntilStale: 30 -# markComment: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. - -# issues: -# exemptLabels: -# - confirmed \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ca2261c0..1301858d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,8 @@ proguardDictionaries { android { compileSdkVersion 29 + buildToolsVersion = '29.0.2' + defaultConfig { minSdkVersion 21 targetSdkVersion 29 @@ -81,7 +83,7 @@ android { sourceCompatibility '1.8' targetCompatibility '1.8' } - buildToolsVersion = '29.0.2' + configurations.all { resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9' @@ -130,10 +132,10 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.core:core-ktx:1.2.0' - implementation 'androidx.fragment:fragment:1.2.1' + implementation 'androidx.fragment:fragment:1.2.2' implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'com.google.android.material:material:1.2.0-alpha04' + implementation 'com.google.android.material:material:1.2.0-alpha05' def retrofit_version = "2.6.2" implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @@ -160,7 +162,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - def kotlin_coroutines_version = "1.3.0" + def kotlin_coroutines_version = "1.3.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java index fab4dd45..e8f80904 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.java @@ -51,8 +51,8 @@ import code.name.monkey.retromusic.fragments.mainactivity.GenresFragment; import code.name.monkey.retromusic.fragments.mainactivity.PlayingQueueFragment; import code.name.monkey.retromusic.fragments.mainactivity.PlaylistsFragment; import code.name.monkey.retromusic.fragments.mainactivity.SongsFragment; -import code.name.monkey.retromusic.fragments.mainactivity.folders.FoldersFragment; -import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment; +import code.name.monkey.retromusic.fragments.mainactivity.FoldersFragment; +import code.name.monkey.retromusic.fragments.mainactivity.BannerHomeFragment; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.helper.SearchQueryHelper; import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder; diff --git a/app/src/main/java/code/name/monkey/retromusic/dagger/MusicComponent.kt b/app/src/main/java/code/name/monkey/retromusic/dagger/MusicComponent.kt index 6815f918..efa9a1c5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dagger/MusicComponent.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dagger/MusicComponent.kt @@ -26,7 +26,7 @@ import code.name.monkey.retromusic.fragments.mainactivity.ArtistsFragment import code.name.monkey.retromusic.fragments.mainactivity.GenresFragment import code.name.monkey.retromusic.fragments.mainactivity.PlaylistsFragment import code.name.monkey.retromusic.fragments.mainactivity.SongsFragment -import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment +import code.name.monkey.retromusic.fragments.mainactivity.BannerHomeFragment import dagger.Component import javax.inject.Singleton diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt index 64e400dc..d9a3cb50 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt @@ -21,7 +21,7 @@ import android.widget.EditText import androidx.annotation.LayoutRes import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.TintHelper - +import com.google.android.material.slider.Slider @Suppress("UNCHECKED_CAST") fun ViewGroup.inflate(@LayoutRes layout: Int): T { @@ -45,4 +45,10 @@ fun View.showOrHide(show: Boolean) = if (show) show() else hide() fun EditText.appHandleColor(): EditText { TintHelper.colorHandles(this, ThemeStore.accentColor(context)) return this +} + +fun Slider.setRange(progress: Float, to: Float) { + valueFrom = 0F + valueTo = to + value = progress } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt index acb67d59..2af15b23 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/NowPlayingScreen.kt @@ -11,20 +11,19 @@ enum class NowPlayingScreen constructor( val id: Int ) { - NORMAL(R.string.normal, R.drawable.np_normal, 0), - FLAT(R.string.flat, R.drawable.np_flat, 1), - FIT(R.string.fit, R.drawable.np_fit, 12), - TINY(R.string.tiny, R.drawable.np_tiny, 7), - PEAK(R.string.peak, R.drawable.np_peak, 14), - ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10), BLUR(R.string.blur, R.drawable.np_blur, 4), BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9), CARD(R.string.card, R.drawable.np_card, 6), - COLOR(R.string.color, R.drawable.np_color, 5), CIRCLE(R.string.circle, R.drawable.np_minimalistic_circle, 15), + COLOR(R.string.color, R.drawable.np_color, 5), + FIT(R.string.fit, R.drawable.np_fit, 12), + FLAT(R.string.flat, R.drawable.np_flat, 1), FULL(R.string.full, R.drawable.np_full, 2), MATERIAL(R.string.material, R.drawable.np_material, 11), + NORMAL(R.string.normal, R.drawable.np_normal, 0), + PEAK(R.string.peak, R.drawable.np_peak, 14), PLAIN(R.string.plain, R.drawable.np_plain, 3), SIMPLE(R.string.simple, R.drawable.np_simple, 8), + TINY(R.string.tiny, R.drawable.np_tiny, 7), } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt index 0ab8c3fa..c7900e5d 100755 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt @@ -8,7 +8,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.SeekBar import androidx.fragment.app.Fragment import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R @@ -17,12 +16,14 @@ import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.volume.AudioVolumeObserver import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener +import com.google.android.material.slider.Slider +import com.google.android.material.slider.Slider.OnChangeListener import kotlinx.android.synthetic.main.fragment_volume.volumeDown import kotlinx.android.synthetic.main.fragment_volume.volumeSeekBar import kotlinx.android.synthetic.main.fragment_volume.volumeUp -class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolumeChangedListener, - View.OnClickListener { +class VolumeFragment : Fragment(), OnAudioVolumeChangedListener, + View.OnClickListener, OnChangeListener { private var audioVolumeObserver: AudioVolumeObserver? = null @@ -51,20 +52,22 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum val audioManager = audioManager if (audioManager != null) { - volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) - volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + volumeSeekBar.valueTo = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat() + volumeSeekBar.value = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat() } - volumeSeekBar.setOnSeekBarChangeListener(this) + volumeSeekBar.addOnChangeListener(this) } - override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) { + override fun onAudioVolumeChanged(currentVolume: Float, maxVolume: Float) { if (volumeSeekBar == null) { return } - - volumeSeekBar.max = maxVolume - volumeSeekBar.progress = currentVolume - volumeDown.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) + if (maxVolume <= 0) { + return + } + volumeSeekBar.valueTo = maxVolume + volumeSeekBar.valueFrom = currentVolume + volumeDown.setImageResource(if (currentVolume == 0.0f) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) } override fun onDestroyView() { @@ -74,19 +77,6 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum } } - override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) { - val audioManager = audioManager - audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0) - setPauseWhenZeroVolume(i < 1) - volumeDown?.setImageResource(if (i == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) - } - - override fun onStartTrackingTouch(seekBar: SeekBar) { - } - - override fun onStopTrackingTouch(seekBar: SeekBar) { - } - override fun onClick(view: View) { val audioManager = audioManager when (view.id) { @@ -111,10 +101,6 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum ViewUtil.setProgressDrawable(volumeSeekBar, color, true) } - fun removeThumb() { - volumeSeekBar.thumb = null - } - private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) { if (PreferenceUtil.getInstance(requireContext()).pauseOnZeroVolume()) if (MusicPlayerRemote.isPlaying && pauseWhenZeroVolume) { MusicPlayerRemote.pauseSong() @@ -134,4 +120,14 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum return VolumeFragment() } } + + override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { + if (value <= 0) { + return + } + val audioManager = audioManager + audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, value.toInt(), 0) + setPauseWhenZeroVolume(value < 1.0f) + volumeDown.setImageResource(if (value == 0.0f) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp) + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/home/BannerHomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/BannerHomeFragment.kt similarity index 91% rename from app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/home/BannerHomeFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/BannerHomeFragment.kt index 3d06f897..cfc5c9a5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/home/BannerHomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/BannerHomeFragment.kt @@ -1,4 +1,18 @@ -package code.name.monkey.retromusic.fragments.mainactivity.home +/* + * Copyright (c) 2020 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package code.name.monkey.retromusic.fragments.mainactivity import android.app.ActivityOptions import android.os.Bundle diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/FoldersFragment.java similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/folders/FoldersFragment.java rename to app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/FoldersFragment.java index 927808ae..d3bf8c43 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/mainactivity/FoldersFragment.java @@ -1,4 +1,18 @@ -package code.name.monkey.retromusic.fragments.mainactivity.folders; +/* + * Copyright (c) 2020 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ + +package code.name.monkey.retromusic.fragments.mainactivity; import android.app.Dialog; import android.content.Context; diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt index bcde8aeb..bd8b2d93 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptivePlaybackControlsFragment.kt @@ -1,13 +1,10 @@ package code.name.monkey.retromusic.fragments.player.adaptive -import android.animation.ObjectAnimator import android.graphics.PorterDuff import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -16,12 +13,12 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil @@ -212,13 +209,10 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } @@ -232,16 +226,14 @@ class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) } - }) + } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlaybackControlsFragment.kt index 41120517..cb466d23 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.blur -import android.animation.ObjectAnimator import android.graphics.Color import android.graphics.PorterDuff import android.os.Bundle @@ -9,20 +8,18 @@ import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import androidx.core.content.ContextCompat 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.R import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil @@ -235,20 +232,6 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { } } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } - } - }) - } - private fun showBonceAnimation() { playPauseButton.apply { clearAnimation() @@ -273,14 +256,23 @@ class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt index 81f1e9c4..7326d247 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.cardblur -import android.animation.ObjectAnimator import android.graphics.Color import android.graphics.PorterDuff import android.os.Bundle @@ -8,19 +7,17 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar 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.R import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil @@ -210,29 +207,24 @@ class CardBlurPlaybackControlsFragment : AbsPlayerControlsFragment() { } } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } - } - }) - } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt index 39a55edc..dea50e38 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt @@ -14,7 +14,6 @@ package code.name.monkey.retromusic.fragments.player.circle -import android.animation.ObjectAnimator import android.content.Context import android.graphics.Color import android.graphics.PorterDuff @@ -23,8 +22,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil @@ -32,14 +29,13 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil @@ -100,14 +96,7 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged } private fun setupViews() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) - } - } - }) + setUpProgressSlider() ViewUtil.setProgressDrawable(progressSlider, ThemeStore.accentColor(requireContext())) volumeSeekBar.progressColor = ThemeStore.accentColor(requireContext()) setUpPlayPauseFab() @@ -205,18 +194,6 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged } } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - - songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) - } - private fun updatePlayPauseDrawableState() { when { MusicPlayerRemote.isPlaying -> playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) @@ -224,12 +201,13 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged } } - override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) { + override fun onAudioVolumeChanged(currentVolume: Float, maxVolume: Float) { if (volumeSeekBar == null) { return } - volumeSeekBar.max = maxVolume - volumeSeekBar.progress = currentVolume + + volumeSeekBar.max = maxVolume.toInt() + volumeSeekBar.progress = currentVolume.toInt() } override fun onDestroyView() { @@ -249,4 +227,25 @@ class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChanged override fun onStopTrackingTouch(seekArc: SeekArc?) { } + + override fun onUpdateProgressViews(progress: Int, total: Int) { + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + + fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorPlaybackControlsFragment.kt index d4b46285..a7c2bc8a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.color -import android.animation.ObjectAnimator import android.graphics.Color import android.graphics.PorterDuff import android.os.Bundle @@ -8,33 +7,21 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.nextButton -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.previousButton -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.songInfo -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.text -import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.title +import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.* class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() { @@ -47,7 +34,11 @@ class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() { progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { return inflater.inflate(R.layout.fragment_color_player_playback_controls, container, false) } @@ -173,7 +164,10 @@ class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -185,7 +179,10 @@ class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -215,26 +212,24 @@ class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() { } } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) - } - } - }) - } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider!!.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitPlaybackControlsFragment.kt index 6a3f9e6f..7dfea6d4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitPlaybackControlsFragment.kt @@ -27,17 +27,7 @@ import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.nextButton -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.previousButton -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.songInfo -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.text -import kotlinx.android.synthetic.main.fragment_fit_playback_controls.title +import kotlinx.android.synthetic.main.fragment_fit_playback_controls.* class FitPlaybackControlsFragment : AbsPlayerControlsFragment() { @@ -126,10 +116,12 @@ class FitPlaybackControlsFragment : AbsPlayerControlsFragment() { val colorBg = ATHUtil.resolveColor(activity, android.R.attr.colorBackground) if (ColorUtil.isColorLight(colorBg)) { lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) } else { lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) } val colorFinal = if (PreferenceUtil.getInstance(requireContext()).adaptiveColor) { @@ -196,7 +188,10 @@ class FitPlaybackControlsFragment : AbsPlayerControlsFragment() { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -208,7 +203,10 @@ class FitPlaybackControlsFragment : AbsPlayerControlsFragment() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlaybackControlsFragment.kt index 4780e6b1..c538794c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlaybackControlsFragment.kt @@ -1,14 +1,11 @@ package code.name.monkey.retromusic.fragments.player.flat -import android.animation.ObjectAnimator import android.graphics.PorterDuff import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -17,26 +14,18 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.songInfo -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.text -import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.title +import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.* class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { @@ -73,18 +62,6 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { progressViewUpdateHelper.stop() } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - - songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) - } - public override fun show() { playPauseButton!!.animate() .scaleX(1f) @@ -106,10 +83,12 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { val isDark = ColorUtil.isColorLight(colorBg) if (isDark) { lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getSecondaryDisabledTextColor(activity, true) } else { lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getPrimaryDisabledTextColor(activity, false) } val colorFinal = if (PreferenceUtil.getInstance(requireContext()).adaptiveColor) { @@ -131,7 +110,8 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { val isDark = ColorUtil.isColorLight(color) val darkColor = ColorUtil.darkenColor(color) val colorPrimary = MaterialValueHelper.getPrimaryTextColor(context, isDark) - val colorSecondary = MaterialValueHelper.getSecondaryTextColor(context, ColorUtil.isColorLight(darkColor)) + val colorSecondary = + MaterialValueHelper.getSecondaryTextColor(context, ColorUtil.isColorLight(darkColor)) TintHelper.setTintAuto(playPauseButton!!, colorPrimary, false) TintHelper.setTintAuto(playPauseButton!!, color, true) @@ -191,18 +171,25 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { } } + override fun onUpdateProgressViews(progress: Int, total: Int) { + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + override fun setUpProgressSlider() { - progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) } - }) + } } override fun onRepeatModeChanged() { @@ -221,7 +208,10 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -244,7 +234,10 @@ class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlaybackControlsFragment.kt index 49d92967..c1ab60aa 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.full -import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.res.ColorStateList import android.graphics.Color @@ -12,9 +11,7 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator import android.widget.PopupMenu -import android.widget.SeekBar import androidx.core.content.ContextCompat import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ColorUtil @@ -23,36 +20,25 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_full_player_controls.nextButton -import kotlinx.android.synthetic.main.fragment_full_player_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_full_player_controls.playerMenu -import kotlinx.android.synthetic.main.fragment_full_player_controls.previousButton -import kotlinx.android.synthetic.main.fragment_full_player_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_full_player_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_full_player_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_full_player_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_full_player_controls.songFavourite -import kotlinx.android.synthetic.main.fragment_full_player_controls.songInfo -import kotlinx.android.synthetic.main.fragment_full_player_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_full_player_controls.text -import kotlinx.android.synthetic.main.fragment_full_player_controls.title +import kotlinx.android.synthetic.main.fragment_full_player_controls.* /** * Created by hemanths on 20/09/17. */ -class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMenuItemClickListener { +class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), + PopupMenu.OnMenuItemClickListener { private var lastPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0 @@ -91,17 +77,6 @@ class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMe progressViewUpdateHelper!!.stop() } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - - songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) - } public override fun show() { playPauseButton!!.animate() @@ -228,15 +203,25 @@ class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMe previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN) } + override fun onUpdateProgressViews(progress: Int, total: Int) { + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + override fun setUpProgressSlider() { - progressSlider!!.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) - } + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) } - }) + } } override fun onRepeatModeChanged() { @@ -257,7 +242,10 @@ class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMe lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -269,7 +257,10 @@ class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMe when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -300,7 +291,7 @@ class FullPlaybackControlsFragment : AbsPlayerControlsFragment(), PopupMenu.OnMe @SuppressLint("StaticFieldLeak") fun updateIsFavorite() { if (updateIsFavoriteTask != null) { - updateIsFavoriteTask!!.cancel(false) + updateIsFavoriteTask?.cancel(false) } updateIsFavoriteTask = object : AsyncTask() { override fun doInBackground(vararg params: Song): Boolean? { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialControlsFragment.kt index 4a6a9e7a..22844441 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialControlsFragment.kt @@ -1,41 +1,24 @@ package code.name.monkey.retromusic.fragments.player.material -import android.animation.ObjectAnimator import android.graphics.PorterDuff import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.extensions.hide -import code.name.monkey.retromusic.extensions.ripAlpha -import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.extensions.textColorSecondary +import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_material_playback_controls.nextButton -import kotlinx.android.synthetic.main.fragment_material_playback_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_material_playback_controls.previousButton -import kotlinx.android.synthetic.main.fragment_material_playback_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_material_playback_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_material_playback_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_material_playback_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_material_playback_controls.songInfo -import kotlinx.android.synthetic.main.fragment_material_playback_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_material_playback_controls.text -import kotlinx.android.synthetic.main.fragment_material_playback_controls.title +import kotlinx.android.synthetic.main.fragment_material_playback_controls.* /** * @author Hemanth S (h4h13). @@ -115,11 +98,13 @@ class MaterialControlsFragment : AbsPlayerControlsFragment() { override fun setDark(color: Int) { val colorBg = ATHUtil.resolveColor(requireContext(), R.attr.colorSurface) if (ColorUtil.isColorLight(colorBg)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(requireContext(), true) + lastPlaybackControlsColor = + MaterialValueHelper.getSecondaryTextColor(requireContext(), true) lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true) } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(requireContext(), false) + lastPlaybackControlsColor = + MaterialValueHelper.getPrimaryTextColor(requireContext(), false) lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false) } @@ -187,7 +172,10 @@ class MaterialControlsFragment : AbsPlayerControlsFragment() { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -199,7 +187,10 @@ class MaterialControlsFragment : AbsPlayerControlsFragment() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -218,29 +209,24 @@ class MaterialControlsFragment : AbsPlayerControlsFragment() { public override fun hide() { } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } - } - }) - } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider!!.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerPlaybackControlsFragment.kt index e98736d2..4e3d5621 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.normal -import android.animation.ObjectAnimator import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.graphics.PorterDuff @@ -9,8 +8,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -19,29 +16,20 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_player_playback_controls.nextButton -import kotlinx.android.synthetic.main.fragment_player_playback_controls.playPauseButton -import kotlinx.android.synthetic.main.fragment_player_playback_controls.previousButton -import kotlinx.android.synthetic.main.fragment_player_playback_controls.progressSlider -import kotlinx.android.synthetic.main.fragment_player_playback_controls.repeatButton -import kotlinx.android.synthetic.main.fragment_player_playback_controls.shuffleButton -import kotlinx.android.synthetic.main.fragment_player_playback_controls.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_player_playback_controls.songInfo -import kotlinx.android.synthetic.main.fragment_player_playback_controls.songTotalTime -import kotlinx.android.synthetic.main.fragment_player_playback_controls.text -import kotlinx.android.synthetic.main.fragment_player_playback_controls.title +import kotlinx.android.synthetic.main.fragment_player_playback_controls.* -class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPreferenceChangeListener { +class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), + OnSharedPreferenceChangeListener { private var lastPlaybackControlsColor: Int = 0 private var lastDisabledPlaybackControlsColor: Int = 0 @@ -77,11 +65,13 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref override fun setDark(color: Int) { val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground) if (ColorUtil.isColorLight(colorBg)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(requireContext(), true) + lastPlaybackControlsColor = + MaterialValueHelper.getSecondaryTextColor(requireContext(), true) lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true) } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(requireContext(), false) + lastPlaybackControlsColor = + MaterialValueHelper.getPrimaryTextColor(requireContext(), false) lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false) } @@ -94,15 +84,15 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref TintHelper.setTintAuto( playPauseButton, - MaterialValueHelper.getPrimaryTextColor(requireContext(), ColorUtil.isColorLight(colorFinal)), + MaterialValueHelper.getPrimaryTextColor( + requireContext(), + ColorUtil.isColorLight(colorFinal) + ), false ) TintHelper.setTintAuto(playPauseButton, colorFinal, true) - - ViewUtil.setProgressDrawable(progressSlider, colorFinal, false) - + ViewUtil.setProgressDrawable(progressSlider, colorFinal, true) volumeFragment?.setTintable(colorFinal) - updateRepeatState() updateShuffleState() updatePrevNextColor() @@ -196,7 +186,10 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -208,7 +201,10 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -240,32 +236,27 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref } } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } - } - }) - } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key == PreferenceUtil.EXTRA_SONG_INFO) { @@ -275,6 +266,7 @@ class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment(), OnSharedPref override fun onDestroyView() { super.onDestroyView() - PreferenceUtil.getInstance(requireContext()).unregisterOnSharedPreferenceChangedListener(this) + PreferenceUtil.getInstance(requireContext()) + .unregisterOnSharedPreferenceChangedListener(this) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerControlFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerControlFragment.kt index 6af74e40..b77017f3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerControlFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerControlFragment.kt @@ -14,39 +14,29 @@ package code.name.monkey.retromusic.fragments.player.peak -import android.animation.ObjectAnimator import android.graphics.Color import android.graphics.PorterDuff import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.util.ATHUtil 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.R import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.textColorSecondary import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_peak_control_player.nextButton -import kotlinx.android.synthetic.main.fragment_peak_control_player.playPauseButton -import kotlinx.android.synthetic.main.fragment_peak_control_player.previousButton -import kotlinx.android.synthetic.main.fragment_peak_control_player.progressSlider -import kotlinx.android.synthetic.main.fragment_peak_control_player.repeatButton -import kotlinx.android.synthetic.main.fragment_peak_control_player.shuffleButton -import kotlinx.android.synthetic.main.fragment_peak_control_player.songCurrentProgress -import kotlinx.android.synthetic.main.fragment_peak_control_player.songTotalTime +import kotlinx.android.synthetic.main.fragment_peak_control_player.* /** * Created by hemanths on 2019-10-04. @@ -98,11 +88,13 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment() { override fun setDark(color: Int) { val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground) if (ColorUtil.isColorLight(colorBg)) { - lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(requireContext(), true) + lastPlaybackControlsColor = + MaterialValueHelper.getSecondaryTextColor(requireContext(), true) lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true) } else { - lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(requireContext(), false) + lastPlaybackControlsColor = + MaterialValueHelper.getPrimaryTextColor(requireContext(), false) lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false) } @@ -122,18 +114,6 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment() { updatePrevNextColor() } - override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - - songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) - songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) - } - private fun updatePlayPauseDrawableState() { if (MusicPlayerRemote.isPlaying) { playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp) @@ -162,15 +142,25 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment() { } } + override fun onUpdateProgressViews(progress: Int, total: Int) { + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) + songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) + songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) + } + override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis) - } + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) } - }) + } } private fun setUpPlayPauseFab() { @@ -196,7 +186,10 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment() { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -204,7 +197,10 @@ class PeakPlayerControlFragment : AbsPlayerControlsFragment() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlaybackControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlaybackControlsFragment.kt index 9d279e90..db9602f9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlaybackControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlaybackControlsFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.plain -import android.animation.ObjectAnimator import android.graphics.PorterDuff import android.os.Bundle import android.view.LayoutInflater @@ -8,8 +7,6 @@ import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator -import android.view.animation.LinearInterpolator -import android.widget.SeekBar import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -18,12 +15,12 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.ripAlpha +import code.name.monkey.retromusic.extensions.setRange import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler -import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil @@ -86,7 +83,11 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { progressViewUpdateHelper = MusicProgressViewUpdateHelper(this) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { return inflater.inflate(R.layout.fragment_plain_controls_fragment, container, false) } @@ -142,10 +143,12 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { val colorBg = ATHUtil.resolveColor(context!!, android.R.attr.colorBackground) if (ColorUtil.isColorLight(colorBg)) { lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(context!!, true) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(context!!, true) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getSecondaryDisabledTextColor(context!!, true) } else { lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(context!!, false) - lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(context!!, false) + lastDisabledPlaybackControlsColor = + MaterialValueHelper.getPrimaryDisabledTextColor(context!!, false) } val colorFinal = if (PreferenceUtil.getInstance(requireContext()).adaptiveColor) { @@ -179,7 +182,10 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN ) - else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + else -> shuffleButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } } @@ -191,7 +197,10 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { when (MusicPlayerRemote.repeatMode) { MusicService.REPEAT_MODE_NONE -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN) + repeatButton.setColorFilter( + lastDisabledPlaybackControlsColor, + PorterDuff.Mode.SRC_IN + ) } MusicService.REPEAT_MODE_ALL -> { repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp) @@ -223,20 +232,6 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { } } - override fun setUpProgressSlider() { - progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() { - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - MusicPlayerRemote.seekTo(progress) - onUpdateProgressViews( - MusicPlayerRemote.songProgressMillis, - MusicPlayerRemote.songDurationMillis - ) - } - } - }) - } - private fun showBonceAnimation() { playPauseButton.apply { clearAnimation() @@ -269,14 +264,23 @@ class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() { } override fun onUpdateProgressViews(progress: Int, total: Int) { - progressSlider.max = total - - val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress) - animator.duration = SLIDER_ANIMATION_TIME - animator.interpolator = LinearInterpolator() - animator.start() - + if (total <= 0) { + return + } + progressSlider.setRange(progress.toFloat(), total.toFloat()) songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong()) songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong()) } + + override fun setUpProgressSlider() { + progressSlider.addOnChangeListener { _, value, fromUser -> + if (fromUser) { + MusicPlayerRemote.seekTo(value.toInt()) + onUpdateProgressViews( + MusicPlayerRemote.songProgressMillis, + MusicPlayerRemote.songDurationMillis + ) + } + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt index e6918903..60c2597c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt @@ -25,12 +25,7 @@ import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ViewUtil -import kotlinx.android.synthetic.main.fragment_tiny_player.playerSongTotalTime -import kotlinx.android.synthetic.main.fragment_tiny_player.playerToolbar -import kotlinx.android.synthetic.main.fragment_tiny_player.progressBar -import kotlinx.android.synthetic.main.fragment_tiny_player.songInfo -import kotlinx.android.synthetic.main.fragment_tiny_player.text -import kotlinx.android.synthetic.main.fragment_tiny_player.title +import kotlinx.android.synthetic.main.fragment_tiny_player.* class TinyPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback { override fun onUpdateProgressViews(progress: Int, total: Int) { @@ -86,10 +81,12 @@ class TinyPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca if (ColorUtil.isColorLight(colorFinal)) { textColorPrimary = MaterialValueHelper.getSecondaryTextColor(requireContext(), true) - textColorPrimaryDisabled = MaterialValueHelper.getSecondaryTextColor(requireContext(), true) + textColorPrimaryDisabled = + MaterialValueHelper.getSecondaryTextColor(requireContext(), true) } else { textColorPrimary = MaterialValueHelper.getPrimaryTextColor(requireContext(), false) - textColorPrimaryDisabled = MaterialValueHelper.getSecondaryTextColor(requireContext(), false) + textColorPrimaryDisabled = + MaterialValueHelper.getSecondaryTextColor(requireContext(), false) } this.lastColor = colorFinal @@ -143,7 +140,11 @@ class TinyPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { return inflater.inflate(R.layout.fragment_tiny_player, container, false) } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt index 5561555b..7ff6e4e2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicProgressViewUpdateHelper.kt @@ -17,7 +17,6 @@ package code.name.monkey.retromusic.helper import android.os.Handler import android.os.Message - class MusicProgressViewUpdateHelper : Handler { private var callback: Callback? = null @@ -54,8 +53,8 @@ class MusicProgressViewUpdateHelper : Handler { private fun refreshProgressViews(): Int { val progressMillis = MusicPlayerRemote.songProgressMillis val totalMillis = MusicPlayerRemote.songDurationMillis - - callback!!.onUpdateProgressViews(progressMillis, totalMillis) + println("$progressMillis $totalMillis") + callback?.onUpdateProgressViews(progressMillis, totalMillis) if (!MusicPlayerRemote.isPlaying) { return intervalPaused diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java index 776e1acd..02cffb40 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java @@ -32,7 +32,7 @@ import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.dialogs.OptionsSheetDialogFragment; import code.name.monkey.retromusic.fragments.AlbumCoverStyle; import code.name.monkey.retromusic.fragments.NowPlayingScreen; -import code.name.monkey.retromusic.fragments.mainactivity.folders.FoldersFragment; +import code.name.monkey.retromusic.fragments.mainactivity.FoldersFragment; import code.name.monkey.retromusic.helper.SortOrder; import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder; import code.name.monkey.retromusic.model.CategoryInfo; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.kt index bc1eb93c..15438f9a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.kt @@ -28,6 +28,7 @@ import androidx.core.view.ViewCompat import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper +import com.google.android.material.slider.Slider object ViewUtil { @@ -47,6 +48,16 @@ object ViewUtil { } } + fun setProgressDrawable(progressSlider: Slider, color: Int, thumbTint: Boolean = false) { + if (thumbTint) { + progressSlider.thumbColor = ColorStateList.valueOf(color) + } + val colorWithAlpha = ColorUtil.withAlpha(color, 0.25f) + progressSlider.haloColor = ColorStateList.valueOf(colorWithAlpha) + progressSlider.trackColorActive = ColorStateList.valueOf(color) + progressSlider.trackColorInactive = ColorStateList.valueOf(colorWithAlpha) + } + fun setProgressDrawable(progressSlider: ProgressBar, newColor: Int) { val layerDrawable = progressSlider.progressDrawable as LayerDrawable diff --git a/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt b/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt index 79371a4a..c81341eb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt +++ b/app/src/main/java/code/name/monkey/retromusic/views/RetroShapeableImageView.kt @@ -16,12 +16,11 @@ package code.name.monkey.retromusic.views import android.content.Context import android.util.AttributeSet import code.name.monkey.retromusic.R -import com.google.android.material.imageview.ExperimentalImageView import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.shape.CornerFamily import com.google.android.material.shape.ShapeAppearanceModel -@ExperimentalImageView + class RetroShapeableImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -29,8 +28,10 @@ class RetroShapeableImageView @JvmOverloads constructor( ) : ShapeableImageView(context, attrs, defStyle) { init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RetroShapeableImageView, defStyle, -1) - val cornerSize = typedArray.getDimension(R.styleable.RetroShapeableImageView_retroCornerSize, 0f); + val typedArray = + context.obtainStyledAttributes(attrs, R.styleable.RetroShapeableImageView, defStyle, -1) + val cornerSize = + typedArray.getDimension(R.styleable.RetroShapeableImageView_retroCornerSize, 0f); shapeAppearanceModel = ShapeAppearanceModel.Builder() .setAllCorners(CornerFamily.ROUNDED, cornerSize) .build() diff --git a/app/src/main/java/code/name/monkey/retromusic/volume/AudioVolumeContentObserver.java b/app/src/main/java/code/name/monkey/retromusic/volume/AudioVolumeContentObserver.java index ae63bce4..f11c4bd3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/volume/AudioVolumeContentObserver.java +++ b/app/src/main/java/code/name/monkey/retromusic/volume/AudioVolumeContentObserver.java @@ -23,13 +23,16 @@ import androidx.annotation.NonNull; public class AudioVolumeContentObserver extends ContentObserver { private final OnAudioVolumeChangedListener mListener; + private final AudioManager mAudioManager; + private final int mAudioStreamType; - private int mLastVolume; + + private float mLastVolume; AudioVolumeContentObserver(@NonNull Handler handler, @NonNull AudioManager audioManager, - int audioStreamType, - @NonNull OnAudioVolumeChangedListener listener) { + int audioStreamType, + @NonNull OnAudioVolumeChangedListener listener) { super(handler); mAudioManager = audioManager; @@ -44,8 +47,8 @@ public class AudioVolumeContentObserver extends ContentObserver { @Override public void onChange(boolean selfChange, Uri uri) { if (mAudioManager != null && mListener != null) { - int maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType); - int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType); + float maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType); + float currentVolume = mAudioManager.getStreamVolume(mAudioStreamType); if (currentVolume != mLastVolume) { mLastVolume = currentVolume; mListener.onAudioVolumeChanged(currentVolume, maxVolume); diff --git a/app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.java b/app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.kt similarity index 78% rename from app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.java rename to app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.kt index 31813ada..3fb91db0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.java +++ b/app/src/main/java/code/name/monkey/retromusic/volume/OnAudioVolumeChangedListener.kt @@ -11,10 +11,8 @@ * 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.volume -package code.name.monkey.retromusic.volume; - -public interface OnAudioVolumeChangedListener { - - void onAudioVolumeChanged(int currentVolume, int maxVolume); +interface OnAudioVolumeChangedListener { + fun onAudioVolumeChanged(currentVolume: Float, maxVolume: Float) } \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_circle_player.xml b/app/src/main/res/layout-land/fragment_circle_player.xml index 466cd3a9..d917d4fb 100644 --- a/app/src/main/res/layout-land/fragment_circle_player.xml +++ b/app/src/main/res/layout-land/fragment_circle_player.xml @@ -54,39 +54,6 @@ app:navigationIcon="@drawable/ic_keyboard_arrow_down_black_24dp" tools:layout_editor_absoluteY="24dp" /> - - - - - - - + app:layout_constraintTop_toBottomOf="@+id/playerToolbar"> - + android:gravity="center" + android:maxLines="1" + android:paddingStart="0dp" + android:paddingEnd="16dp" + android:textAppearance="@style/TextViewHeadline4" + android:textColor="?android:attr/textColorPrimary" + android:textStyle="bold" + tools:text="@tools:sample/lorem/random" /> + - + + + + + + + - - \ No newline at end of file diff --git a/app/src/main/res/layout-xlarge-land/fragment_player_playback_controls.xml b/app/src/main/res/layout-xlarge-land/fragment_player_playback_controls.xml index b4c3855e..657889fc 100755 --- a/app/src/main/res/layout-xlarge-land/fragment_player_playback_controls.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_player_playback_controls.xml @@ -128,17 +128,14 @@ app:layout_constraintStart_toEndOf="@+id/nextButton" app:layout_constraintTop_toTopOf="@+id/playPauseButton"> - diff --git a/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml b/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml index c4d6cffa..52d8c80a 100644 --- a/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_adaptive_player_playback_controls.xml @@ -1,8 +1,8 @@ - - + app:trackHeight="3dp" + tools:valueFrom="0.0" + tools:valueTo="11.0" /> - + tools:progress="20" + tools:valueFrom="0.0" + tools:valueTo="11.0" /> - - + app:layout_constraintEnd_toStartOf="@id/songTotalTime" + app:layout_constraintStart_toEndOf="@id/songCurrentProgress" + app:layout_constraintTop_toBottomOf="@+id/volumeSeekBar" + app:thumbColor="?attr/colorSurface" + app:trackColorInactive="?attr/colorControlNormal" + tools:ignore="RtlHardcoded,UnusedAttribute" + tools:progress="20" /> - + - - - - + - - @@ -46,7 +49,6 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/progressSlider" diff --git a/app/src/main/res/layout/fragment_full_player_controls.xml b/app/src/main/res/layout/fragment_full_player_controls.xml index c6c50f6f..a4f85a11 100644 --- a/app/src/main/res/layout/fragment_full_player_controls.xml +++ b/app/src/main/res/layout/fragment_full_player_controls.xml @@ -83,19 +83,20 @@ tools:ignore="RtlHardcoded,RtlSymmetry" tools:text="00:22" /> - diff --git a/app/src/main/res/layout/fragment_material_playback_controls.xml b/app/src/main/res/layout/fragment_material_playback_controls.xml index 702beb88..9f0200ec 100644 --- a/app/src/main/res/layout/fragment_material_playback_controls.xml +++ b/app/src/main/res/layout/fragment_material_playback_controls.xml @@ -18,26 +18,22 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/progressSlider" tools:ignore="RtlHardcoded,RtlSymmetry" tools:text="00:22" /> - @@ -50,7 +46,6 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/progressSlider" diff --git a/app/src/main/res/layout/fragment_peak_control_player.xml b/app/src/main/res/layout/fragment_peak_control_player.xml index 840100bd..af5ef629 100644 --- a/app/src/main/res/layout/fragment_peak_control_player.xml +++ b/app/src/main/res/layout/fragment_peak_control_player.xml @@ -28,23 +28,19 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/progressSlider" tools:ignore="RtlHardcoded,RtlSymmetry" tools:text="00:22" /> - - @@ -48,7 +44,6 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/progressSlider" diff --git a/app/src/main/res/layout/fragment_player_playback_controls.xml b/app/src/main/res/layout/fragment_player_playback_controls.xml index d7d2ab06..3dd6bb1f 100755 --- a/app/src/main/res/layout/fragment_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_player_playback_controls.xml @@ -18,26 +18,21 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/progressSlider" tools:ignore="RtlHardcoded,RtlSymmetry" tools:text="00:22" /> - @@ -50,7 +45,6 @@ android:minWidth="40dp" android:singleLine="true" android:textColor="?android:attr/textColorSecondary" - android:textSize="12sp" app:layout_constraintBottom_toBottomOf="@+id/progressSlider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/fragment_tiny_player.xml b/app/src/main/res/layout/fragment_tiny_player.xml index bd75804e..83c390a9 100644 --- a/app/src/main/res/layout/fragment_tiny_player.xml +++ b/app/src/main/res/layout/fragment_tiny_player.xml @@ -45,7 +45,6 @@ - - + app:thumbRadius="8dp" + app:trackHeight="2dp" /> #000000 #FFFFFF + #40FFFFFF #1DE9B6 diff --git a/gradle.properties b/gradle.properties index a09e1510..4e2b3ea7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,10 +3,8 @@ org.gradle.daemon=true org.gradle.parallel=true jvmArgs='-Xmx2048m' android.useAndroidX=true -android.enabelR8=true android.enableR8.fullMode=false android.enableJetifier=true android.debug.obsoleteApi=true android.enableBuildCache=true -android.jetifier.blacklist = butterknife.*\\.jar kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 75163178..a2b14700 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Sep 20 00:21:23 IST 2019 +#Mon Feb 24 22:35:41 IST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/liboverscroll/.gitignore b/liboverscroll/.gitignore deleted file mode 100755 index 796b96d1..00000000 --- a/liboverscroll/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/liboverscroll/build.gradle b/liboverscroll/build.gradle deleted file mode 100755 index aad70cc4..00000000 --- a/liboverscroll/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion 29 - defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01' - - testCompile 'junit:junit:4.12' - testCompile "org.mockito:mockito-core:1.9.5" - testCompile "org.robolectric:robolectric:3.0" -} - - -// Running from Gradle tab in IDE would create liboverscroll/build/lib/liboverscroll-sources.jar -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) -} - -// Running from Gradle tab in IDE would create liboverscroll/build/lib/liboverscroll-javadoc.jar -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives javadocJar - archives sourcesJar -} diff --git a/liboverscroll/src/main/AndroidManifest.xml b/liboverscroll/src/main/AndroidManifest.xml deleted file mode 100755 index 6d52f958..00000000 --- a/liboverscroll/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecorator.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecorator.java deleted file mode 100755 index 76eb2ece..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecorator.java +++ /dev/null @@ -1,99 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.MotionEvent; -import android.view.View; - -import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; - -/** - * A concrete implementation of {@link OverScrollBounceEffectDecoratorBase} for a horizontal orientation. - * - * @author amit - */ -public class HorizontalOverScrollBounceEffectDecorator extends OverScrollBounceEffectDecoratorBase { - - protected static class MotionAttributesHorizontal extends MotionAttributes { - - public boolean init(View view, MotionEvent event) { - - // We must have history available to calc the dx. Normally it's there - if it isn't temporarily, - // we declare the event 'invalid' and expect it in consequent events. - if (event.getHistorySize() == 0) { - return false; - } - - // Allow for counter-orientation-direction operations (e.g. item swiping) to run fluently. - final float dy = event.getY(0) - event.getHistoricalY(0, 0); - final float dx = event.getX(0) - event.getHistoricalX(0, 0); - if (Math.abs(dx) < Math.abs(dy)) { - return false; - } - - mAbsOffset = view.getTranslationX(); - mDeltaOffset = dx; - mDir = mDeltaOffset > 0; - - return true; - } - } - - protected static class AnimationAttributesHorizontal extends AnimationAttributes { - - public AnimationAttributesHorizontal() { - mProperty = View.TRANSLATION_X; - } - - @Override - protected void init(View view) { - mAbsOffset = view.getTranslationX(); - mMaxOffset = view.getWidth(); - } - } - - /** - * C'tor, creating the effect with default arguments: - *
Touch-drag ratio in 'forward' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD. - *
Touch-drag ratio in 'backwards' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK. - *
Deceleration factor (for the bounce-back effect) will be set to DEFAULT_DECELERATE_FACTOR. - * - * @param viewAdapter The view's encapsulation. - */ - public HorizontalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter) { - this(viewAdapter, DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD, DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK, DEFAULT_DECELERATE_FACTOR); - } - - /** - * C'tor, creating the effect with explicit arguments. - * @param viewAdapter The view's encapsulation. - * @param touchDragRatioFwd Ratio of touch distance to actual drag distance when in 'forward' direction. - * @param touchDragRatioBck Ratio of touch distance to actual drag distance when in 'backward' - * direction (opposite to initial one). - * @param decelerateFactor Deceleration factor used when decelerating the motion to create the - * bounce-back effect. - */ - public HorizontalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter, - float touchDragRatioFwd, float touchDragRatioBck, float decelerateFactor) { - super(viewAdapter, decelerateFactor, touchDragRatioFwd, touchDragRatioBck); - } - - @Override - protected MotionAttributes createMotionAttributes() { - return new MotionAttributesHorizontal(); - } - - @Override - protected AnimationAttributes createAnimationAttributes() { - return new AnimationAttributesHorizontal(); - } - - @Override - protected void translateView(View view, float offset) { - view.setTranslationX(offset); - } - - @Override - protected void translateViewAndEvent(View view, float offset, MotionEvent event) { - view.setTranslationX(offset); - event.offsetLocation(offset - event.getX(0), 0f); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollDecor.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollDecor.java deleted file mode 100755 index fc6f4eef..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollDecor.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.View; - -/** - * @author amit - */ -public interface IOverScrollDecor { - View getView(); - - void setOverScrollStateListener(IOverScrollStateListener listener); - void setOverScrollUpdateListener(IOverScrollUpdateListener listener); - - /** - * Get the current decorator's runtime state, i.e. one of the values specified by {@link IOverScrollState}. - * @return The state. - */ - int getCurrentState(); - - /** - * Detach the decorator from its associated view, thus disabling it entirely. - * - *

It is best to call this only when over-scroll isn't currently in-effect - i.e. verify that - * getCurrentState()==IOverScrollState.STATE_IDLE as a precondition, or otherwise - * use a state listener previously installed using - * {@link #setOverScrollStateListener(IOverScrollStateListener)}.

- * - *

Note: Upon detachment completion, the view in question will return to the default - * Android over-scroll configuration (i.e. {@link View.OVER_SCROLL_ALWAYS} mode). This can be - * overridden by calling View.setOverScrollMode(mode) immediately thereafter.

- */ - void detach(); -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollState.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollState.java deleted file mode 100755 index 0d1e5ce2..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollState.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.everything.android.ui.overscroll; - -/** - * @author amit - */ -public interface IOverScrollState { - - /** No over-scroll is in-effect. */ - int STATE_IDLE = 0; - - /** User is actively touch-dragging, thus enabling over-scroll at the view's start side. */ - int STATE_DRAG_START_SIDE = 1; - - /** User is actively touch-dragging, thus enabling over-scroll at the view's end side. */ - int STATE_DRAG_END_SIDE = 2; - - /** User has released their touch, thus throwing the view back into place via bounce-back animation. */ - int STATE_BOUNCE_BACK = 3; -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollStateListener.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollStateListener.java deleted file mode 100755 index a6647690..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollStateListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.everything.android.ui.overscroll; - -/** - * A callback-listener enabling over-scroll effect clients to be notified of effect state transitions. - *
Invoked whenever state is transitioned onto one of {@link IOverScrollState#STATE_IDLE}, - * {@link IOverScrollState#STATE_DRAG_START_SIDE}, {@link IOverScrollState#STATE_DRAG_END_SIDE} - * or {@link IOverScrollState#STATE_BOUNCE_BACK}. - * - * @author amit - * - * @see IOverScrollUpdateListener - */ -public interface IOverScrollStateListener { - - /** - * The invoked callback. - * - * @param decor The associated over-scroll 'decorator'. - * @param oldState The old over-scroll state; ID's specified by {@link IOverScrollState}, e.g. - * {@link IOverScrollState#STATE_IDLE}. - * @param newState The new over-scroll state; ID's specified by {@link IOverScrollState}, - * e.g. {@link IOverScrollState#STATE_IDLE}. - */ - void onOverScrollStateChange(IOverScrollDecor decor, int oldState, int newState); -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollUpdateListener.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollUpdateListener.java deleted file mode 100755 index 69d8e5ee..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/IOverScrollUpdateListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.everything.android.ui.overscroll; - -/** - * A callback-listener enabling over-scroll effect clients to subscribe to real-time updates - * of over-scrolling intensity, provided as the view-translation offset from pre-scroll position. - * - * @author amit - * - * @see IOverScrollStateListener - */ -public interface IOverScrollUpdateListener { - - /** - * The invoked callback. - * - * @param decor The associated over-scroll 'decorator'. - * @param state One of: {@link IOverScrollState#STATE_IDLE}, {@link IOverScrollState#STATE_DRAG_START_SIDE}, - * {@link IOverScrollState#STATE_DRAG_START_SIDE} or {@link IOverScrollState#STATE_BOUNCE_BACK}. - * @param offset The currently visible offset created due to over-scroll. - */ - void onOverScrollUpdate(IOverScrollDecor decor, int state, float offset); -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/ListenerStubs.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/ListenerStubs.java deleted file mode 100755 index 8cb22d4d..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/ListenerStubs.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.everything.android.ui.overscroll; - -/** - * @author amit - */ -public interface ListenerStubs { - - class OverScrollStateListenerStub implements IOverScrollStateListener { - @Override - public void onOverScrollStateChange(IOverScrollDecor decor, int oldState, int newState) { } - } - - class OverScrollUpdateListenerStub implements IOverScrollUpdateListener { - @Override - public void onOverScrollUpdate(IOverScrollDecor decor, int state, float offset) { } - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollBounceEffectDecoratorBase.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollBounceEffectDecoratorBase.java deleted file mode 100755 index cd1f5f93..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollBounceEffectDecoratorBase.java +++ /dev/null @@ -1,483 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.util.Log; -import android.util.Property; -import android.view.MotionEvent; -import android.view.View; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; - -import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; -import me.everything.android.ui.overscroll.adapters.RecyclerViewOverScrollDecorAdapter; - -import static me.everything.android.ui.overscroll.IOverScrollState.*; -import static me.everything.android.ui.overscroll.ListenerStubs.*; - -/** - * A standalone view decorator adding over-scroll with a smooth bounce-back effect to (potentially) any view - - * provided that an appropriate {@link IOverScrollDecoratorAdapter} implementation exists / can be written - * for that view type (e.g. {@link RecyclerViewOverScrollDecorAdapter}). - * - *

Design-wise, being a standalone class, this decorator powerfully provides the ability to add - * the over-scroll effect over any view without adjusting the view's implementation. In essence, this - * eliminates the need to repeatedly implement the effect per each view type (list-view, - * recycler-view, image-view, etc.). Therefore, using it is highly recommended compared to other - * more intrusive solutions.

- * - *

Note that this class is abstract, having {@link HorizontalOverScrollBounceEffectDecorator} and - * {@link VerticalOverScrollBounceEffectDecorator} providing concrete implementations that are - * view-orientation specific.

- * - *
- *

Implementation Notes

- * - *

At it's core, the class simply registers itself as a touch-listener over the decorated view and - * intercepts touch events as needed.

- * - *

Internally, it delegates the over-scrolling calculations onto 3 state-based classes: - *

    - *
  1. Idle state - monitors view state and touch events to intercept over-scrolling initiation - * (in which case it hands control over to the Over-scrolling state).
  2. - *
  3. Over-scrolling state - handles motion events to apply the over-scroll effect as users - * interact with the view.
  4. - *
  5. Bounce-back state - runs the bounce-back animation, all-the-while blocking all - * touch events till the animation completes (in which case it hands control back to the idle - * state).
  6. - *
- *

- * - * @author amit - * - * @see RecyclerViewOverScrollDecorAdapter - * @see IOverScrollDecoratorAdapter - */ -public abstract class OverScrollBounceEffectDecoratorBase implements IOverScrollDecor, View.OnTouchListener { - - public static final String TAG = "OverScrollDecor"; - - public static final float DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD = 3f; - public static final float DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK = 1f; - public static final float DEFAULT_DECELERATE_FACTOR = -2f; - - protected static final int MAX_BOUNCE_BACK_DURATION_MS = 800; - protected static final int MIN_BOUNCE_BACK_DURATION_MS = 200; - - protected final OverScrollStartAttributes mStartAttr = new OverScrollStartAttributes(); - protected final IOverScrollDecoratorAdapter mViewAdapter; - - protected final IdleState mIdleState; - protected final OverScrollingState mOverScrollingState; - protected final BounceBackState mBounceBackState; - protected IDecoratorState mCurrentState; - - protected IOverScrollStateListener mStateListener = new OverScrollStateListenerStub(); - protected IOverScrollUpdateListener mUpdateListener = new OverScrollUpdateListenerStub(); - - /** - * When in over-scroll mode, keep track of dragging velocity to provide a smooth slow-down - * for the bounce-back effect. - */ - protected float mVelocity; - - /** - * Motion attributes: keeps data describing current motion event. - *
Orientation agnostic: subclasses provide either horizontal or vertical - * initialization of the agnostic attributes. - */ - protected abstract static class MotionAttributes { - public float mAbsOffset; - public float mDeltaOffset; - public boolean mDir; // True = 'forward', false = 'backwards'. - - protected abstract boolean init(View view, MotionEvent event); - } - - protected static class OverScrollStartAttributes { - protected int mPointerId; - protected float mAbsOffset; - protected boolean mDir; // True = 'forward', false = 'backwards'. - } - - protected abstract static class AnimationAttributes { - public Property mProperty; - public float mAbsOffset; - public float mMaxOffset; - - protected abstract void init(View view); - } - - /** - * Interface of decorator-state delegation classes. Defines states as handles of two fundamental - * touch events: actual movement, up/cancel. - */ - protected interface IDecoratorState { - - /** - * Handle a motion (touch) event. - * - * @param event The event from onTouch. - * @return Return value for onTouch. - */ - boolean handleMoveTouchEvent(MotionEvent event); - - /** - * Handle up / touch-cancel events. - * - * @param event The event from onTouch. - * @return Return value for onTouch. - */ - boolean handleUpOrCancelTouchEvent(MotionEvent event); - - /** - * Handle a transition onto this state, as it becomes 'current' state. - * @param fromState - */ - void handleEntryTransition(IDecoratorState fromState); - - /** - * The client-perspective ID of the state associated with this (internal) one. ID's - * are as specified in {@link IOverScrollState}. - * - * @return The ID, e.g. {@link IOverScrollState#STATE_IDLE}. - */ - int getStateId(); - } - - /** - * Idle state: monitors move events, trying to figure out whether over-scrolling should be - * initiated (i.e. when scrolled further when the view is at one of its displayable ends). - *
When such is the case, it hands over control to the over-scrolling state. - */ - protected class IdleState implements IDecoratorState { - - final MotionAttributes mMoveAttr; - - public IdleState() { - mMoveAttr = createMotionAttributes(); - } - - @Override - public int getStateId() { - return STATE_IDLE; - } - - @Override - public boolean handleMoveTouchEvent(MotionEvent event) { - - final View view = mViewAdapter.getView(); - if (!mMoveAttr.init(view, event)) { - return false; - } - - // Has over-scrolling officially started? - if ((mViewAdapter.isInAbsoluteStart() && mMoveAttr.mDir) || - (mViewAdapter.isInAbsoluteEnd() && !mMoveAttr.mDir)) { - - // Save initial over-scroll attributes for future reference. - mStartAttr.mPointerId = event.getPointerId(0); - mStartAttr.mAbsOffset = mMoveAttr.mAbsOffset; - mStartAttr.mDir = mMoveAttr.mDir; - - issueStateTransition(mOverScrollingState); - return mOverScrollingState.handleMoveTouchEvent(event); - } - - return false; - } - - @Override - public boolean handleUpOrCancelTouchEvent(MotionEvent event) { - return false; - } - - @Override - public void handleEntryTransition(IDecoratorState fromState) { - mStateListener.onOverScrollStateChange(OverScrollBounceEffectDecoratorBase.this, fromState.getStateId(), this.getStateId()); - } - } - - /** - * Handles the actual over-scrolling: thus translating the view according to configuration - * and user interactions, dynamically. - * - *

The state is exited - thus completing over-scroll handling, in one of two cases: - *
When user lets go of the view, it transitions control to the bounce-back state. - *
When user moves the view back onto a potential 'under-scroll' state, it abruptly - * transitions control to the idle-state, so as to return touch-events management to the - * normal over-scroll-less environment (thus preventing under-scrolling and potentially regaining - * regular scrolling). - */ - protected class OverScrollingState implements IDecoratorState { - - protected final float mTouchDragRatioFwd; - protected final float mTouchDragRatioBck; - - final MotionAttributes mMoveAttr; - int mCurrDragState; - - public OverScrollingState(float touchDragRatioFwd, float touchDragRatioBck) { - mMoveAttr = createMotionAttributes(); - mTouchDragRatioFwd = touchDragRatioFwd; - mTouchDragRatioBck = touchDragRatioBck; - } - - @Override - public int getStateId() { - // This is really a single class that implements 2 states, so our ID depends on what - // it was during the last invocation. - return mCurrDragState; - } - - @Override - public boolean handleMoveTouchEvent(MotionEvent event) { - - // Switching 'pointers' (e.g. fingers) on-the-fly isn't supported -- abort over-scroll - // smoothly using the default bounce-back animation in this case. - if (mStartAttr.mPointerId != event.getPointerId(0)) { - issueStateTransition(mBounceBackState); - return true; - } - - final View view = mViewAdapter.getView(); - if (!mMoveAttr.init(view, event)) { - // Keep intercepting the touch event as long as we're still over-scrolling... - return true; - } - - float deltaOffset = mMoveAttr.mDeltaOffset / (mMoveAttr.mDir == mStartAttr.mDir ? mTouchDragRatioFwd : mTouchDragRatioBck); - float newOffset = mMoveAttr.mAbsOffset + deltaOffset; - - // If moved in counter direction onto a potential under-scroll state -- don't. Instead, abort - // over-scrolling abruptly, thus returning control to which-ever touch handlers there - // are waiting (e.g. regular scroller handlers). - if ( (mStartAttr.mDir && !mMoveAttr.mDir && (newOffset <= mStartAttr.mAbsOffset)) || - (!mStartAttr.mDir && mMoveAttr.mDir && (newOffset >= mStartAttr.mAbsOffset)) ) { - translateViewAndEvent(view, mStartAttr.mAbsOffset, event); - mUpdateListener.onOverScrollUpdate(OverScrollBounceEffectDecoratorBase.this, mCurrDragState, 0); - - issueStateTransition(mIdleState); - return true; - } - - if (view.getParent() != null) { - view.getParent().requestDisallowInterceptTouchEvent(true); - } - - long dt = event.getEventTime() - event.getHistoricalEventTime(0); - if (dt > 0) { // Sometimes (though rarely) dt==0 cause originally timing is in nanos, but is presented in millis. - mVelocity = deltaOffset / dt; - } - - translateView(view, newOffset); - mUpdateListener.onOverScrollUpdate(OverScrollBounceEffectDecoratorBase.this, mCurrDragState, newOffset); - - return true; - } - - @Override - public boolean handleUpOrCancelTouchEvent(MotionEvent event) { - issueStateTransition(mBounceBackState); - return false; - } - - @Override - public void handleEntryTransition(IDecoratorState fromState) { - mCurrDragState = (mStartAttr.mDir ? STATE_DRAG_START_SIDE : STATE_DRAG_END_SIDE); - mStateListener.onOverScrollStateChange(OverScrollBounceEffectDecoratorBase.this, fromState.getStateId(), this.getStateId()); - } - } - - /** - * When entered, starts the bounce-back animation. - *
Upon animation completion, transitions control onto the idle state; Does so by - * registering itself as an animation listener. - *
In the meantime, blocks (intercepts) all touch events. - */ - protected class BounceBackState implements IDecoratorState, Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { - - protected final Interpolator mBounceBackInterpolator = new DecelerateInterpolator(); - protected final float mDecelerateFactor; - protected final float mDoubleDecelerateFactor; - - protected final AnimationAttributes mAnimAttributes; - - public BounceBackState(float decelerateFactor) { - mDecelerateFactor = decelerateFactor; - mDoubleDecelerateFactor = 2f * decelerateFactor; - - mAnimAttributes = createAnimationAttributes(); - } - - @Override - public int getStateId() { - return STATE_BOUNCE_BACK; - } - - @Override - public void handleEntryTransition(IDecoratorState fromState) { - - mStateListener.onOverScrollStateChange(OverScrollBounceEffectDecoratorBase.this, fromState.getStateId(), this.getStateId()); - - Animator bounceBackAnim = createAnimator(); - bounceBackAnim.addListener(this); - - bounceBackAnim.start(); - } - - @Override - public boolean handleMoveTouchEvent(MotionEvent event) { - // Flush all touches down the drain till animation is over. - return true; - } - - @Override - public boolean handleUpOrCancelTouchEvent(MotionEvent event) { - // Flush all touches down the drain till animation is over. - return true; - } - - @Override - public void onAnimationEnd(Animator animation) { - issueStateTransition(mIdleState); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mUpdateListener.onOverScrollUpdate(OverScrollBounceEffectDecoratorBase.this, STATE_BOUNCE_BACK, (Float) animation.getAnimatedValue()); - } - - @Override public void onAnimationStart(Animator animation) {} - @Override public void onAnimationCancel(Animator animation) {} - @Override public void onAnimationRepeat(Animator animation) {} - - protected Animator createAnimator() { - - final View view = mViewAdapter.getView(); - - mAnimAttributes.init(view); - - // Set up a low-duration slow-down animation IN the drag direction. - - // Exception: If wasn't dragging in 'forward' direction (or velocity=0 -- i.e. not dragging at all), - // skip slow-down anim directly to the bounce-back. - if (mVelocity == 0f || (mVelocity < 0 && mStartAttr.mDir) || (mVelocity > 0 && !mStartAttr.mDir)) { - return createBounceBackAnimator(mAnimAttributes.mAbsOffset); - } - - // dt = (Vt - Vo) / a; Vt=0 ==> dt = -Vo / a - float slowdownDuration = -mVelocity / mDecelerateFactor; - slowdownDuration = (slowdownDuration < 0 ? 0 : slowdownDuration); // Happens in counter-direction dragging - - // dx = (Vt^2 - Vo^2) / 2a; Vt=0 ==> dx = -Vo^2 / 2a - float slowdownDistance = -mVelocity * mVelocity / mDoubleDecelerateFactor; - float slowdownEndOffset = mAnimAttributes.mAbsOffset + slowdownDistance; - - ObjectAnimator slowdownAnim = createSlowdownAnimator(view, (int) slowdownDuration, slowdownEndOffset); - - // Set up the bounce back animation, bringing the view back into the original, pre-overscroll position (translation=0). - - ObjectAnimator bounceBackAnim = createBounceBackAnimator(slowdownEndOffset); - - // Play the 2 animations as a sequence. - AnimatorSet wholeAnim = new AnimatorSet(); - wholeAnim.playSequentially(slowdownAnim, bounceBackAnim); - return wholeAnim; - } - - protected ObjectAnimator createSlowdownAnimator(View view, int slowdownDuration, float slowdownEndOffset) { - ObjectAnimator slowdownAnim = ObjectAnimator.ofFloat(view, mAnimAttributes.mProperty, slowdownEndOffset); - slowdownAnim.setDuration(slowdownDuration); - slowdownAnim.setInterpolator(mBounceBackInterpolator); - slowdownAnim.addUpdateListener(this); - return slowdownAnim; - } - - protected ObjectAnimator createBounceBackAnimator(float startOffset) { - - final View view = mViewAdapter.getView(); - - // Duration is proportional to the view's size. - float bounceBackDuration = (Math.abs(startOffset) / mAnimAttributes.mMaxOffset) * MAX_BOUNCE_BACK_DURATION_MS; - ObjectAnimator bounceBackAnim = ObjectAnimator.ofFloat(view, mAnimAttributes.mProperty, mStartAttr.mAbsOffset); - bounceBackAnim.setDuration(Math.max((int) bounceBackDuration, MIN_BOUNCE_BACK_DURATION_MS)); - bounceBackAnim.setInterpolator(mBounceBackInterpolator); - bounceBackAnim.addUpdateListener(this); - return bounceBackAnim; - } - } - - public OverScrollBounceEffectDecoratorBase(IOverScrollDecoratorAdapter viewAdapter, float decelerateFactor, float touchDragRatioFwd, float touchDragRatioBck) { - mViewAdapter = viewAdapter; - - mBounceBackState = new BounceBackState(decelerateFactor); - mOverScrollingState = new OverScrollingState(touchDragRatioFwd, touchDragRatioBck); - mIdleState = new IdleState(); - - mCurrentState = mIdleState; - - attach(); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_MOVE: - return mCurrentState.handleMoveTouchEvent(event); - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - return mCurrentState.handleUpOrCancelTouchEvent(event); - } - - return false; - } - - @Override - public void setOverScrollStateListener(IOverScrollStateListener listener) { - mStateListener = (listener != null ? listener : new OverScrollStateListenerStub()); - } - - @Override - public void setOverScrollUpdateListener(IOverScrollUpdateListener listener) { - mUpdateListener = (listener != null ? listener : new OverScrollUpdateListenerStub()); - } - - @Override - public int getCurrentState() { - return mCurrentState.getStateId(); - } - - @Override - public View getView() { - return mViewAdapter.getView(); - } - - protected void issueStateTransition(IDecoratorState state) { - IDecoratorState oldState = mCurrentState; - mCurrentState = state; - mCurrentState.handleEntryTransition(oldState); - } - - protected void attach() { - getView().setOnTouchListener(this); - getView().setOverScrollMode(View.OVER_SCROLL_NEVER); - } - - @Override - public void detach() { - if (mCurrentState != mIdleState) { - Log.w(TAG, "Decorator detached while over-scroll is in effect. You might want to add a precondition of that getCurrentState()==STATE_IDLE, first."); - } - getView().setOnTouchListener(null); - getView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } - - protected abstract MotionAttributes createMotionAttributes(); - protected abstract AnimationAttributes createAnimationAttributes(); - protected abstract void translateView(View view, float offset); - protected abstract void translateViewAndEvent(View view, float offset, MotionEvent event); -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollDecoratorHelper.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollDecoratorHelper.java deleted file mode 100755 index 89510253..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/OverScrollDecoratorHelper.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.View; -import android.widget.ScrollView; -import androidx.annotation.NonNull; -import androidx.core.widget.NestedScrollView; -import androidx.recyclerview.widget.RecyclerView; -import me.everything.android.ui.overscroll.adapters.NestedScrollViewOverScrollDecorAdapter; -import me.everything.android.ui.overscroll.adapters.RecyclerViewOverScrollDecorAdapter; -import me.everything.android.ui.overscroll.adapters.ScrollViewOverScrollDecorAdapter; -import me.everything.android.ui.overscroll.adapters.StaticOverScrollDecorAdapter; - -/** - * @author amit - */ -public class OverScrollDecoratorHelper { - - public static final int ORIENTATION_VERTICAL = 0; - - public static final int ORIENTATION_HORIZONTAL = 1; - - /** - * Set up the over-scroll effect over a specified {@link RecyclerView} view. - *
Only recycler-views using native Android layout managers (i.e. {@link LinearLayoutManager}, - * {@link GridLayoutManager} and {@link StaggeredGridLayoutManager}) are currently supported - * by this convenience method. - * - * @param recyclerView The view. - * @param orientation Either {@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL}. - * @return The over-scroll effect 'decorator', enabling further effect configuration. - */ - @NonNull - public static IOverScrollDecor setUpOverScroll(@NonNull RecyclerView recyclerView, int orientation) { - switch (orientation) { - case ORIENTATION_HORIZONTAL: - return new HorizontalOverScrollBounceEffectDecorator( - new RecyclerViewOverScrollDecorAdapter(recyclerView)); - case ORIENTATION_VERTICAL: - return new VerticalOverScrollBounceEffectDecorator( - new RecyclerViewOverScrollDecorAdapter(recyclerView)); - default: - throw new IllegalArgumentException("orientation"); - } - } - - @NonNull - public static IOverScrollDecor setUpOverScroll(@NonNull ScrollView scrollView) { - return new VerticalOverScrollBounceEffectDecorator(new ScrollViewOverScrollDecorAdapter(scrollView)); - } - - @NonNull - public static IOverScrollDecor setUpOverScroll(@NonNull NestedScrollView nestedScrollView) { - return new VerticalOverScrollBounceEffectDecorator( - new NestedScrollViewOverScrollDecorAdapter(nestedScrollView)); - } - - /** - * Set up the over-scroll over a generic view, assumed to always be over-scroll ready (e.g. - * a plain text field, image view). - * - * @param view The view. - * @param orientation One of {@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL}. - * @return The over-scroll effect 'decorator', enabling further effect configuration. - */ - public static IOverScrollDecor setUpStaticOverScroll(View view, int orientation) { - switch (orientation) { - case ORIENTATION_HORIZONTAL: - return new HorizontalOverScrollBounceEffectDecorator(new StaticOverScrollDecorAdapter(view)); - - case ORIENTATION_VERTICAL: - return new VerticalOverScrollBounceEffectDecorator(new StaticOverScrollDecorAdapter(view)); - - default: - throw new IllegalArgumentException("orientation"); - } - } - -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecorator.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecorator.java deleted file mode 100755 index bc6ce620..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecorator.java +++ /dev/null @@ -1,100 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.MotionEvent; -import android.view.View; -import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; - -/** - * A concrete implementation of {@link OverScrollBounceEffectDecoratorBase} for a vertical orientation. - * - * @author amit - */ -public class VerticalOverScrollBounceEffectDecorator extends OverScrollBounceEffectDecoratorBase { - - protected static class MotionAttributesVertical extends MotionAttributes { - - public boolean init(View view, MotionEvent event) { - - // We must have history available to calc the dx. Normally it's there - if it isn't temporarily, - // we declare the event 'invalid' and expect it in consequent events. - if (event.getHistorySize() == 0) { - return false; - } - - // Allow for counter-orientation-direction operations (e.g. item swiping) to run fluently. - final float dy = event.getY(0) - event.getHistoricalY(0, 0); - final float dx = event.getX(0) - event.getHistoricalX(0, 0); - if (Math.abs(dx) > Math.abs(dy)) { - return false; - } - - mAbsOffset = view.getTranslationY(); - mDeltaOffset = dy; - mDir = mDeltaOffset > 0; - - return true; - } - } - - protected static class AnimationAttributesVertical extends AnimationAttributes { - - public AnimationAttributesVertical() { - mProperty = View.TRANSLATION_Y; - } - - @Override - protected void init(View view) { - mAbsOffset = view.getTranslationY(); - mMaxOffset = view.getHeight(); - } - } - - /** - * C'tor, creating the effect with default arguments: - *
Touch-drag ratio in 'forward' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD. - *
Touch-drag ratio in 'backwards' direction will be set to DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK. - *
Deceleration factor (for the bounce-back effect) will be set to DEFAULT_DECELERATE_FACTOR. - * - * @param viewAdapter The view's encapsulation. - */ - public VerticalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter) { - this(viewAdapter, DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD, DEFAULT_TOUCH_DRAG_MOVE_RATIO_BCK, - DEFAULT_DECELERATE_FACTOR); - } - - /** - * C'tor, creating the effect with explicit arguments. - * - * @param viewAdapter The view's encapsulation. - * @param touchDragRatioFwd Ratio of touch distance to actual drag distance when in 'forward' direction. - * @param touchDragRatioBck Ratio of touch distance to actual drag distance when in 'backward' - * direction (opposite to initial one). - * @param decelerateFactor Deceleration factor used when decelerating the motion to create the - * bounce-back effect. - */ - public VerticalOverScrollBounceEffectDecorator(IOverScrollDecoratorAdapter viewAdapter, - float touchDragRatioFwd, float touchDragRatioBck, float decelerateFactor) { - super(viewAdapter, decelerateFactor, touchDragRatioFwd, touchDragRatioBck); - } - - @Override - protected AnimationAttributes createAnimationAttributes() { - return new AnimationAttributesVertical(); - } - - @Override - protected MotionAttributes createMotionAttributes() { - return new MotionAttributesVertical(); - } - - @Override - protected void translateView(View view, float offset) { - view.setTranslationY(offset); - } - - @Override - protected void translateViewAndEvent(View view, float offset, MotionEvent event) { - view.setTranslationY(offset); - event.offsetLocation(offset - event.getY(0), 0f); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/AbsListViewOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/AbsListViewOverScrollDecorAdapter.java deleted file mode 100755 index 9437fd71..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/AbsListViewOverScrollDecorAdapter.java +++ /dev/null @@ -1,57 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; -import android.widget.AbsListView; - -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; -import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; - -/** - * An adapter to enable over-scrolling over object of {@link AbsListView}, namely {@link - * android.widget.ListView} and it's extensions, and {@link android.widget.GridView}. - * - * @author amit - * - * @see HorizontalOverScrollBounceEffectDecorator - * @see VerticalOverScrollBounceEffectDecorator - */ -public class AbsListViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - protected final AbsListView mView; - - public AbsListViewOverScrollDecorAdapter(AbsListView view) { - mView = view; - } - - @Override - public View getView() { - return mView; - } - - @Override - public boolean isInAbsoluteStart() { - return mView.getChildCount() > 0 && !canScrollListUp(); - } - - @Override - public boolean isInAbsoluteEnd() { - return mView.getChildCount() > 0 && !canScrollListDown(); - } - - public boolean canScrollListUp() { - // Ported from AbsListView#canScrollList() which isn't compatible to all API levels - final int firstTop = mView.getChildAt(0).getTop(); - final int firstPosition = mView.getFirstVisiblePosition(); - return firstPosition > 0 || firstTop < mView.getListPaddingTop(); - } - - public boolean canScrollListDown() { - // Ported from AbsListView#canScrollList() which isn't compatible to all API levels - final int childCount = mView.getChildCount(); - final int itemsCount = mView.getCount(); - final int firstPosition = mView.getFirstVisiblePosition(); - final int lastPosition = firstPosition + childCount; - final int lastBottom = mView.getChildAt(childCount - 1).getBottom(); - return lastPosition < itemsCount || lastBottom > mView.getHeight() - mView.getListPaddingBottom(); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/HorizontalScrollViewOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/HorizontalScrollViewOverScrollDecorAdapter.java deleted file mode 100755 index 72c873c5..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/HorizontalScrollViewOverScrollDecorAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; -import android.widget.HorizontalScrollView; - -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; -import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; - -/** - * An adapter that enables over-scrolling support over a {@link HorizontalScrollView}. - *
Seeing that {@link HorizontalScrollView} only supports horizontal scrolling, this adapter - * should only be used with a {@link HorizontalOverScrollBounceEffectDecorator}. - * - * @author amit - * - * @see HorizontalOverScrollBounceEffectDecorator - * @see VerticalOverScrollBounceEffectDecorator - */ -public class HorizontalScrollViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - protected final HorizontalScrollView mView; - - public HorizontalScrollViewOverScrollDecorAdapter(HorizontalScrollView view) { - mView = view; - } - - @Override - public View getView() { - return mView; - } - - @Override - public boolean isInAbsoluteStart() { - return !mView.canScrollHorizontally(-1); - } - - @Override - public boolean isInAbsoluteEnd() { - return !mView.canScrollHorizontally(1); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/IOverScrollDecoratorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/IOverScrollDecoratorAdapter.java deleted file mode 100755 index 212156cb..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/IOverScrollDecoratorAdapter.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; - -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; - -/** - * @author amitd - * - * @see HorizontalOverScrollBounceEffectDecorator - */ -public interface IOverScrollDecoratorAdapter { - - View getView(); - - /** - * Is view in it's absolute start position - such that a negative over-scroll can potentially - * be initiated. For example, in list-views, this is synonymous with the first item being - * fully visible. - * - * @return Whether in absolute start position. - */ - boolean isInAbsoluteStart(); - - /** - * Is view in it's absolute end position - such that an over-scroll can potentially - * be initiated. For example, in list-views, this is synonymous with the last item being - * fully visible. - * - * @return Whether in absolute end position. - */ - boolean isInAbsoluteEnd(); -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/NestedScrollViewOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/NestedScrollViewOverScrollDecorAdapter.java deleted file mode 100644 index 1d8e6b6c..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/NestedScrollViewOverScrollDecorAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; -import androidx.annotation.NonNull; -import androidx.core.widget.NestedScrollView; - -public class NestedScrollViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - protected final NestedScrollView mView; - - public NestedScrollViewOverScrollDecorAdapter(@NonNull NestedScrollView view) { - this.mView = view; - } - - public View getView() { - return this.mView; - } - - public boolean isInAbsoluteEnd() { - return !this.mView.canScrollVertically(1); - } - - public boolean isInAbsoluteStart() { - return !this.mView.canScrollVertically(-1); - } -} \ No newline at end of file diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/RecyclerViewOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/RecyclerViewOverScrollDecorAdapter.java deleted file mode 100755 index bd1f38b3..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/RecyclerViewOverScrollDecorAdapter.java +++ /dev/null @@ -1,237 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.graphics.Canvas; -import android.view.View; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.StaggeredGridLayoutManager; -import java.util.List; -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; -import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; - -/** - * @author amitd - * @see HorizontalOverScrollBounceEffectDecorator - * @see VerticalOverScrollBounceEffectDecorator - */ -public class RecyclerViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - private static class ItemTouchHelperCallbackWrapper extends ItemTouchHelper.Callback { - - final ItemTouchHelper.Callback mCallback; - - private ItemTouchHelperCallbackWrapper(ItemTouchHelper.Callback callback) { - mCallback = callback; - } - - @Override - public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, - RecyclerView.ViewHolder target) { - return mCallback.canDropOver(recyclerView, current, target); - } - - @Override - public RecyclerView.ViewHolder chooseDropTarget(RecyclerView.ViewHolder selected, - List dropTargets, int curX, int curY) { - return mCallback.chooseDropTarget(selected, dropTargets, curX, curY); - } - - @Override - public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - mCallback.clearView(recyclerView, viewHolder); - } - - @Override - public int convertToAbsoluteDirection(int flags, int layoutDirection) { - return mCallback.convertToAbsoluteDirection(flags, layoutDirection); - } - - @Override - public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, - float animateDy) { - return mCallback.getAnimationDuration(recyclerView, animationType, animateDx, animateDy); - } - - @Override - public int getBoundingBoxMargin() { - return mCallback.getBoundingBoxMargin(); - } - - @Override - public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) { - return mCallback.getMoveThreshold(viewHolder); - } - - @Override - public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - return mCallback.getMovementFlags(recyclerView, viewHolder); - } - - @Override - public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) { - return mCallback.getSwipeThreshold(viewHolder); - } - - @Override - public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, - int totalSize, long msSinceStartScroll) { - return mCallback.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, - msSinceStartScroll); - } - - @Override - public boolean isItemViewSwipeEnabled() { - return mCallback.isItemViewSwipeEnabled(); - } - - @Override - public boolean isLongPressDragEnabled() { - return mCallback.isLongPressDragEnabled(); - } - - @Override - public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, - float dY, int actionState, boolean isCurrentlyActive) { - mCallback.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); - } - - @Override - public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, - float dY, int actionState, boolean isCurrentlyActive) { - mCallback.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); - } - - @Override - public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, - RecyclerView.ViewHolder target) { - return mCallback.onMove(recyclerView, viewHolder, target); - } - - @Override - public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, - RecyclerView.ViewHolder target, int toPos, int x, int y) { - mCallback.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y); - } - - @Override - public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { - mCallback.onSelectedChanged(viewHolder, actionState); - } - - @Override - public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - mCallback.onSwiped(viewHolder, direction); - } - } - - protected class ImplHorizLayout implements Impl { - - @Override - public boolean isInAbsoluteEnd() { - return !mRecyclerView.canScrollHorizontally(1); - } - - @Override - public boolean isInAbsoluteStart() { - return !mRecyclerView.canScrollHorizontally(-1); - } - } - - protected class ImplVerticalLayout implements Impl { - - @Override - public boolean isInAbsoluteEnd() { - return !mRecyclerView.canScrollVertically(1); - } - - @Override - public boolean isInAbsoluteStart() { - return !mRecyclerView.canScrollVertically(-1); - } - } - - /** - * A delegation of the adapter implementation of this view that should provide the processing - * of {@link #isInAbsoluteStart()} and {@link #isInAbsoluteEnd()}. Essentially needed simply - * because the implementation depends on the layout manager implementation being used. - */ - protected interface Impl { - - boolean isInAbsoluteEnd(); - - boolean isInAbsoluteStart(); - } - - protected final Impl mImpl; - - protected boolean mIsItemTouchInEffect = false; - - protected final RecyclerView mRecyclerView; - - public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView) { - - mRecyclerView = recyclerView; - - final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); - if (layoutManager instanceof LinearLayoutManager || - layoutManager instanceof StaggeredGridLayoutManager) { - final int orientation = - (layoutManager instanceof LinearLayoutManager - ? ((LinearLayoutManager) layoutManager).getOrientation() - : ((StaggeredGridLayoutManager) layoutManager).getOrientation()); - - if (orientation == LinearLayoutManager.HORIZONTAL) { - mImpl = new ImplHorizLayout(); - } else { - mImpl = new ImplVerticalLayout(); - } - } else { - throw new IllegalArgumentException( - "Recycler views with custom layout managers are not supported by this adapter out of the box." + - "Try implementing and providing an explicit 'impl' parameter to the other c'tors, or otherwise create a custom adapter subclass of your own."); - } - } - - public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, Impl impl) { - mRecyclerView = recyclerView; - mImpl = impl; - } - - public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, - ItemTouchHelper.Callback itemTouchHelperCallback) { - this(recyclerView); - setUpTouchHelperCallback(itemTouchHelperCallback); - } - - public RecyclerViewOverScrollDecorAdapter(RecyclerView recyclerView, Impl impl, - ItemTouchHelper.Callback itemTouchHelperCallback) { - this(recyclerView, impl); - setUpTouchHelperCallback(itemTouchHelperCallback); - } - - @Override - public View getView() { - return mRecyclerView; - } - - @Override - public boolean isInAbsoluteEnd() { - return !mIsItemTouchInEffect && mImpl.isInAbsoluteEnd(); - } - - @Override - public boolean isInAbsoluteStart() { - return !mIsItemTouchInEffect && mImpl.isInAbsoluteStart(); - } - - protected void setUpTouchHelperCallback(final ItemTouchHelper.Callback itemTouchHelperCallback) { - new ItemTouchHelper(new ItemTouchHelperCallbackWrapper(itemTouchHelperCallback) { - @Override - public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { - mIsItemTouchInEffect = actionState != 0; - super.onSelectedChanged(viewHolder, actionState); - } - }).attachToRecyclerView(mRecyclerView); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ScrollViewOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ScrollViewOverScrollDecorAdapter.java deleted file mode 100755 index 2c4c79cb..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/ScrollViewOverScrollDecorAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; -import android.widget.ScrollView; - -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; -import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; - -/** - * An adapter that enables over-scrolling over a {@link ScrollView}. - *
Seeing that {@link ScrollView} only supports vertical scrolling, this adapter - * should only be used with a {@link VerticalOverScrollBounceEffectDecorator}. For horizontal - * over-scrolling, use {@link HorizontalScrollViewOverScrollDecorAdapter} in conjunction with - * a {@link android.widget.HorizontalScrollView}. - * - * @author amit - * - * @see HorizontalOverScrollBounceEffectDecorator - * @see VerticalOverScrollBounceEffectDecorator - */ -public class ScrollViewOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - protected final ScrollView mView; - - public ScrollViewOverScrollDecorAdapter(ScrollView view) { - mView = view; - } - - @Override - public View getView() { - return mView; - } - - @Override - public boolean isInAbsoluteStart() { - return !mView.canScrollVertically(-1); - } - - @Override - public boolean isInAbsoluteEnd() { - return !mView.canScrollVertically(1); - } -} diff --git a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/StaticOverScrollDecorAdapter.java b/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/StaticOverScrollDecorAdapter.java deleted file mode 100755 index 95395f82..00000000 --- a/liboverscroll/src/main/java/me/everything/android/ui/overscroll/adapters/StaticOverScrollDecorAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.everything.android.ui.overscroll.adapters; - -import android.view.View; - -import me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator; -import me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator; - -/** - * A static adapter for views that are ALWAYS over-scroll-able (e.g. image view). - * - * @author amit - * - * @see HorizontalOverScrollBounceEffectDecorator - * @see VerticalOverScrollBounceEffectDecorator - */ -public class StaticOverScrollDecorAdapter implements IOverScrollDecoratorAdapter { - - protected final View mView; - - public StaticOverScrollDecorAdapter(View view) { - mView = view; - } - - @Override - public View getView() { - return mView; - } - - @Override - public boolean isInAbsoluteStart() { - return true; - } - - @Override - public boolean isInAbsoluteEnd() { - return true; - } -} diff --git a/liboverscroll/src/test/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecoratorTest.java b/liboverscroll/src/test/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecoratorTest.java deleted file mode 100755 index cddd6d21..00000000 --- a/liboverscroll/src/test/java/me/everything/android/ui/overscroll/HorizontalOverScrollBounceEffectDecoratorTest.java +++ /dev/null @@ -1,640 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.MotionEvent; -import android.view.View; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; - -import static me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator.DEFAULT_DECELERATE_FACTOR; -import static me.everything.android.ui.overscroll.HorizontalOverScrollBounceEffectDecorator.DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; -import static me.everything.android.ui.overscroll.IOverScrollState.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -/** - * @author amitd - */ -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class HorizontalOverScrollBounceEffectDecoratorTest { - - View mView; - IOverScrollDecoratorAdapter mViewAdapter; - IOverScrollStateListener mStateListener; - IOverScrollUpdateListener mUpdateListener; - - @Before - public void setUp() throws Exception { - mView = mock(View.class); - mViewAdapter = mock(IOverScrollDecoratorAdapter.class); - when(mViewAdapter.getView()).thenReturn(mView); - - mStateListener = mock(IOverScrollStateListener.class); - mUpdateListener = mock(IOverScrollUpdateListener.class); - } - - @Test - public void detach_decoratorIsAttached_detachFromView() throws Exception { - - // Arrange - - HorizontalOverScrollBounceEffectDecorator uut = new HorizontalOverScrollBounceEffectDecorator(mViewAdapter); - - // Act - - uut.detach(); - - // Assert - - verify(mView).setOnTouchListener(eq((View.OnTouchListener) null)); - verify(mView).setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } - - @Test - public void detach_overScrollInEffect_detachFromView() throws Exception { - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, createShortRightMoveEvent()); - - // Act - - uut.detach(); - - // Assert - - verify(mView).setOnTouchListener(eq((View.OnTouchListener) null)); - verify(mView).setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } - - /* - * Move-action event - */ - - @Test - public void onTouchMoveAction_notInViewEnds_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortRightMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_dragRightInLeftEnd_overscrollRight() throws Exception { - - // Arrange - - MotionEvent event = createShortRightMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - final boolean ret = uut.onTouch(mView, event); - - // Assert - - final float expectedTransX = (event.getX() - event.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationX(expectedTransX); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransX)); - } - - @Test - public void onTouchMoveAction_dragLeftInRightEnd_overscrollLeft() throws Exception { - - // Arrange - - MotionEvent event = createShortLeftMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - final boolean ret = uut.onTouch(mView, event); - - // Assert - - final float expectedTransX = (event.getX() - event.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationX(expectedTransX); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransX)); - } - - @Test - public void onTouchMoveAction_dragLeftInLeftEnd_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortLeftMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - final boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_dragRightInRightEnd_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortRightMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_2ndRightDragInLeftEnd_overscrollRightFurther() throws Exception { - - // Arrange - - // Bring UUT to a right-overscroll state - MotionEvent event1 = createShortRightMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, event1); - reset(mView); - - // Create 2nd right-drag event - MotionEvent event2 = createLongRightMoveEvent(); - - // Act - - final boolean ret = uut.onTouch(mView, event2); - - // Assert - - final float expectedTransX1 = (event1.getX() - event1.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - final float expectedTransX2 = (event2.getX() - event2.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationX(expectedTransX2); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransX1)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransX2)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_2ndLeftDragInRightEnd_overscrollLeftFurther() throws Exception { - - // Arrange - - // Bring UUT to a left-overscroll state - MotionEvent event1 = createShortLeftMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, event1); - reset(mView); - - // Create 2nd left-drag event - MotionEvent event2 = createLongLeftMoveEvent(); - - // Act - - final boolean ret = uut.onTouch(mView, event2); - - // Assert - - final float expectedTransX1 = (event1.getX() - event1.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - final float expectedTransX2 = (event2.getX() - event2.getHistoricalX(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationX(expectedTransX2); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransX1)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransX2)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /** - * When over-scroll has already started (to the right in this case) and suddenly the user changes - * their mind and scrolls a bit in the other direction: - *
We expect the touch to still be intercepted in that case, and the overscroll to - * remain in effect. - */ - @Test - public void onTouchMoveAction_dragLeftWhenRightOverscolled_continueOverscrollingLeft() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a right-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveRight = createLongRightMoveEvent(); - uut.onTouch(mView, eventMoveRight); - reset(mView); - float startTransX = (eventMoveRight.getX() - eventMoveRight.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the left-drag event - MotionEvent eventMoveLeft = createShortLeftMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveLeft); - - // Assert - - float expectedTransX = startTransX + - (eventMoveLeft.getX() - eventMoveLeft.getHistoricalX(0)) / touchDragRatioBck; - verify(mView).setTranslationX(expectedTransX); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransX)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /** - * When over-scroll has already started (to the left in this case) and suddenly the user changes - * their mind and scrolls a bit in the other direction: - *
We expect the touch to still be intercepted in that case, and the overscroll to remain in effect. - */ - @Test - public void onTouchMoveAction_dragRightWhenLeftOverscolled_continueOverscrollingRight() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a left-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveLeft = createLongLeftMoveEvent(); - uut.onTouch(mView, eventMoveLeft); - reset(mView); - - float startTransX = (eventMoveLeft.getX() - eventMoveLeft.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the right-drag event - MotionEvent eventMoveRight = createShortRightMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveRight); - - // Assert - - float expectedTransX = startTransX + (eventMoveRight.getX() - eventMoveRight.getHistoricalX(0)) / touchDragRatioBck; - verify(mView).setTranslationX(expectedTransX); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransX)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_undragWhenRightOverscrolled_endOverscrolling() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a right-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveRight = createLongRightMoveEvent(); - uut.onTouch(mView, eventMoveRight); - reset(mView); - float startTransX = (eventMoveRight.getX() - eventMoveRight.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the left-drag event - MotionEvent eventMoveLeft = createLongLeftMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveLeft); - - // Assert - - verify(mView).setTranslationX(0); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - // State-change listener invoked to say drag-on and drag-off (idle). - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_DRAG_START_SIDE), eq(STATE_IDLE)); - verify(mStateListener, times(2)).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(0f)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_undragWhenLeftOverscrolled_endOverscrolling() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a left-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveLeft = createLongLeftMoveEvent(); - uut.onTouch(mView, eventMoveLeft); - reset(mView); - float startTransX = (eventMoveLeft.getX() - eventMoveLeft.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the left-drag event - MotionEvent eventMoveRight = createLongRightMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveRight); - - // Assert - - verify(mView).setTranslationX(0); - verify(mView, never()).setTranslationY(anyFloat()); - assertTrue(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - // State-change listener invoked to say drag-on and drag-off (idle). - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_DRAG_END_SIDE), eq(STATE_IDLE)); - verify(mStateListener, times(2)).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(0f)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - - /* - * Up action event - */ - - @Test - public void onTouchUpAction_eventWhenNotOverscrolled_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createDefaultUpActionEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /** - * TODO: Make this work using a decent animation shadows / newer Robolectric - * @throws Exception - */ - @Ignore - @Test - public void onTouchUpAction_eventWhenLeftOverscrolling_smoothScrollBackToRightEnd() throws Exception { - - // Arrange - - // Bring UUT to a left-overscroll state - MotionEvent moveEvent = createShortLeftMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - HorizontalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, moveEvent); - reset(mView); - - // Make the view as though it's been moved by the move event - float viewX = moveEvent.getX(); - when(mView.getTranslationX()).thenReturn(viewX); - - MotionEvent upEvent = createDefaultUpActionEvent(); - - // Act - - boolean ret = uut.onTouch(mView, upEvent); - - // Assert - - assertTrue(ret); - - verify(mView, atLeastOnce()).setTranslationX(anyFloat()); - } - - protected MotionEvent createShortRightMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(100f); - when(event.getY()).thenReturn(200f); - when(event.getX(0)).thenReturn(100f); - when(event.getY(0)).thenReturn(200f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(80f); - when(event.getHistoricalY(eq(0))).thenReturn(190f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(80f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(190f); - return event; - } - - protected MotionEvent createLongRightMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(150f); - when(event.getY()).thenReturn(250f); - when(event.getX(0)).thenReturn(150f); - when(event.getY(0)).thenReturn(250f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(100f); - when(event.getHistoricalY(eq(0))).thenReturn(200f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(100f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(200f); - return event; - } - - protected MotionEvent createShortLeftMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(100f); - when(event.getY()).thenReturn(200f); - when(event.getX(0)).thenReturn(100f); - when(event.getY(0)).thenReturn(200f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(120f); - when(event.getHistoricalY(eq(0))).thenReturn(220f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(120f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(220f); - return event; - } - - protected MotionEvent createLongLeftMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(50f); - when(event.getY()).thenReturn(150f); - when(event.getX(0)).thenReturn(50f); - when(event.getY(0)).thenReturn(150f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(100f); - when(event.getHistoricalY(eq(0))).thenReturn(200f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(100f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(200f); - return event; - } - - protected MotionEvent createDefaultUpActionEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_UP); - return event; - } - - protected HorizontalOverScrollBounceEffectDecorator getUUT() { - HorizontalOverScrollBounceEffectDecorator uut = new HorizontalOverScrollBounceEffectDecorator(mViewAdapter); - uut.setOverScrollStateListener(mStateListener); - uut.setOverScrollUpdateListener(mUpdateListener); - return uut; - } - - protected HorizontalOverScrollBounceEffectDecorator getUUT(float touchDragRatioFwd, float touchDragRatioBck) { - HorizontalOverScrollBounceEffectDecorator uut = new HorizontalOverScrollBounceEffectDecorator(mViewAdapter, touchDragRatioFwd, touchDragRatioBck, DEFAULT_DECELERATE_FACTOR); - uut.setOverScrollStateListener(mStateListener); - uut.setOverScrollUpdateListener(mUpdateListener); - return uut; - } -} diff --git a/liboverscroll/src/test/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecoratorTest.java b/liboverscroll/src/test/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecoratorTest.java deleted file mode 100755 index b7c7ff38..00000000 --- a/liboverscroll/src/test/java/me/everything/android/ui/overscroll/VerticalOverScrollBounceEffectDecoratorTest.java +++ /dev/null @@ -1,600 +0,0 @@ -package me.everything.android.ui.overscroll; - -import android.view.MotionEvent; -import android.view.View; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import me.everything.android.ui.overscroll.adapters.IOverScrollDecoratorAdapter; - -import static me.everything.android.ui.overscroll.IOverScrollState.*; -import static me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator.DEFAULT_DECELERATE_FACTOR; -import static me.everything.android.ui.overscroll.VerticalOverScrollBounceEffectDecorator.DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -/** - * @author amitd - */ -@RunWith(RobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class VerticalOverScrollBounceEffectDecoratorTest { - - View mView; - IOverScrollDecoratorAdapter mViewAdapter; - IOverScrollStateListener mStateListener; - IOverScrollUpdateListener mUpdateListener; - - @Before - public void setUp() throws Exception { - mView = mock(View.class); - mViewAdapter = mock(IOverScrollDecoratorAdapter.class); - when(mViewAdapter.getView()).thenReturn(mView); - - mStateListener = mock(IOverScrollStateListener.class); - mUpdateListener = mock(IOverScrollUpdateListener.class); - } - - @Test - public void detach_decoratorIsAttached_detachFromView() throws Exception { - - // Arrange - - HorizontalOverScrollBounceEffectDecorator uut = new HorizontalOverScrollBounceEffectDecorator(mViewAdapter); - - // Act - - uut.detach(); - - // Assert - - verify(mView).setOnTouchListener(eq((View.OnTouchListener) null)); - verify(mView).setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } - - @Test - public void detach_overScrollInEffect_detachFromView() throws Exception { - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, createShortDownwardsMoveEvent()); - - // Act - - uut.detach(); - - // Assert - - verify(mView).setOnTouchListener(eq((View.OnTouchListener) null)); - verify(mView).setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } - - /* - * Move-action event - */ - - @Test - public void onTouchMoveAction_notInViewEnds_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortDownwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut),anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_dragDownInUpperEnd_overscrollDownwards() throws Exception { - - // Arrange - - MotionEvent event = createShortDownwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - float expectedTransY = (event.getY() - event.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationY(expectedTransY); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransY)); - } - - @Test - public void onTouchMoveAction_dragUpInBottomEnd_overscrollUpwards() throws Exception { - - // Arrange - - MotionEvent event = createShortUpwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - float expectedTransY = (event.getY() - event.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationY(expectedTransY); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransY)); - } - - @Test - public void onTouchMoveAction_dragUpInUpperEnd_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortUpwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_dragDownInBottomEnd_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createShortDownwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_2ndDownDragInUpperEnd_overscrollDownwardsFurther() throws Exception { - - // Arrange - - // Bring UUT to a downwards-overscroll state - MotionEvent event1 = createShortDownwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, event1); - reset(mView); - - // Create 2nd downwards-drag event - MotionEvent event2 = createLongDownwardsMoveEvent(); - - // Act - - final boolean ret = uut.onTouch(mView, event2); - - // Assert - - final float expectedTransY1 = (event1.getY() - event1.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - final float expectedTransY2 = (event2.getY() - event2.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationY(expectedTransY2); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransY1)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransY2)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_2ndUpDragInBottomEnd_overscrollUpwardsFurther() throws Exception { - - // Arrange - - // Bring UUT to an upwards-overscroll state - MotionEvent event1 = createShortUpwardsMoveEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - uut.onTouch(mView, event1); - reset(mView); - - // Create 2nd upward-drag event - MotionEvent event2 = createLongUpwardsMoveEvent(); - - // Act - - final boolean ret = uut.onTouch(mView, event2); - - // Assert - - final float expectedTransY1 = (event1.getY() - event1.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - final float expectedTransY2 = (event2.getY() - event2.getHistoricalY(0)) / DEFAULT_TOUCH_DRAG_MOVE_RATIO_FWD; - verify(mView).setTranslationY(expectedTransY2); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransY1)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransY2)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /** - * When over-scroll has already started (downwards in this case) and suddenly the user changes - * their mind and scrolls a bit in the other direction: - *
We expect the touch to still be intercepted in that case, and the overscroll to remain in effect. - */ - @Test - public void onTouchMoveAction_dragUpWhenDownOverscolled_continueOverscrollingUpwards() throws Exception { - - // Arrange - - // In down & up drag tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a downwrads-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveRight = createLongDownwardsMoveEvent(); - uut.onTouch(mView, eventMoveRight); - reset(mView); - float startTransY = (eventMoveRight.getY() - eventMoveRight.getHistoricalY(0)) / touchDragRatioFwd; - when(mView.getTranslationY()).thenReturn(startTransY); - - // Create the up-drag event - MotionEvent eventMoveUpwards = createShortUpwardsMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveUpwards); - - // Assert - - float expectedTransY = startTransY + - (eventMoveUpwards.getY() - eventMoveUpwards.getHistoricalY(0)) / touchDragRatioBck; - verify(mView).setTranslationY(expectedTransY); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_START_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(startTransY)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(expectedTransY)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /** - * When over-scroll has already started (upwards in this case) and suddenly the user changes - * their mind and scrolls a bit in the other direction: - *
We expect the touch to still be intercepted in that case, and the overscroll to remain in effect. - */ - @Test - public void onTouchMoveAction_dragDownWhenUpOverscolled_continueOverscrollingDownwards() throws Exception { - - // Arrange - - // In up & down drag tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to an upwards-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveUp = createLongUpwardsMoveEvent(); - uut.onTouch(mView, eventMoveUp); - reset(mView); - - float startTransY = (eventMoveUp.getY() - eventMoveUp.getHistoricalY(0)) / touchDragRatioFwd; - when(mView.getTranslationY()).thenReturn(startTransY); - - // Create the down-drag event - MotionEvent eventMoveDown = createShortDownwardsMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveDown); - - // Assert - - float expectedTransY = startTransY + (eventMoveDown.getY() - eventMoveDown.getHistoricalY(0)) / touchDragRatioBck; - verify(mView).setTranslationY(expectedTransY); - verify(mView, never()).setTranslationX(anyFloat()); - assertTrue(ret); - assertEquals(STATE_DRAG_END_SIDE, uut.getCurrentState()); - - // State-change listener called only once? - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(startTransY)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(expectedTransY)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_undragWhenDownOverscrolled_endOverscrolling() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a downwards-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(false); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveDown = createLongDownwardsMoveEvent(); - uut.onTouch(mView, eventMoveDown); - reset(mView); - float startTransX = (eventMoveDown.getX() - eventMoveDown.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the (negative) upwards-drag event - MotionEvent eventMoveUp = createLongUpwardsMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveUp); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView).setTranslationY(0); - assertTrue(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - // State-change listener invoked to say drag-on and drag-off (idle). - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_START_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_DRAG_START_SIDE), eq(STATE_IDLE)); - verify(mStateListener, times(2)).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_START_SIDE), eq(0f)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - @Test - public void onTouchMoveAction_undragWhenUpOverscrolled_endOverscrolling() throws Exception { - - // Arrange - - // In left & right tests we use equal ratios to avoid the effect's under-scroll handling - final float touchDragRatioFwd = 3f; - final float touchDragRatioBck = 3f; - - // Bring UUT to a left-overscroll state - when(mViewAdapter.isInAbsoluteStart()).thenReturn(false); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(touchDragRatioFwd, touchDragRatioBck); - MotionEvent eventMoveUp = createLongUpwardsMoveEvent(); - uut.onTouch(mView, eventMoveUp); - reset(mView); - float startTransX = (eventMoveUp.getX() - eventMoveUp.getHistoricalX(0)) / touchDragRatioFwd; - when(mView.getTranslationX()).thenReturn(startTransX); - - // Create the (negative) downwards-drag event - MotionEvent eventMoveDown = createLongDownwardsMoveEvent(); - - // Act - - boolean ret = uut.onTouch(mView, eventMoveDown); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView).setTranslationY(0); - assertTrue(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - // State-change listener invoked to say drag-on and drag-off (idle). - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_IDLE), eq(STATE_DRAG_END_SIDE)); - verify(mStateListener).onOverScrollStateChange(eq(uut), eq(STATE_DRAG_END_SIDE), eq(STATE_IDLE)); - verify(mStateListener, times(2)).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - // Update-listener called exactly twice? - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(startTransX)); - verify(mUpdateListener).onOverScrollUpdate(eq(uut), eq(STATE_DRAG_END_SIDE), eq(0f)); - verify(mUpdateListener, times(2)).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - /* - * Up action event - */ - - @Test - public void onTouchUpAction_eventWhenNotOverscrolled_ignoreTouchEvent() throws Exception { - - // Arrange - - MotionEvent event = createDefaultUpActionEvent(); - - when(mViewAdapter.isInAbsoluteStart()).thenReturn(true); - when(mViewAdapter.isInAbsoluteEnd()).thenReturn(true); - - VerticalOverScrollBounceEffectDecorator uut = getUUT(); - - // Act - - boolean ret = uut.onTouch(mView, event); - - // Assert - - verify(mView, never()).setTranslationX(anyFloat()); - verify(mView, never()).setTranslationY(anyFloat()); - assertFalse(ret); - assertEquals(STATE_IDLE, uut.getCurrentState()); - - verify(mStateListener, never()).onOverScrollStateChange(eq(uut), anyInt(), anyInt()); - verify(mUpdateListener, never()).onOverScrollUpdate(eq(uut), anyInt(), anyFloat()); - } - - protected MotionEvent createShortDownwardsMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(200f); - when(event.getY()).thenReturn(100f); - when(event.getX(0)).thenReturn(200f); - when(event.getY(0)).thenReturn(100f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(190f); - when(event.getHistoricalY(eq(0))).thenReturn(80f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(190f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(80f); - return event; - } - - protected MotionEvent createLongDownwardsMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(250f); - when(event.getY()).thenReturn(150f); - when(event.getX(0)).thenReturn(250f); - when(event.getY(0)).thenReturn(150f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(200f); - when(event.getHistoricalY(eq(0))).thenReturn(100f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(200f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(100f); - return event; - } - - protected MotionEvent createShortUpwardsMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(200f); - when(event.getY()).thenReturn(100f); - when(event.getX(0)).thenReturn(200f); - when(event.getY(0)).thenReturn(100f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(220f); - when(event.getHistoricalY(eq(0))).thenReturn(120f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(220f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(120f); - return event; - } - - protected MotionEvent createLongUpwardsMoveEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_MOVE); - when(event.getX()).thenReturn(200f); - when(event.getY()).thenReturn(100f); - when(event.getX(0)).thenReturn(200f); - when(event.getY(0)).thenReturn(100f); - when(event.getHistorySize()).thenReturn(1); - when(event.getHistoricalX(eq(0))).thenReturn(250f); - when(event.getHistoricalY(eq(0))).thenReturn(150f); - when(event.getHistoricalX(eq(0), eq(0))).thenReturn(250f); - when(event.getHistoricalY(eq(0), eq(0))).thenReturn(150f); - return event; - } - - protected MotionEvent createDefaultUpActionEvent() { - MotionEvent event = mock(MotionEvent.class); - when(event.getAction()).thenReturn(MotionEvent.ACTION_UP); - return event; - } - - protected VerticalOverScrollBounceEffectDecorator getUUT() { - VerticalOverScrollBounceEffectDecorator uut = new VerticalOverScrollBounceEffectDecorator(mViewAdapter); - uut.setOverScrollStateListener(mStateListener); - uut.setOverScrollUpdateListener(mUpdateListener); - return uut; - } - - protected VerticalOverScrollBounceEffectDecorator getUUT(float touchDragRatioFwd, float touchDragRatioBck) { - VerticalOverScrollBounceEffectDecorator uut = new VerticalOverScrollBounceEffectDecorator(mViewAdapter, touchDragRatioFwd, touchDragRatioBck, DEFAULT_DECELERATE_FACTOR); - uut.setOverScrollStateListener(mStateListener); - uut.setOverScrollUpdateListener(mUpdateListener); - return uut; - } -} \ No newline at end of file