1
0
Fork 0
fenix/app/src/main/java/org/mozilla/fenix/quickactionsheet/QuickActionSheet.kt

175 lines
6.4 KiB
Kotlin
Raw Normal View History

2019-03-20 16:29:04 +01:00
/* 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/. */
2019-03-19 17:13:48 +01:00
package org.mozilla.fenix.quickactionsheet
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import mozilla.components.browser.toolbar.BrowserToolbar
import org.mozilla.fenix.R
import android.os.Bundle
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.ImageButton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
2019-03-20 16:55:24 +01:00
import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext
2019-03-19 17:13:48 +01:00
const val POSITION_SNAP_BUFFER = 1f
2019-03-19 17:13:48 +01:00
class QuickActionSheet @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyle, defStyleRes), CoroutineScope {
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
private lateinit var handle: ImageButton
private lateinit var linearLayout: LinearLayout
private lateinit var quickActionSheetBehavior: QuickActionSheetBehavior
2019-03-19 17:13:48 +01:00
init {
2019-03-20 17:28:36 +01:00
inflate(getContext(), R.layout.layout_quick_action_sheet, this)
}
2019-03-21 15:58:24 +01:00
override fun onAttachedToWindow() {
super.onAttachedToWindow()
job = Job()
handle = findViewById(R.id.quick_action_sheet_handle)
linearLayout = findViewById(R.id.quick_action_sheet)
quickActionSheetBehavior = BottomSheetBehavior.from(linearLayout.parent as View) as QuickActionSheetBehavior
quickActionSheetBehavior.isHideable = false
setupHandle()
2019-03-19 17:13:48 +01:00
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
job.cancel()
}
2019-03-20 17:28:36 +01:00
private fun setupHandle() {
2019-03-19 21:26:36 +01:00
handle.setOnClickListener {
quickActionSheetBehavior.state = when (quickActionSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_COLLAPSED
else -> BottomSheetBehavior.STATE_EXPANDED
}
2019-03-19 21:26:36 +01:00
}
2019-03-19 17:13:48 +01:00
handle.setAccessibilityDelegate(HandleAccessibilityDelegate(quickActionSheetBehavior))
2019-03-19 17:13:48 +01:00
}
fun bounceSheet() {
launch(Main) {
delay(BOUNCE_ANIMATION_DELAY_LENGTH)
quickActionSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
delay(BOUNCE_ANIMATION_PAUSE_LENGTH)
quickActionSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
2019-03-21 18:44:50 +01:00
}
Settings.getInstance(context).incrementAutomaticBounceQuickActionSheetCount()
}
2019-03-20 16:29:04 +01:00
class HandleAccessibilityDelegate(
private val quickActionSheetBehavior: QuickActionSheetBehavior
) : View.AccessibilityDelegate() {
private var finalState = BottomSheetBehavior.STATE_COLLAPSED
get() = when (quickActionSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED,
BottomSheetBehavior.STATE_HIDDEN,
BottomSheetBehavior.STATE_COLLAPSED -> {
quickActionSheetBehavior.state
}
else -> field
}
set(value) {
field = value
quickActionSheetBehavior.state = value
}
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
when (action) {
AccessibilityNodeInfo.ACTION_CLICK -> {
finalState = when (quickActionSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_COLLAPSED
else -> BottomSheetBehavior.STATE_EXPANDED
}
}
AccessibilityNodeInfo.ACTION_COLLAPSE ->
finalState = BottomSheetBehavior.STATE_COLLAPSED
AccessibilityNodeInfo.ACTION_EXPAND ->
finalState = BottomSheetBehavior.STATE_EXPANDED
else -> return super.performAccessibilityAction(host, action, args)
}
host?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED)
return true
}
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.addAction(when (finalState) {
BottomSheetBehavior.STATE_COLLAPSED,
BottomSheetBehavior.STATE_HIDDEN -> AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND
else -> AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
})
}
}
2019-03-20 16:29:04 +01:00
companion object {
const val BOUNCE_ANIMATION_DELAY_LENGTH = 1000L
const val BOUNCE_ANIMATION_PAUSE_LENGTH = 2000L
2019-03-20 16:29:04 +01:00
}
2019-03-19 17:13:48 +01:00
}
@Suppress("unused") // Referenced from XML
class QuickActionSheetBehavior(
context: Context,
attrs: AttributeSet
) : BottomSheetBehavior<NestedScrollView>(context, attrs) {
2019-03-19 17:13:48 +01:00
override fun layoutDependsOn(parent: CoordinatorLayout, child: NestedScrollView, dependency: View): Boolean {
if (dependency is BrowserToolbar) {
return true
}
return super.layoutDependsOn(parent, child, dependency)
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: NestedScrollView,
dependency: View
): Boolean {
return if (dependency is BrowserToolbar) {
repositionQuickActionSheet(child, dependency)
true
} else {
false
}
}
private fun repositionQuickActionSheet(quickActionSheetContainer: NestedScrollView, toolbar: BrowserToolbar) {
if (toolbar.translationY >= toolbar.height.toFloat() - POSITION_SNAP_BUFFER) {
state = STATE_HIDDEN
} else if (state == STATE_HIDDEN || state == STATE_SETTLING) {
state = STATE_COLLAPSED
}
quickActionSheetContainer.translationY = toolbar.translationY + toolbar.height * -1.0f
2019-03-19 17:13:48 +01:00
}
2019-03-20 16:29:04 +01:00
}