aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt')
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt237
1 files changed, 0 insertions, 237 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt b/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
deleted file mode 100644
index c39a278..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * 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 ca.allanwang.kau.widgets
-
-import android.app.Activity
-import android.content.Context
-import android.graphics.Color
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.*
-
-/**
- * A [FrameLayout] which responds to nested scrolls to create drag-dismissable layouts.
- * Applies an elasticity factor to reduce movement as you approach the given dismiss distance.
- * Optionally also scales down content during drag.
- */
-class ElasticDragDismissFrameLayout @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
-
- // configurable attribs
- var dragDismissDistance = context.dimen(R.dimen.kau_drag_dismiss_distance).dpToPx
- var dragDismissFraction = -1f
- var dragDismissScale = 1f
- set(value) {
- field = value
- shouldScale = value != 1f
- }
- private var shouldScale = false
- var dragElacticity = 0.8f
-
- // state
- private var totalDrag: Float = 0f
- private var draggingDown = false
- private var draggingUp = false
-
- private var callbacks: MutableList<ElasticDragDismissCallback> = mutableListOf()
-
- init {
- if (attrs != null) {
- val a = getContext().obtainStyledAttributes(attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0)
- dragDismissDistance = a.getDimensionPixelSize(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance, Int.MAX_VALUE).toFloat()
- dragDismissFraction = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction)
- dragDismissScale = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale)
- dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity, dragElacticity)
- a.recycle()
- }
- }
-
- abstract class ElasticDragDismissCallback {
-
- /**
- * Called for each drag event.
-
- * @param elasticOffset Indicating the drag offset with elasticity applied i.e. may
- * * exceed 1.
- * *
- * @param elasticOffsetPixels The elastically scaled drag distance in pixels.
- * *
- * @param rawOffset Value from [0, 1] indicating the raw drag offset i.e.
- * * without elasticity applied. A value of 1 indicates that the
- * * dismiss distance has been reached.
- * *
- * @param rawOffsetPixels The raw distance the user has dragged
- */
- internal open fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- }
-
- /**
- * Called when dragging is released and has exceeded the threshold dismiss distance.
- */
- internal open fun onDragDismissed() {}
-
- }
-
- override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean {
- return nestedScrollAxes and View.SCROLL_AXIS_VERTICAL != 0
- }
-
- override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {
- // if we're in a drag gesture and the user reverses up the we should take those events
- if (draggingDown && dy > 0 || draggingUp && dy < 0) {
- dragScale(dy)
- consumed[1] = dy
- }
- }
-
- override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int,
- dxUnconsumed: Int, dyUnconsumed: Int) {
- dragScale(dyUnconsumed)
- }
-
- override fun onStopNestedScroll(child: View) {
- if (Math.abs(totalDrag) >= dragDismissDistance) {
- dispatchDismissCallback()
- } else { // settle back to natural position
- animate()
- .translationY(0f)
- .scaleX(1f)
- .scaleY(1f)
- .setDuration(200L)
- .setInterpolator(AnimHolder.fastOutSlowInInterpolator(context))
- .setListener(null)
- .start()
- totalDrag = 0f
- draggingUp = false
- draggingDown = draggingUp
- dispatchDragCallback(0f, 0f, 0f, 0f)
- }
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- if (dragDismissFraction > 0f) {
- dragDismissDistance = h * dragDismissFraction
- }
- }
-
- fun addListener(listener: ElasticDragDismissCallback) {
- callbacks.add(listener)
- }
-
- fun removeListener(listener: ElasticDragDismissCallback) {
- callbacks.remove(listener)
- }
-
- private fun dragScale(scroll: Int) {
- if (scroll == 0) return
-
- totalDrag += scroll.toFloat()
-
- // track the direction & set the pivot point for scaling
- // don't double track i.e. if start dragging down and then reverse, keep tracking as
- // dragging down until they reach the 'natural' position
- if (scroll < 0 && !draggingUp && !draggingDown) {
- draggingDown = true
- if (shouldScale) pivotY = height.toFloat()
- } else if (scroll > 0 && !draggingDown && !draggingUp) {
- draggingUp = true
- if (shouldScale) pivotY = 0f
- }
- // how far have we dragged relative to the distance to perform a dismiss
- // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
- var dragFraction = Math.log10((1 + Math.abs(totalDrag) / dragDismissDistance).toDouble()).toFloat()
-
- // calculate the desired translation given the drag fraction
- var dragTo = dragFraction * dragDismissDistance * dragElacticity
-
- if (draggingUp) {
- // as we use the absolute magnitude when calculating the drag fraction, need to
- // re-apply the drag direction
- dragTo *= -1f
- }
- translationY = dragTo
-
- if (shouldScale) {
- val scale = 1 - (1 - dragDismissScale) * dragFraction
- scaleX = scale
- scaleY = scale
- }
-
- // if we've reversed direction and gone past the settle point then clear the flags to
- // allow the list to get the scroll events & reset any transforms
- if (draggingDown && totalDrag >= 0 || draggingUp && totalDrag <= 0) {
- dragFraction = 0f
- dragTo = dragFraction
- totalDrag = dragTo
- draggingUp = false
- draggingDown = draggingUp
- translationY = 0f
- scaleX = 1f
- scaleY = 1f
- }
- dispatchDragCallback(dragFraction, dragTo,
- Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag)
- }
-
- private fun dispatchDragCallback(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- callbacks.forEach {
- it.onDrag(elasticOffset, elasticOffsetPixels,
- rawOffset, rawOffsetPixels)
- }
- }
-
- private fun dispatchDismissCallback() {
- callbacks.forEach { it.onDragDismissed() }
- }
-
- /**
- * An [ElasticDragDismissCallback] which fades system chrome (i.e. status bar and
- * navigation bar) whilst elastic drags are performed and
- * [finishes][Activity.finishAfterTransition] the activity when drag dismissed.
- */
- open class SystemChromeFader(private val activity: Activity) : ElasticDragDismissCallback() {
- private val statusBarAlpha: Int = Color.alpha(activity.statusBarColor)
- private val navBarAlpha: Int = Color.alpha(activity.navigationBarColor)
- private val fadeNavBar: Boolean = activity.isNavBarOnBottom
-
- public override fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- if (elasticOffsetPixels > 0) {
- // dragging downward, fade the status bar in proportion
- activity.statusBarColor = activity.statusBarColor.withAlpha(((1f - rawOffset) * statusBarAlpha).toInt())
- } else if (elasticOffsetPixels == 0f) {
- // reset
- activity.statusBarColor = activity.statusBarColor.withAlpha(statusBarAlpha)
- activity.navigationBarColor = activity.navigationBarColor.withAlpha(navBarAlpha)
- } else if (fadeNavBar) {
- // dragging upward, fade the navigation bar in proportion
- activity.navigationBarColor = activity.navigationBarColor.withAlpha(((1f - rawOffset) * navBarAlpha).toInt())
- }
- }
-
- public override fun onDragDismissed() {
- activity.finishAfterTransition()
- }
- }
-
-}