@ -1,3 +1,25 @@
/ * This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/. */
/* Added the Mozilla Public License above to avoid failing detekt rule */
/ *
* Copyright ( C ) 2015 The Android Open Source Project
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
package org.mozilla.fenix.components.topsheet
package org.mozilla.fenix.components.topsheet
import android.animation.ValueAnimator
import android.animation.ValueAnimator
@ -6,7 +28,12 @@ import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable
import android.util.AttributeSet
import android.util.AttributeSet
import android.util.TypedValue
import android.util.TypedValue
import android.view.*
import android.view.AbsSavedState
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import androidx.annotation.IntDef
import androidx.annotation.IntDef
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingChild
@ -19,47 +46,31 @@ import com.google.android.material.shape.ShapeAppearanceModel
import java.lang.ref.WeakReference
import java.lang.ref.WeakReference
import kotlin.math.abs
import kotlin.math.abs
/ *
/ * *
* Copyright ( C ) 2015 The Android Open Source Project
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* / / * *
* An interaction behavior plugin for a child view of [ CoordinatorLayout ] to make it work as
* An interaction behavior plugin for a child view of [ CoordinatorLayout ] to make it work as
* a top sheet .
* a top sheet .
* /
* /
@Suppress ( " TooManyFunctions " , " LargeClass " )
class TopSheetBehavior < V : View ? >
class TopSheetBehavior < V : View ? >
/ * *
/ * *
* Default constructor for inflating TopSheetBehaviors from layout .
* Default constructor for inflating TopSheetBehaviors from layout .
*
*
* @param context The [ Context ] .
* @param context The [ Context ] .
* @param attrs The [ AttributeSet ] .
* @param attrs The [ AttributeSet ] .
* / ( context : Context , attrs : AttributeSet ? ) : CoordinatorLayout . Behavior < V > ( context , attrs ) {
* / ( context : Context , attrs : AttributeSet ? ) : CoordinatorLayout . Behavior < V > ( context , attrs ) {
/ * *
/ * *
* Callback for monitoring events about top sheets .
* Callback for monitoring events about top sheets .
* /
* /
abstract class TopSheetCallback {
interface TopSheetCallback {
/ * *
/ * *
* Called when the top sheet changes its state .
* Called when the top sheet changes its state .
*
*
* @param topSheet The top sheet view .
* @param topSheet The top sheet view .
* @param newState The new state . This will be one of [ . STATE _DRAGGING ] ,
* @param newState The new state . This will be one of [ . STATE _DRAGGING ] ,
* [ . STATE _SETTLING ] , [ . STATE _EXPANDED ] ,
* [ . STATE _SETTLING ] , [ . STATE _EXPANDED ] ,
* [ . STATE _COLLAPSED ] , or [ . STATE _HIDDEN ] .
* [ . STATE _COLLAPSED ] , or [ . STATE _HIDDEN ] .
* /
* /
abstract fun onStateChanged (
fun onStateChanged ( topSheet : View , @State newState : Int )
topSheet : View ,
@State newState : Int
)
/ * *
/ * *
* Called when the top sheet is being dragged .
* Called when the top sheet is being dragged .
@ -67,13 +78,9 @@ class TopSheetBehavior<V : View?>
* @param topSheet The top sheet view .
* @param topSheet The top sheet view .
* @param slideOffset The new offset of this top sheet within its range , from 0 to 1
* @param slideOffset The new offset of this top sheet within its range , from 0 to 1
* when it is moving upward , and from 0 to - 1 when it moving downward .
* when it is moving upward , and from 0 to - 1 when it moving downward .
* @param isOpening detect showing
* @param isOpening detect showing
* /
* /
abstract fun onSlide (
fun onSlide ( topSheet : View , slideOffset : Float , isOpening : Boolean ? )
topSheet : View ,
slideOffset : Float ,
isOpening : Boolean ?
)
}
}
/ * *
/ * *
@ -148,7 +155,7 @@ class TopSheetBehavior<V : View?>
a . hasValue ( R . styleable . BottomSheetBehavior _Layout _shapeAppearance )
a . hasValue ( R . styleable . BottomSheetBehavior _Layout _shapeAppearance )
createMaterialShapeDrawable ( context , attrs !! )
createMaterialShapeDrawable ( context , attrs !! )
createShapeValueAnimator ( )
createShapeValueAnimator ( )
peekHeight = context . resources . displayMetrics . heightPixels * 3 / 4
peekHeight = context . resources . displayMetrics . heightPixels * PEEK _HEIGHT _RATIO
isHideable = a . getBoolean (
isHideable = a . getBoolean (
R . styleable . BottomSheetBehavior _Layout _behavior _hideable ,
R . styleable . BottomSheetBehavior _Layout _behavior _hideable ,
false
false
@ -188,6 +195,7 @@ class TopSheetBehavior<V : View?>
}
}
}
}
@Suppress ( " ComplexMethod " )
override fun onLayoutChild (
override fun onLayoutChild (
parent : CoordinatorLayout ,
parent : CoordinatorLayout ,
child : V ,
child : V ,
@ -238,6 +246,7 @@ class TopSheetBehavior<V : View?>
return true
return true
}
}
@Suppress ( " ComplexMethod " , " ReturnCount " )
override fun onInterceptTouchEvent (
override fun onInterceptTouchEvent (
parent : CoordinatorLayout ,
parent : CoordinatorLayout ,
child : V ,
child : V ,
@ -306,7 +315,7 @@ class TopSheetBehavior<V : View?>
return true
return true
}
}
if ( mViewDragHelper != null ) {
if ( mViewDragHelper != null ) {
// no crash
// no crash
mViewDragHelper !! . processTouchEvent ( event )
mViewDragHelper !! . processTouchEvent ( event )
// Record the velocity
// Record the velocity
if ( action == MotionEvent . ACTION _DOWN ) {
if ( action == MotionEvent . ACTION _DOWN ) {
@ -318,13 +327,12 @@ class TopSheetBehavior<V : View?>
mVelocityTracker !! . addMovement ( event )
mVelocityTracker !! . addMovement ( event )
// The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
// The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
// to capture the top sheet in case it is not captured and the touch slop is passed.
// to capture the top sheet in case it is not captured and the touch slop is passed.
if ( action == MotionEvent . ACTION _MOVE && ! mIgnoreEvents ) {
if ( action == MotionEvent . ACTION _MOVE && ! mIgnoreEvents &&
if ( abs ( mInitialY - event . y ) > mViewDragHelper !! . touchSlop ) {
abs ( mInitialY - event . y ) > mViewDragHelper !! . touchSlop ) {
mViewDragHelper !! . captureChildView (
mViewDragHelper !! . captureChildView (
child ,
child ,
event . getPointerId ( event . actionIndex )
event . getPointerId ( event . actionIndex )
)
)
}
}
}
}
}
return ! mIgnoreEvents
return ! mIgnoreEvents
@ -343,8 +351,12 @@ class TopSheetBehavior<V : View?>
}
}
override fun onNestedPreScroll (
override fun onNestedPreScroll (
coordinatorLayout : CoordinatorLayout , child : V , target : View , dx : Int ,
coordinatorLayout : CoordinatorLayout ,
dy : Int , consumed : IntArray
child : V ,
target : View ,
dx : Int ,
dy : Int ,
consumed : IntArray
) {
) {
val scrollingChild = mNestedScrollingChildRef !! . get ( )
val scrollingChild = mNestedScrollingChildRef !! . get ( )
if ( target !== scrollingChild ) {
if ( target !== scrollingChild ) {
@ -430,8 +442,11 @@ class TopSheetBehavior<V : View?>
}
}
override fun onNestedPreFling (
override fun onNestedPreFling (
coordinatorLayout : CoordinatorLayout , child : V , target : View ,
coordinatorLayout : CoordinatorLayout ,
velocityX : Float , velocityY : Float
child : V ,
target : View ,
velocityX : Float ,
velocityY : Float
) : Boolean {
) : Boolean {
return target === mNestedScrollingChildRef !! . get ( ) &&
return target === mNestedScrollingChildRef !! . get ( ) &&
( mState != STATE _EXPANDED ||
( mState != STATE _EXPANDED ||
@ -466,9 +481,9 @@ class TopSheetBehavior<V : View?>
}
}
if ( mViewRef == null ) {
if ( mViewRef == null ) {
// The view is not laid out yet; modify mState and let onLayoutChild handle it later
// The view is not laid out yet; modify mState and let onLayoutChild handle it later
if ( state == STATE _COLLAPSED || state == STATE _EXPANDED ||
val stateCondition = state == STATE _COLLAPSED || state == STATE _EXPANDED ||
isHideable && state == STATE _HIDDEN
isHideable && state == STATE _HIDDEN
) {
if ( stateCondition ) {
mState = state
mState = state
}
}
return
return
@ -509,6 +524,7 @@ class TopSheetBehavior<V : View?>
}
}
}
}
@Suppress ( " NestedBlockDepth " )
private fun updateDrawableForTargetState ( @BottomSheetBehavior . State state : Int ) {
private fun updateDrawableForTargetState ( @BottomSheetBehavior . State state : Int ) {
if ( state == BottomSheetBehavior . STATE _SETTLING ) {
if ( state == BottomSheetBehavior . STATE _SETTLING ) {
// Special case: we want to know which state we're settling to, so wait for another call.
// Special case: we want to know which state we're settling to, so wait for another call.
@ -567,11 +583,12 @@ class TopSheetBehavior<V : View?>
private val yVelocity : Float
private val yVelocity : Float
get ( ) {
get ( ) {
mVelocityTracker !! . computeCurrentVelocity ( 1000 , mMaximumVelocity )
mVelocityTracker !! . computeCurrentVelocity ( VELOCITY _UNITS , mMaximumVelocity )
return mVelocityTracker !! . getYVelocity ( mActivePointerId )
return mVelocityTracker !! . getYVelocity ( mActivePointerId )
}
}
private val mDragCallback : ViewDragHelper . Callback = object : ViewDragHelper . Callback ( ) {
private val mDragCallback : ViewDragHelper . Callback = object : ViewDragHelper . Callback ( ) {
@Suppress ( " ReturnCount " )
override fun tryCaptureView (
override fun tryCaptureView (
child : View ,
child : View ,
pointerId : Int
pointerId : Int
@ -709,7 +726,6 @@ class TopSheetBehavior<V : View?>
}
}
}
}
private fun dispatchOnSlide ( top : Int ) {
private fun dispatchOnSlide ( top : Int ) {
val topSheet : View ? = mViewRef !! . get ( )
val topSheet : View ? = mViewRef !! . get ( )
if ( topSheet != null && mCallback != null ) {
if ( topSheet != null && mCallback != null ) {
@ -742,7 +758,6 @@ class TopSheetBehavior<V : View?>
setStateInternal ( mTargetState )
setStateInternal ( mTargetState )
}
}
}
}
}
}
private class SavedState ( superState : Parcelable ? , @State val state : Int ) :
private class SavedState ( superState : Parcelable ? , @State val state : Int ) :
@ -806,5 +821,7 @@ class TopSheetBehavior<V : View?>
private const val CORNER _ANIMATION _DURATION = 500
private const val CORNER _ANIMATION _DURATION = 500
private val DEF _STYLE _RES = R . style . Widget _Design _BottomSheet _Modal
private val DEF _STYLE _RES = R . style . Widget _Design _BottomSheet _Modal
private const val PEEK _HEIGHT _RATIO = 3 / 4
private const val VELOCITY _UNITS = 1000
}
}
}
}