From 5ef17269315e45820ea0d20b62e52216a43d8db7 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Thu, 3 May 2018 16:10:13 +0200
Subject: [PATCH] Removes a vendored dependency

Signed-off-by: Janne Koschinski <janne@kuschku.de>
---
 README.md                                     |    2 -
 app/build.gradle.kts                          |    1 -
 app/sampledata/libraries.json                 |    9 -
 .../quasseldroid/ui/chat/ChatActivity.kt      |   40 +-
 .../ui/chat/input/AutoCompleteHelper.kt       |   17 +-
 .../ui/chat/input/ChatlineFragment.kt         |   43 +-
 .../ui/chat/input/RichEditText.kt             |    4 +-
 .../ui/chat/topic/TopicFragment.kt            |    2 +-
 .../ui/clientsettings/about/AboutFragment.kt  |    6 -
 .../aliasitem/AliasItemFragment.kt            |    2 +-
 .../ui/DragInterceptBottomSheetBehavior.kt    |   56 +
 .../quasseldroid/util/ui/ShadowView.java      |  186 ++
 .../util/ui/TouchInterceptingFrameLayout.kt   |   38 +
 app/src/main/res/layout-land/layout_main.xml  |   44 +-
 .../res/layout-sw600dp-land/layout_main.xml   |   32 +-
 app/src/main/res/layout/fragment_chatline.xml |   13 +-
 app/src/main/res/layout/layout_editor.xml     |  141 +-
 app/src/main/res/layout/layout_history.xml    |   13 +-
 app/src/main/res/layout/layout_main.xml       |   33 +-
 app/src/main/res/values/strings.xml           |    2 +
 app/src/main/res/values/styles_widgets.xml    |   11 +-
 settings.gradle                               |    1 -
 slidingpanel/build.gradle.kts                 |   32 -
 slidingpanel/src/main/AndroidManifest.xml     |   23 -
 .../slidinguppanel/ScrollableViewHelper.java  |   82 -
 .../slidinguppanel/SlidingUpPanelLayout.java  | 1506 -----------------
 .../slidinguppanel/ViewDragHelper.java        | 1481 ----------------
 slidingpanel/src/main/res/values/attrs.xml    |   43 -
 28 files changed, 522 insertions(+), 3341 deletions(-)
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/DragInterceptBottomSheetBehavior.kt
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/ShadowView.java
 create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/ui/TouchInterceptingFrameLayout.kt
 delete mode 100644 slidingpanel/build.gradle.kts
 delete mode 100644 slidingpanel/src/main/AndroidManifest.xml
 delete mode 100644 slidingpanel/src/main/java/com/sothree/slidinguppanel/ScrollableViewHelper.java
 delete mode 100644 slidingpanel/src/main/java/com/sothree/slidinguppanel/SlidingUpPanelLayout.java
 delete mode 100644 slidingpanel/src/main/java/com/sothree/slidinguppanel/ViewDragHelper.java
 delete mode 100644 slidingpanel/src/main/res/values/attrs.xml

diff --git a/README.md b/README.md
index 7a01f76c2..bfb8e57e7 100644
--- a/README.md
+++ b/README.md
@@ -65,8 +65,6 @@ Authors of the previous version of Quasseldroid:
   Apache-2.0
 * [**Android Architecture Components: Room**](https://android.googlesource.com/platform/frameworks/support/+/master/persistence)
   Apache-2.0
-* [**Android Sliding Up Panel**](https://github.com/umano/AndroidSlidingUpPanel)
-  Apache-2.0
 * [**Android Support Library**](https://android.googlesource.com/platform/frameworks/support/+/master)
   Apache-2.0
 * [**Android Support Library: Constraint Layout**](https://android.googlesource.com/platform/frameworks/opt/sherpa/+/studio-3.0/constraintlayout)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 9985a8ed0..0673a83e8 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -175,7 +175,6 @@ dependencies {
     implementation("com.github.bumptech.glide", "recyclerview-integration", version)
     kapt("com.github.bumptech.glide", "compiler", version)
   }
-  implementation(project(":slidingpanel"))
 
   // Quality Assurance
   implementation(project(":malheur"))
diff --git a/app/sampledata/libraries.json b/app/sampledata/libraries.json
index ef65ab1d1..a4e876054 100644
--- a/app/sampledata/libraries.json
+++ b/app/sampledata/libraries.json
@@ -36,15 +36,6 @@
       },
       "url": "https://android.googlesource.com/platform/frameworks/support/+/master/persistence"
     },
-    {
-      "name": "Android Sliding Up Panel",
-      "version": "3.5.0",
-      "license": {
-        "short_name": "Apache-2.0",
-        "full_name": "Apache License"
-      },
-      "url": "https://github.com/umano/AndroidSlidingUpPanel"
-    },
     {
       "name": "Android Support Library",
       "version": "27.1.1",
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index c93283df5..f24be1b2d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -27,6 +27,7 @@ import android.content.SharedPreferences
 import android.os.Build
 import android.os.Bundle
 import android.os.PersistableBundle
+import android.support.design.widget.BottomSheetBehavior
 import android.support.v4.widget.DrawerLayout
 import android.support.v7.app.ActionBarDrawerToggle
 import android.support.v7.widget.DefaultItemAnimator
@@ -39,7 +40,6 @@ import android.widget.EditText
 import butterknife.BindView
 import butterknife.ButterKnife
 import com.afollestad.materialdialogs.MaterialDialog
-import com.sothree.slidinguppanel.SlidingUpPanelLayout
 import de.kuschku.libquassel.connection.ConnectionState
 import de.kuschku.libquassel.connection.QuasselSecurityException
 import de.kuschku.libquassel.protocol.Buffer_Type
@@ -64,6 +64,7 @@ import de.kuschku.quasseldroid.ui.coresettings.CoreSettingsActivity
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundActivity
+import de.kuschku.quasseldroid.util.ui.DragInterceptBottomSheetBehavior
 import de.kuschku.quasseldroid.util.ui.MaterialContentLoadingProgressBar
 import de.kuschku.quasseldroid.viewmodel.data.BufferData
 import org.threeten.bp.Instant
@@ -85,9 +86,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   @BindView(R.id.progress_bar)
   lateinit var progressBar: MaterialContentLoadingProgressBar
 
-  @BindView(R.id.editor_panel)
-  lateinit var editorPanel: SlidingUpPanelLayout
-
   @BindView(R.id.autocomplete_list)
   lateinit var autoCompleteList: RecyclerView
 
@@ -107,6 +105,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   @Named("ui")
   lateinit var autoCompleteAdapter: AutoCompleteAdapter
 
+  lateinit var editorBottomSheet: DragInterceptBottomSheetBehavior<View>
+
   private val dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
 
   private lateinit var drawerToggle: ActionBarDrawerToggle
@@ -176,12 +176,16 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     })
 
     if (autoCompleteSettings.prefix || autoCompleteSettings.auto) {
+      val autoCompleteBottomSheet = BottomSheetBehavior.from(autoCompleteList)
       chatlineFragment?.let {
         autoCompleteAdapter.setOnClickListener(it.chatline::autoComplete)
         autoCompleteList.layoutManager = LinearLayoutManager(it.activity)
         autoCompleteList.itemAnimator = DefaultItemAnimator()
         autoCompleteList.adapter = autoCompleteAdapter
-        it.autoCompleteHelper.setDataListener {
+        it.autoCompleteHelper.addDataListener {
+          autoCompleteBottomSheet.state =
+            if (it.isEmpty()) BottomSheetBehavior.STATE_HIDDEN
+            else BottomSheetBehavior.STATE_COLLAPSED
           autoCompleteAdapter.submitList(it)
         }
       }
@@ -484,8 +488,22 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
 
     onNewIntent(intent)
 
-    editorPanel.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
-    chatlineFragment?.panelSlideListener?.let(editorPanel::addPanelSlideListener)
+    editorBottomSheet = DragInterceptBottomSheetBehavior.from(chatlineFragment?.view)
+    editorBottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
+    chatlineFragment?.panelSlideListener?.let(editorBottomSheet::setBottomSheetCallback)
+
+    chatlineFragment?.historyBottomSheet?.setBottomSheetCallback(
+      object : BottomSheetBehavior.BottomSheetCallback() {
+        override fun onSlide(bottomSheet: View, slideOffset: Float) {
+          val opacity = (1.0f - slideOffset) / 2.0f
+          chatlineFragment?.editorContainer?.alpha = opacity
+        }
+
+        override fun onStateChanged(bottomSheet: View, newState: Int) {
+          editorBottomSheet.allowDragging = newState == BottomSheetBehavior.STATE_HIDDEN
+        }
+      }
+    )
   }
 
   var bufferData: BufferData? = null
@@ -627,13 +645,13 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
   }
 
   override fun onBackPressed() {
-    if (chatlineFragment?.historyPanel?.panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
-      chatlineFragment?.historyPanel?.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+    if (chatlineFragment?.historyBottomSheet?.state == BottomSheetBehavior.STATE_EXPANDED) {
+      chatlineFragment?.historyBottomSheet?.state = BottomSheetBehavior.STATE_HIDDEN
       return
     }
 
-    if (editorPanel.panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
-      editorPanel.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+    if (editorBottomSheet.state == BottomSheetBehavior.STATE_EXPANDED) {
+      editorBottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
       return
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
index 44a0b934b..814dc3432 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
@@ -50,7 +50,7 @@ class AutoCompleteHelper(
   private val viewModel: EditorViewModel
 ) {
   private var autocompleteListener: ((AutoCompletionState) -> Unit)? = null
-  private var dataListener: ((List<AutoCompleteItem>) -> Unit)? = null
+  private var dataListeners: List<((List<AutoCompleteItem>) -> Unit)> = emptyList()
 
   var autoCompletionState: AutoCompletionState? = null
 
@@ -72,7 +72,7 @@ class AutoCompleteHelper(
                               (autoCompleteSettings.prefix && query.startsWith('@')) ||
                               (autoCompleteSettings.prefix && query.startsWith('#'))
       val list = if (shouldShowResults) it?.second.orEmpty() else emptyList()
-      dataListener?.invoke(list.map {
+      val data = list.map {
         if (it is AutoCompleteItem.UserItem) {
           val nickName = it.nick
           val senderColorIndex = IrcUserUtils.senderColor(nickName)
@@ -114,7 +114,10 @@ class AutoCompleteHelper(
         } else {
           it
         }
-      })
+      }
+      dataListeners.forEach {
+        it(data)
+      }
     })
   }
 
@@ -122,8 +125,12 @@ class AutoCompleteHelper(
     this.autocompleteListener = listener
   }
 
-  fun setDataListener(listener: ((List<AutoCompleteItem>) -> Unit)?) {
-    this.dataListener = listener
+  fun addDataListener(listener: ((List<AutoCompleteItem>) -> Unit)) {
+    this.dataListeners += listener
+  }
+
+  fun removeDataListener(listener: ((List<AutoCompleteItem>) -> Unit)) {
+    this.dataListeners -= listener
   }
 
   private fun autoCompleteDataFull(): List<AutoCompleteItem> {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
index 232e4b1e2..d8a0262ed 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt
@@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.ui.chat.input
 
 import android.arch.lifecycle.Observer
 import android.os.Bundle
+import android.support.design.widget.BottomSheetBehavior
 import android.support.v7.widget.AppCompatImageButton
 import android.support.v7.widget.DefaultItemAnimator
 import android.support.v7.widget.LinearLayoutManager
@@ -31,7 +32,6 @@ import android.view.View
 import android.view.ViewGroup
 import butterknife.BindView
 import butterknife.ButterKnife
-import com.sothree.slidinguppanel.SlidingUpPanelLayout
 import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.AppearanceSettings
@@ -60,12 +60,18 @@ class ChatlineFragment : ServiceBoundFragment() {
   @BindView(R.id.msg_history)
   lateinit var messageHistory: RecyclerView
 
-  @BindView(R.id.history_panel)
-  lateinit var historyPanel: SlidingUpPanelLayout
-
   @BindView(R.id.autocomplete_list)
   lateinit var autoCompleteList: RecyclerView
 
+  @BindView(R.id.close)
+  lateinit var close: AppCompatImageButton
+
+  @BindView(R.id.card_panel)
+  lateinit var cardPanel: View
+
+  @BindView(R.id.editor_container)
+  lateinit var editorContainer: View
+
   @Inject
   lateinit var autoCompleteSettings: AutoCompleteSettings
 
@@ -91,13 +97,13 @@ class ChatlineFragment : ServiceBoundFragment() {
 
   lateinit var autoCompleteHelper: AutoCompleteHelper
 
-  val panelSlideListener = object : SlidingUpPanelLayout.PanelSlideListener {
-    override fun onPanelSlide(panel: View?, slideOffset: Float) = Unit
+  lateinit var historyBottomSheet: BottomSheetBehavior<View>
+
+  val panelSlideListener = object : BottomSheetBehavior.BottomSheetCallback() {
+    override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
 
-    override fun onPanelStateChanged(panel: View?,
-                                     previousState: SlidingUpPanelLayout.PanelState?,
-                                     newState: SlidingUpPanelLayout.PanelState?) {
-      editorHelper.setMultiLine(newState == SlidingUpPanelLayout.PanelState.COLLAPSED)
+    override fun onStateChanged(bottomSheet: View, newState: Int) {
+      editorHelper.setMultiLine(newState != BottomSheetBehavior.STATE_COLLAPSED)
     }
   }
 
@@ -127,22 +133,31 @@ class ChatlineFragment : ServiceBoundFragment() {
 
     editorViewModel.lastWord.onNext(editorHelper.lastWord)
 
+    val autoCompleteBottomSheet = BottomSheetBehavior.from(autoCompleteList)
     if (autoCompleteSettings.prefix || autoCompleteSettings.auto) {
       autoCompleteAdapter.setOnClickListener(chatline::autoComplete)
-      autoCompleteList.layoutManager = LinearLayoutManager(activity)
+      autoCompleteList.layoutManager = LinearLayoutManager(context)
       autoCompleteList.itemAnimator = DefaultItemAnimator()
       autoCompleteList.adapter = autoCompleteAdapter
-      autoCompleteHelper.setDataListener {
+      autoCompleteHelper.addDataListener {
+        autoCompleteBottomSheet.state =
+          if (it.isEmpty()) BottomSheetBehavior.STATE_HIDDEN
+          else BottomSheetBehavior.STATE_COLLAPSED
         autoCompleteAdapter.submitList(it)
       }
     }
 
+    historyBottomSheet = BottomSheetBehavior.from(cardPanel)
+    historyBottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
     messageHistory.itemAnimator = DefaultItemAnimator()
     messageHistory.layoutManager = LinearLayoutManager(requireContext())
     val messageHistoryAdapter = MessageHistoryAdapter()
     messageHistoryAdapter.setOnItemClickListener { text ->
       editorHelper.replaceText(text)
-      historyPanel.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
+      historyBottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
+    }
+    close.setOnClickListener {
+      historyBottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
     }
     messageHistory.adapter = messageHistoryAdapter
     viewModel.recentlySentMessages.toLiveData()
@@ -186,7 +201,7 @@ class ChatlineFragment : ServiceBoundFragment() {
     toolbar.setOnMenuItemClickListener {
       when (it.itemId) {
         R.id.action_input_history -> {
-          historyPanel.panelState = SlidingUpPanelLayout.PanelState.EXPANDED
+          historyBottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
           true
         }
         else                      -> false
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
index a72f0dfd3..b73400456 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/RichEditText.kt
@@ -279,9 +279,9 @@ class RichEditText : EditTextSelectionChange {
     val selectionEnd = selectionEnd
 
     inputType = if (enabled) {
-      inputType and InputType.TYPE_TEXT_FLAG_MULTI_LINE.inv()
-    } else {
       inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
+    } else {
+      inputType and InputType.TYPE_TEXT_FLAG_MULTI_LINE.inv()
     }
 
     setSelection(selectionStart, selectionEnd)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
index 8d5a21025..6efb38992 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt
@@ -106,7 +106,7 @@ class TopicFragment : SettingsFragment(), SettingsFragment.Savable {
       autoCompleteList.layoutManager = LinearLayoutManager(activity)
       autoCompleteList.itemAnimator = DefaultItemAnimator()
       autoCompleteList.adapter = autoCompleteAdapter
-      autoCompleteHelper.setDataListener {
+      autoCompleteHelper.addDataListener {
         autoCompleteAdapter.submitList(it)
       }
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
index 9c0315ce3..86bd2b5d0 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragment.kt
@@ -114,12 +114,6 @@ class AboutFragment : DaggerFragment() {
         license = apache2,
         url = "https://android.googlesource.com/platform/frameworks/support/+/master/persistence"
       ),
-      Library(
-        name = "Android Sliding Up Panel",
-        version = "3.5.0",
-        license = apache2,
-        url = "https://github.com/umano/AndroidSlidingUpPanel"
-      ),
       Library(
         name = "Android Support Library",
         version = "27.1.1",
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt
index dd5b2bc89..e1e7cd6b3 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt
@@ -119,7 +119,7 @@ class AliasItemFragment : SettingsFragment(), SettingsFragment.Savable,
       autoCompleteList.layoutManager = LinearLayoutManager(activity)
       autoCompleteList.itemAnimator = DefaultItemAnimator()
       autoCompleteList.adapter = autoCompleteAdapter
-      autoCompleteHelper.setDataListener {
+      autoCompleteHelper.addDataListener {
         autoCompleteAdapter.submitList(it)
       }
     }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/DragInterceptBottomSheetBehavior.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/DragInterceptBottomSheetBehavior.kt
new file mode 100644
index 000000000..0234f9c01
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/DragInterceptBottomSheetBehavior.kt
@@ -0,0 +1,56 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util.ui
+
+import android.content.Context
+import android.support.design.widget.BottomSheetBehavior
+import android.support.design.widget.CoordinatorLayout
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+
+class DragInterceptBottomSheetBehavior<V : View> : BottomSheetBehavior<V> {
+  constructor() : super()
+  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+
+  var allowDragging = true
+
+  override fun onInterceptTouchEvent(parent: CoordinatorLayout?, child: V,
+                                     event: MotionEvent?): Boolean {
+    if (!allowDragging) return false
+    return super.onInterceptTouchEvent(parent, child, event)
+  }
+
+  companion object {
+    /**
+     * A utility function to get the [BottomSheetBehavior] associated with the `view`.
+     *
+     * @param view The [View] with [BottomSheetBehavior].
+     * @return The [BottomSheetBehavior] associated with the `view`.
+     */
+    fun <V : View> from(view: V?): DragInterceptBottomSheetBehavior<V> {
+      val params = view?.layoutParams as? CoordinatorLayout.LayoutParams
+                   ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout")
+      val behavior = params.behavior as? DragInterceptBottomSheetBehavior<*>
+                     ?: throw IllegalArgumentException("The view is not associated with BottomSheetBehavior")
+      return behavior as DragInterceptBottomSheetBehavior<V>
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/ShadowView.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ShadowView.java
new file mode 100644
index 000000000..3b4389bfb
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/ShadowView.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 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 de.kuschku.quasseldroid.util.ui;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.util.AttributeSet;
+import android.util.LruCache;
+import android.view.Gravity;
+import android.view.View;
+
+import de.kuschku.quasseldroid.R;
+
+/**
+ * Shadow view based on the {@code ScrimUtil.java} class from the Muzei App. Take a look at
+ * <a href="https://plus.google.com/+RomanNurik/posts/2QvHVFWrHZf">this post</a> from Roman
+ * Nurik for more details. Find the source code
+ * <a href="https://github.com/romannurik/muzei/blob/master/main/src/main/java/com/google/android/apps/muzei/util/ScrimUtil.java">here</a>.
+ */
+public class ShadowView extends View {
+
+  private static final LruCache<Integer, Drawable> cubicGradientScrimCache = new LruCache<>(10);
+
+  public ShadowView(Context context) {
+    super(context);
+    initialize(context, null, 0, 0);
+  }
+
+  public ShadowView(Context context, @Nullable AttributeSet attrs) {
+    super(context, attrs);
+    initialize(context, attrs, 0, 0);
+  }
+
+  public ShadowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    initialize(context, attrs, defStyleAttr, 0);
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+  public ShadowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    super(context, attrs, defStyleAttr, defStyleRes);
+    initialize(context, attrs, defStyleAttr, defStyleRes);
+  }
+
+  /**
+   * Initializes the view.
+   *
+   * @param context      The Context the view is running in, through which it can
+   *                     access the current theme, resources, etc.
+   * @param attrs        The attributes of the XML tag that is inflating the view.
+   * @param defStyleAttr An attribute in the current theme that contains a
+   *                     reference to a style resource that supplies default values for
+   *                     the view. Can be 0 to not look for defaults.
+   * @param defStyleRes  A resource identifier of a style resource that
+   *                     supplies default values for the view, used only if
+   *                     defStyleAttr is 0 or can not be found in the theme. Can be 0
+   *                     to not look for defaults.
+   * @see View(Context, AttributeSet, int)
+   */
+  private void initialize(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    int gravity = Gravity.TOP;
+
+    // Get the attributes.
+    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShadowView, defStyleAttr, defStyleRes);
+    try {
+      if (a != null) {
+        gravity = a.getInt(R.styleable.ShadowView_android_gravity, gravity);
+      }
+
+    } finally {
+      if (a != null) a.recycle();
+    }
+
+    // Set the gradient as background.
+    setBackground(makeCubicGradientScrimDrawable(0x44000000, 8, gravity));
+  }
+
+  /**
+   * Creates an approximated cubic gradient using a multi-stop linear gradient.
+   */
+  private Drawable makeCubicGradientScrimDrawable(int baseColor, int numStops, int gravity) {
+
+    // Generate a cache key by hashing together the inputs, based on the method described in the Effective Java book
+    int cacheKeyHash = baseColor;
+    cacheKeyHash = 31 * cacheKeyHash + numStops;
+    cacheKeyHash = 31 * cacheKeyHash + gravity;
+
+    Drawable cachedGradient = cubicGradientScrimCache.get(cacheKeyHash);
+    if (cachedGradient != null) {
+      return cachedGradient;
+    }
+
+    numStops = Math.max(numStops, 2);
+
+    PaintDrawable paintDrawable = new PaintDrawable();
+    paintDrawable.setShape(new RectShape());
+
+    final int[] stopColors = new int[numStops];
+
+    int red = Color.red(baseColor);
+    int green = Color.green(baseColor);
+    int blue = Color.blue(baseColor);
+    int alpha = Color.alpha(baseColor);
+
+    for (int i = 0; i < numStops; i++) {
+      float x = i * 1f / (numStops - 1);
+      float opacity = constrain(0, 1, (float) Math.pow(x, 3));
+      stopColors[i] = Color.argb((int) (alpha * opacity), red, green, blue);
+    }
+
+    final float x0, x1, y0, y1;
+    switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+      case Gravity.LEFT:
+        x0 = 1;
+        x1 = 0;
+        break;
+      case Gravity.RIGHT:
+        x0 = 0;
+        x1 = 1;
+        break;
+      default:
+        x0 = 0;
+        x1 = 0;
+        break;
+    }
+    switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+      case Gravity.TOP:
+        y0 = 1;
+        y1 = 0;
+        break;
+      case Gravity.BOTTOM:
+        y0 = 0;
+        y1 = 1;
+        break;
+      default:
+        y0 = 0;
+        y1 = 0;
+        break;
+    }
+
+    paintDrawable.setShaderFactory(new ShapeDrawable.ShaderFactory() {
+      @Override
+      public Shader resize(int width, int height) {
+        return new LinearGradient(
+          width * x0,
+          height * y0,
+          width * x1,
+          height * y1,
+          stopColors, null,
+          Shader.TileMode.CLAMP);
+      }
+    });
+
+    cubicGradientScrimCache.put(cacheKeyHash, paintDrawable);
+    return paintDrawable;
+  }
+
+  private float constrain(float min, float max, float v) {
+    return Math.max(min, Math.min(max, v));
+  }
+
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/TouchInterceptingFrameLayout.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/TouchInterceptingFrameLayout.kt
new file mode 100644
index 000000000..0d9731185
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/TouchInterceptingFrameLayout.kt
@@ -0,0 +1,38 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2018 Janne Koschinski
+ * Copyright (c) 2018 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.util.ui
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.widget.FrameLayout
+
+class TouchInterceptingFrameLayout : FrameLayout {
+  constructor(context: Context?) : super(context)
+  constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+    super(context, attrs, defStyleAttr)
+
+  @SuppressLint("ClickableViewAccessibility")
+  override fun onTouchEvent(event: MotionEvent): Boolean {
+    return true
+  }
+}
diff --git a/app/src/main/res/layout-land/layout_main.xml b/app/src/main/res/layout-land/layout_main.xml
index d201b88f2..6ca37163d 100644
--- a/app/src/main/res/layout-land/layout_main.xml
+++ b/app/src/main/res/layout-land/layout_main.xml
@@ -17,47 +17,59 @@
   with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
-  android:id="@+id/editor_panel"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
-  android:gravity="bottom"
-  app:umanoAntiDragView="@id/card_panel"
-  app:umanoPanelHeight="?actionBarSize"
-  app:umanoScrollableView="@id/chatline_scroller"
-  app:umanoShadowHeight="4dp">
+  android:gravity="bottom">
 
-  <LinearLayout
+  <include layout="@layout/layout_toolbar" />
+
+  <android.support.design.widget.CoordinatorLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:fitsSystemWindows="true"
+    android:layout_marginBottom="?actionBarSize"
     android:orientation="vertical">
 
-    <include layout="@layout/layout_toolbar" />
-
     <fragment
       android:id="@+id/fragment_messages"
       android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment"
       android:layout_width="match_parent"
-      android:layout_height="0dip"
-      android:layout_weight="1"
+      android:layout_height="match_parent"
       tools:layout="@layout/fragment_messages" />
 
-    <de.kuschku.quasseldroid.util.ui.AutoCompleteRecyclerView
+    <de.kuschku.quasseldroid.util.ui.ShadowView
+      android:layout_width="match_parent"
+      android:layout_height="16dp"
+      android:gravity="bottom"
+      app:layout_anchor="@id/autocomplete_list" />
+
+    <android.support.v7.widget.RecyclerView
       android:id="@+id/autocomplete_list"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="?colorBackgroundCard"
+      app:behavior_hideable="true"
+      app:behavior_peekHeight="92dp"
+      app:layout_behavior="@string/bottom_sheet_behavior"
       tools:listitem="@layout/widget_nick" />
-  </LinearLayout>
+  </android.support.design.widget.CoordinatorLayout>
+
+  <de.kuschku.quasseldroid.util.ui.ShadowView
+    android:layout_width="match_parent"
+    android:layout_height="16dp"
+    android:gravity="bottom"
+    app:layout_anchor="@id/fragment_chatline" />
 
   <fragment
     android:id="@+id/fragment_chatline"
     android:name="de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    app:behavior_hideable="false"
+    app:behavior_peekHeight="?actionBarSize"
+    app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior"
     tools:layout="@layout/fragment_chatline" />
 
-</com.sothree.slidinguppanel.SlidingUpPanelLayout>
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout-sw600dp-land/layout_main.xml b/app/src/main/res/layout-sw600dp-land/layout_main.xml
index 287a799fa..4cc02bced 100644
--- a/app/src/main/res/layout-sw600dp-land/layout_main.xml
+++ b/app/src/main/res/layout-sw600dp-land/layout_main.xml
@@ -27,45 +27,51 @@
 
   <include layout="@layout/layout_toolbar" />
 
-  <com.sothree.slidinguppanel.SlidingUpPanelLayout
-    android:id="@+id/editor_panel"
+  <android.support.design.widget.CoordinatorLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="bottom"
-    app:umanoAntiDragView="@id/card_panel"
-    app:umanoPanelHeight="?actionBarSize"
-    app:umanoScrollableView="@id/chatline_scroller"
-    app:umanoShadowHeight="4dp">
+    android:gravity="bottom">
 
-    <LinearLayout
+    <android.support.design.widget.CoordinatorLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
+      android:layout_marginBottom="?actionBarSize"
       android:orientation="vertical">
 
       <fragment
         android:id="@+id/fragment_messages"
         android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment"
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"
+        android:layout_height="match_parent"
         tools:layout="@layout/fragment_messages" />
 
-      <de.kuschku.quasseldroid.util.ui.AutoCompleteRecyclerView
+      <android.support.v7.widget.RecyclerView
         android:id="@+id/autocomplete_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="?colorBackgroundCard"
+        app:behavior_hideable="true"
+        app:behavior_peekHeight="92dp"
+        app:layout_behavior="@string/bottom_sheet_behavior"
         tools:listitem="@layout/widget_nick" />
+    </android.support.design.widget.CoordinatorLayout>
 
-    </LinearLayout>
+    <de.kuschku.quasseldroid.util.ui.ShadowView
+      android:layout_width="match_parent"
+      android:layout_height="16dp"
+      android:gravity="bottom"
+      app:layout_anchor="@id/fragment_chatline" />
 
     <fragment
       android:id="@+id/fragment_chatline"
       android:name="de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
+      app:behavior_hideable="false"
+      app:behavior_peekHeight="?actionBarSize"
+      app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior"
       tools:layout="@layout/fragment_chatline" />
 
-  </com.sothree.slidinguppanel.SlidingUpPanelLayout>
+  </android.support.design.widget.CoordinatorLayout>
 
 </LinearLayout>
diff --git a/app/src/main/res/layout/fragment_chatline.xml b/app/src/main/res/layout/fragment_chatline.xml
index fefc8781a..f79e20010 100644
--- a/app/src/main/res/layout/fragment_chatline.xml
+++ b/app/src/main/res/layout/fragment_chatline.xml
@@ -17,20 +17,15 @@
   with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:android="http://schemas.android.com/apk/res/android"
-  xmlns:app="http://schemas.android.com/apk/res-auto"
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/history_panel"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
-  android:gravity="bottom"
-  app:umanoFadeColor="?colorBackground"
-  app:umanoOverlay="true"
-  app:umanoPanelHeight="0dip"
-  app:umanoScrollableView="@id/msg_history"
-  app:umanoShadowHeight="0dip">
+  android:background="?colorBackground"
+  android:gravity="bottom">
 
   <include layout="@layout/layout_editor" />
 
   <include layout="@layout/layout_history" />
 
-</com.sothree.slidinguppanel.SlidingUpPanelLayout>
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/layout_editor.xml b/app/src/main/res/layout/layout_editor.xml
index 44e0fc4ce..41d4276d0 100644
--- a/app/src/main/res/layout/layout_editor.xml
+++ b/app/src/main/res/layout/layout_editor.xml
@@ -17,75 +17,92 @@
   with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
+  android:id="@+id/editor_container"
   android:layout_width="match_parent"
-  android:layout_height="match_parent">
+  android:layout_height="match_parent"
+  android:orientation="vertical">
 
-  <android.support.v7.widget.AppCompatImageButton
-    android:id="@+id/tab_complete"
-    style="?attr/buttonStyleSmall"
-    android:layout_width="?attr/actionBarSize"
-    android:layout_height="?attr/actionBarSize"
-    android:layout_gravity="top"
-    android:background="?attr/selectableItemBackgroundBorderless"
-    android:padding="12dp"
-    android:scaleType="fitXY"
-    app:layout_constraintStart_toStartOf="parent"
-    app:srcCompat="@drawable/ic_tab"
-    app:tint="?attr/colorTextSecondary" />
-
-  <ScrollView
-    android:id="@+id/chatline_scroller"
-    android:layout_width="0dp"
-    android:layout_height="0dp"
-    app:layout_constraintBottom_toTopOf="@+id/autocomplete_list"
-    app:layout_constraintEnd_toStartOf="@+id/send"
-    app:layout_constraintHorizontal_bias="1.0"
-    app:layout_constraintStart_toEndOf="@+id/tab_complete"
-    app:layout_constraintTop_toTopOf="parent">
+  <android.support.design.widget.CoordinatorLayout
+    android:layout_width="match_parent"
+    android:layout_height="0dip"
+    android:layout_weight="1"
+    android:orientation="vertical">
 
-    <de.kuschku.quasseldroid.ui.chat.input.RichEditText
-      android:id="@+id/chatline"
-      style="@style/Widget.RtlConformTextView"
+    <android.support.constraint.ConstraintLayout
       android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:background="@android:color/transparent"
-      android:gravity="center_vertical"
-      android:hint="@string/label_placeholder_message"
-      android:imeOptions="flagNoExtractUi"
-      android:inputType="textCapSentences|textAutoCorrect|textShortMessage"
-      android:minHeight="?attr/actionBarSize"
-      android:paddingBottom="8dp"
-      android:paddingLeft="20dp"
-      android:paddingRight="20dp"
-      android:paddingTop="8dp"
-      android:textColor="?attr/colorForeground"
-      android:textSize="16sp" />
-  </ScrollView>
+      android:layout_height="match_parent">
 
-  <android.support.v7.widget.AppCompatImageButton
-    android:id="@+id/send"
-    style="?attr/buttonStyleSmall"
-    android:layout_width="?attr/actionBarSize"
-    android:layout_height="?attr/actionBarSize"
-    android:layout_gravity="top"
-    android:autoMirrored="true"
-    android:background="?attr/selectableItemBackgroundBorderless"
-    android:padding="12dp"
-    android:scaleType="fitXY"
-    app:layout_constraintEnd_toEndOf="parent"
-    app:srcCompat="@drawable/ic_send"
-    app:tint="?attr/colorAccent"
-    tools:ignore="UnusedAttribute" />
+      <android.support.v7.widget.AppCompatImageButton
+        android:id="@+id/tab_complete"
+        style="?attr/buttonStyleSmall"
+        android:layout_width="?attr/actionBarSize"
+        android:layout_height="?attr/actionBarSize"
+        android:layout_gravity="top"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:padding="12dp"
+        android:scaleType="fitXY"
+        app:layout_constraintStart_toStartOf="parent"
+        app:srcCompat="@drawable/ic_tab"
+        app:tint="?attr/colorTextSecondary" />
 
-  <de.kuschku.quasseldroid.util.ui.AutoCompleteRecyclerView
-    android:id="@+id/autocomplete_list"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    app:layout_constraintBottom_toTopOf="@+id/formatting_toolbar_container"
-    tools:listitem="@layout/widget_nick" />
+      <ScrollView
+        android:id="@+id/chatline_scroller"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/send"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toEndOf="@+id/tab_complete"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <de.kuschku.quasseldroid.ui.chat.input.RichEditText
+          android:id="@+id/chatline"
+          style="@style/Widget.RtlConformTextView"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:background="@android:color/transparent"
+          android:gravity="center_vertical"
+          android:hint="@string/label_placeholder_message"
+          android:imeOptions="flagNoExtractUi"
+          android:inputType="textCapSentences|textAutoCorrect|textShortMessage"
+          android:minHeight="?attr/actionBarSize"
+          android:paddingBottom="8dp"
+          android:paddingLeft="20dp"
+          android:paddingRight="20dp"
+          android:paddingTop="8dp"
+          android:textColor="?attr/colorForeground"
+          android:textSize="16sp" />
+      </ScrollView>
+
+      <android.support.v7.widget.AppCompatImageButton
+        android:id="@+id/send"
+        style="?attr/buttonStyleSmall"
+        android:layout_width="?attr/actionBarSize"
+        android:layout_height="?attr/actionBarSize"
+        android:layout_gravity="top"
+        android:autoMirrored="true"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:padding="12dp"
+        android:scaleType="fitXY"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:srcCompat="@drawable/ic_send"
+        app:tint="?attr/colorAccent"
+        tools:ignore="UnusedAttribute" />
+    </android.support.constraint.ConstraintLayout>
+
+    <android.support.v7.widget.RecyclerView
+      android:id="@+id/autocomplete_list"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:background="?colorBackgroundCard"
+      app:behavior_hideable="true"
+      app:behavior_peekHeight="@dimen/autocomplete_max_height"
+      app:layout_behavior="@string/bottom_sheet_behavior"
+      tools:listitem="@layout/widget_nick" />
+  </android.support.design.widget.CoordinatorLayout>
 
   <android.support.design.widget.AppBarLayout
     android:id="@+id/formatting_toolbar_container"
@@ -100,4 +117,4 @@
       android:layout_height="?attr/actionBarSize"
       app:contentInsetStart="0dip" />
   </android.support.design.widget.AppBarLayout>
-</android.support.constraint.ConstraintLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/layout_history.xml b/app/src/main/res/layout/layout_history.xml
index 8d00232bf..02f37ca28 100644
--- a/app/src/main/res/layout/layout_history.xml
+++ b/app/src/main/res/layout/layout_history.xml
@@ -17,13 +17,16 @@
   with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<de.kuschku.quasseldroid.util.ui.TouchInterceptingFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/card_panel"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
-  tools:ignore="MergeRootFrame">
+  app:behavior_hideable="true"
+  app:behavior_skipCollapsed="true"
+  app:layout_behavior="@string/bottom_sheet_behavior"
+  tools:ignore="KeyboardInaccessibleWidget">
 
   <android.support.v7.widget.CardView
     style="?attr/cardStyle"
@@ -61,7 +64,7 @@
           android:layout_height="match_parent"
           android:layout_weight="1" />
 
-        <android.support.v7.widget.AppCompatImageView
+        <android.support.v7.widget.AppCompatImageButton
           android:id="@+id/close"
           android:layout_width="48dp"
           android:layout_height="48dp"
@@ -69,7 +72,7 @@
           android:background="?attr/selectableItemBackgroundBorderless"
           android:padding="12dp"
           android:scaleType="fitXY"
-          app:srcCompat="@drawable/ic_close"
+          app:srcCompat="@drawable/ic_chevron_down"
           app:tint="?attr/colorForegroundSecondary" />
       </LinearLayout>
 
@@ -80,4 +83,4 @@
         tools:listitem="@layout/widget_history_message" />
     </LinearLayout>
   </android.support.v7.widget.CardView>
-</FrameLayout>
+</de.kuschku.quasseldroid.util.ui.TouchInterceptingFrameLayout>
diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml
index af2067d7c..542176b9e 100644
--- a/app/src/main/res/layout/layout_main.xml
+++ b/app/src/main/res/layout/layout_main.xml
@@ -27,44 +27,51 @@
 
   <include layout="@layout/layout_toolbar" />
 
-  <com.sothree.slidinguppanel.SlidingUpPanelLayout
-    android:id="@+id/editor_panel"
+  <android.support.design.widget.CoordinatorLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="bottom"
-    app:umanoAntiDragView="@id/card_panel"
-    app:umanoPanelHeight="?actionBarSize"
-    app:umanoScrollableView="@id/chatline_scroller"
-    app:umanoShadowHeight="4dp">
+    android:gravity="bottom">
 
-    <LinearLayout
+    <android.support.design.widget.CoordinatorLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
+      android:layout_marginBottom="?actionBarSize"
       android:orientation="vertical">
 
       <fragment
         android:id="@+id/fragment_messages"
         android:name="de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment"
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"
+        android:layout_height="match_parent"
         tools:layout="@layout/fragment_messages" />
 
-      <de.kuschku.quasseldroid.util.ui.AutoCompleteRecyclerView
+      <android.support.v7.widget.RecyclerView
         android:id="@+id/autocomplete_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="?colorBackgroundCard"
+        app:behavior_hideable="true"
+        app:behavior_peekHeight="@dimen/autocomplete_max_height"
+        app:layout_behavior="@string/bottom_sheet_behavior"
         tools:listitem="@layout/widget_nick" />
-    </LinearLayout>
+    </android.support.design.widget.CoordinatorLayout>
+
+    <de.kuschku.quasseldroid.util.ui.ShadowView
+      android:layout_width="match_parent"
+      android:layout_height="16dp"
+      android:gravity="bottom"
+      app:layout_anchor="@id/fragment_chatline" />
 
     <fragment
       android:id="@+id/fragment_chatline"
       android:name="de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
+      app:behavior_hideable="false"
+      app:behavior_peekHeight="?actionBarSize"
+      app:layout_behavior="@string/drag_intercept_bottom_sheet_behavior"
       tools:layout="@layout/fragment_chatline" />
 
-  </com.sothree.slidinguppanel.SlidingUpPanelLayout>
+  </android.support.design.widget.CoordinatorLayout>
 
 </LinearLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9399e8862..25530b8c4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -113,4 +113,6 @@
 
   <string name="delete_confirmation">Are you sure you want to delete this permanently? This can not be undone.</string>
   <string name="cancel_confirmation">You have unsaved changes. Do you wish to discard them?</string>
+
+  <string name="drag_intercept_bottom_sheet_behavior" translatable="false">de.kuschku.quasseldroid.util.ui.DragInterceptBottomSheetBehavior</string>
 </resources>
diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml
index 4f456518a..202296289 100644
--- a/app/src/main/res/values/styles_widgets.xml
+++ b/app/src/main/res/values/styles_widgets.xml
@@ -275,6 +275,10 @@
     <item name="android:textStyle">bold</item>
   </style>
 
+  <style name="Widget.DrawerRecyclerView" parent="">
+    <item name="insetBackground">#4000</item>
+  </style>
+
   <!-- NavigationDrawerLayout -->
   <declare-styleable name="NavigationDrawerLayout">
     <attr name="insetBackground" />
@@ -289,7 +293,8 @@
     <attr name="insetBackground" />
   </declare-styleable>
 
-  <style name="Widget.DrawerRecyclerView" parent="">
-    <item name="insetBackground">#4000</item>
-  </style>
+  <!-- ShadowView -->
+  <declare-styleable name="ShadowView">
+    <attr name="android:gravity" />
+  </declare-styleable>
 </resources>
diff --git a/settings.gradle b/settings.gradle
index 4377405dd..eba2c7c83 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -22,7 +22,6 @@ include ':invokerannotations',
         ':lib',
         ":viewmodel",
         ":persistence",
-        ":slidingpanel",
         ':malheur',
         ':app'
 
diff --git a/slidingpanel/build.gradle.kts b/slidingpanel/build.gradle.kts
deleted file mode 100644
index 273bf0043..000000000
--- a/slidingpanel/build.gradle.kts
+++ /dev/null
@@ -1,32 +0,0 @@
-plugins {
-  id("com.android.library")
-}
-
-android {
-  compileSdkVersion(27)
-  buildToolsVersion("27.0.3")
-
-  defaultConfig {
-    minSdkVersion(14)
-    targetSdkVersion(27)
-
-    // Disable test runner analytics
-    testInstrumentationRunnerArguments = mapOf(
-      "disableAnalytics" to "true"
-    )
-  }
-
-  lintOptions {
-    isWarningsAsErrors = true
-    setLintConfig(file("../lint.xml"))
-  }
-}
-
-dependencies {
-  // App Compat
-  withVersion("27.1.1") {
-    implementation("com.android.support", "support-v4", version)
-    implementation("com.android.support", "support-annotations", version)
-    implementation("com.android.support", "recyclerview-v7", version)
-  }
-}
diff --git a/slidingpanel/src/main/AndroidManifest.xml b/slidingpanel/src/main/AndroidManifest.xml
deleted file mode 100644
index 351ee1205..000000000
--- a/slidingpanel/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Quasseldroid - Quassel client for Android
-
-  Copyright (c) 2018 Janne Koschinski
-  Copyright (c) 2018 The Quassel Project
-
-  This program is free software: you can redistribute it and/or modify it
-  under the terms of the GNU General Public License version 3 as published
-  by the Free Software Foundation.
-
-  This program 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.
-
-  You should have received a copy of the GNU General Public License along
-  with this program.  If not, see <http://www.gnu.org/licenses/>.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-  package="com.sothree.slidinguppanel.library"
-  android:versionCode="17"
-  android:versionName="3.4.0" />
diff --git a/slidingpanel/src/main/java/com/sothree/slidinguppanel/ScrollableViewHelper.java b/slidingpanel/src/main/java/com/sothree/slidinguppanel/ScrollableViewHelper.java
deleted file mode 100644
index e5aff3773..000000000
--- a/slidingpanel/src/main/java/com/sothree/slidinguppanel/ScrollableViewHelper.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2018 Janne Koschinski
- * Copyright (c) 2018 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.sothree.slidinguppanel;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.ListView;
-import android.widget.ScrollView;
-
-/**
- * Helper class for determining the current scroll positions for scrollable views. Currently works
- * for ListView, ScrollView and RecyclerView, but the library users can override it to add support
- * for other views.
- */
-public class ScrollableViewHelper {
-  /**
-   * Returns the current scroll position of the scrollable view. If this method returns zero or
-   * less, it means at the scrollable view is in a position such as the panel should handle
-   * scrolling. If the method returns anything above zero, then the panel will let the scrollable
-   * view handle the scrolling
-   *
-   * @param scrollableView the scrollable view
-   * @param isSlidingUp    whether or not the panel is sliding up or down
-   * @return the scroll position
-   */
-  public int getScrollableViewScrollPosition(View scrollableView, boolean isSlidingUp) {
-    if (scrollableView == null) return 0;
-    if (scrollableView instanceof ScrollView) {
-      if (isSlidingUp) {
-        return scrollableView.getScrollY();
-      } else {
-        ScrollView sv = ((ScrollView) scrollableView);
-        View child = sv.getChildAt(0);
-        return (child.getBottom() - (sv.getHeight() + sv.getScrollY()));
-      }
-    } else if (scrollableView instanceof ListView && ((ListView) scrollableView).getChildCount() > 0) {
-      ListView lv = ((ListView) scrollableView);
-      if (lv.getAdapter() == null) return 0;
-      if (isSlidingUp) {
-        View firstChild = lv.getChildAt(0);
-        // Approximate the scroll position based on the top child and the first visible item
-        return lv.getFirstVisiblePosition() * firstChild.getHeight() - firstChild.getTop();
-      } else {
-        View lastChild = lv.getChildAt(lv.getChildCount() - 1);
-        // Approximate the scroll position based on the bottom child and the last visible item
-        return (lv.getAdapter().getCount() - lv.getLastVisiblePosition() - 1) * lastChild.getHeight() + lastChild.getBottom() - lv.getBottom();
-      }
-    } else if (scrollableView instanceof RecyclerView && ((RecyclerView) scrollableView).getChildCount() > 0) {
-      RecyclerView rv = ((RecyclerView) scrollableView);
-      RecyclerView.LayoutManager lm = rv.getLayoutManager();
-      if (rv.getAdapter() == null) return 0;
-      if (isSlidingUp) {
-        View firstChild = rv.getChildAt(0);
-        // Approximate the scroll position based on the top child and the first visible item
-        return rv.getChildLayoutPosition(firstChild) * lm.getDecoratedMeasuredHeight(firstChild) - lm.getDecoratedTop(firstChild);
-      } else {
-        View lastChild = rv.getChildAt(rv.getChildCount() - 1);
-        // Approximate the scroll position based on the bottom child and the last visible item
-        return (rv.getAdapter().getItemCount() - 1) * lm.getDecoratedMeasuredHeight(lastChild) + lm.getDecoratedBottom(lastChild) - rv.getBottom();
-      }
-    } else {
-      return 0;
-    }
-  }
-}
diff --git a/slidingpanel/src/main/java/com/sothree/slidinguppanel/SlidingUpPanelLayout.java b/slidingpanel/src/main/java/com/sothree/slidinguppanel/SlidingUpPanelLayout.java
deleted file mode 100644
index dd9006a69..000000000
--- a/slidingpanel/src/main/java/com/sothree/slidinguppanel/SlidingUpPanelLayout.java
+++ /dev/null
@@ -1,1506 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2018 Janne Koschinski
- * Copyright (c) 2018 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.sothree.slidinguppanel;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.sothree.slidinguppanel.library.R;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public class SlidingUpPanelLayout extends ViewGroup {
-
-  /**
-   * Tag for the sliding state stored inside the bundle
-   */
-  public static final String SLIDING_STATE = "sliding_state";
-  private static final String TAG = SlidingUpPanelLayout.class.getSimpleName();
-  /**
-   * Default peeking out panel height
-   */
-  private static final int DEFAULT_PANEL_HEIGHT = 68; // dp;
-  /**
-   * Default anchor point height
-   */
-  private static final float DEFAULT_ANCHOR_POINT = 1.0f; // In relative %
-  /**
-   * Default height of the shadow above the peeking out panel
-   */
-  private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;
-
-  /**
-   * If no fade color is given by default it will fade to 80% gray.
-   */
-  private static final int DEFAULT_FADE_COLOR = 0x99000000;
-
-  /**
-   * Default Minimum velocity that will be detected as a fling
-   */
-  private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second
-  /**
-   * Default is set to false because that is how it was written
-   */
-  private static final boolean DEFAULT_OVERLAY_FLAG = false;
-  /**
-   * Default is set to true for clip panel for performance reasons
-   */
-  private static final boolean DEFAULT_CLIP_PANEL_FLAG = true;
-  /**
-   * Default attributes for layout
-   */
-  private static final int[] DEFAULT_ATTRS = new int[]{
-    android.R.attr.gravity
-  };
-  /**
-   * Default parallax length of the main view
-   */
-  private static final int DEFAULT_PARALLAX_OFFSET = 0;
-  /**
-   * Default initial state for the component
-   */
-  private static final PanelState DEFAULT_SLIDE_STATE = PanelState.COLLAPSED;
-  /**
-   * The paint used to dim the main layout when sliding
-   */
-  private final Paint mCoveredFadePaint = new Paint();
-  /**
-   * Drawable used to draw the shadow between panes.
-   */
-  private final Drawable mShadowDrawable;
-  private final List<PanelSlideListener> mPanelSlideListeners = new CopyOnWriteArrayList<>();
-  private final ViewDragHelper mDragHelper;
-  private final Rect mTmpRect = new Rect();
-  /**
-   * Minimum velocity that will be detected as a fling
-   */
-  private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
-  /**
-   * The fade color used for the panel covered by the slider. 0 = no fading.
-   */
-  private int mCoveredFadeColor = DEFAULT_FADE_COLOR;
-  /**
-   * The size of the overhang in pixels.
-   */
-  private int mPanelHeight = -1;
-  /**
-   * The size of the shadow in pixels.
-   */
-  private int mShadowHeight = -1;
-  /**
-   * Parallax offset
-   */
-  private int mParallaxOffset = -1;
-  /**
-   * True if the collapsed panel should be dragged up.
-   */
-  private boolean mIsSlidingUp;
-  /**
-   * Panel overlays the windows instead of putting it underneath it.
-   */
-  private boolean mOverlayContent = DEFAULT_OVERLAY_FLAG;
-  /**
-   * The main view is clipped to the main top border
-   */
-  private boolean mClipPanel = DEFAULT_CLIP_PANEL_FLAG;
-  /**
-   * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
-   * used for dragging.
-   */
-  private View mDragView;
-  /**
-   * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
-   * used for dragging.
-   */
-  private int mDragViewResId = -1;
-  /**
-   * If provided, the panel can not be dragged by this view. Otherwise, the entire panel can be
-   * used for dragging.
-   */
-  private View mAntiDragView;
-  /**
-   * If provided, the panel can not be dragged by this view. Otherwise, the entire panel can be
-   * used for dragging.
-   */
-  private int mAntiDragViewResId = -1;
-  /**
-   * If provided, the panel will transfer the scroll from this view to itself when needed.
-   */
-  private View mScrollableView;
-  private int mScrollableViewResId;
-  private ScrollableViewHelper mScrollableViewHelper = new ScrollableViewHelper();
-  /**
-   * The child view that can slide, if any.
-   */
-  private View mSlideableView;
-  /**
-   * The main view
-   */
-  private View mMainView;
-  private PanelState mSlideState = DEFAULT_SLIDE_STATE;
-  /**
-   * If the current slide state is DRAGGING, this will store the last non dragging state
-   */
-  private PanelState mLastNotDraggingSlideState = DEFAULT_SLIDE_STATE;
-  /**
-   * How far the panel is offset from its expanded position.
-   * range [0, 1] where 0 = collapsed, 1 = expanded.
-   */
-  private float mSlideOffset;
-  /**
-   * How far in pixels the slideable panel may move.
-   */
-  private int mSlideRange;
-  /**
-   * An anchor point where the panel can stop during sliding
-   */
-  private float mAnchorPoint = 1.f;
-  /**
-   * A panel view is locked into internal scrolling or another condition that
-   * is preventing a drag.
-   */
-  private boolean mIsUnableToDrag;
-  /**
-   * Flag indicating that sliding feature is enabled\disabled
-   */
-  private boolean mIsTouchEnabled;
-  private float mPrevMotionX;
-  private float mPrevMotionY;
-  private float mInitialMotionX;
-  private float mInitialMotionY;
-  private boolean mIsScrollableViewHandlingTouch = false;
-  private View.OnClickListener mFadeOnClickListener;
-  /**
-   * Stores whether or not the pane was expanded the last time it was slideable.
-   * If expand/collapse operations are invoked this state is modified. Used by
-   * instance state save/restore.
-   */
-  private boolean mFirstLayout = true;
-
-  public SlidingUpPanelLayout(Context context) {
-    this(context, null);
-  }
-
-  public SlidingUpPanelLayout(Context context, AttributeSet attrs) {
-    this(context, attrs, 0);
-  }
-
-  public SlidingUpPanelLayout(Context context, AttributeSet attrs, int defStyle) {
-    super(context, attrs, defStyle);
-
-    if (isInEditMode()) {
-      mShadowDrawable = null;
-      mDragHelper = null;
-      return;
-    }
-
-    Interpolator scrollerInterpolator = null;
-    if (attrs != null) {
-      TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS);
-
-      if (defAttrs != null) {
-        int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY);
-        setGravity(gravity);
-        defAttrs.recycle();
-      }
-
-
-      TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpPanelLayout);
-
-      if (ta != null) {
-        mPanelHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoPanelHeight, -1);
-        mShadowHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoShadowHeight, -1);
-        mParallaxOffset = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoParallaxOffset, -1);
-
-        mMinFlingVelocity = ta.getInt(R.styleable.SlidingUpPanelLayout_umanoFlingVelocity, DEFAULT_MIN_FLING_VELOCITY);
-        mCoveredFadeColor = ta.getColor(R.styleable.SlidingUpPanelLayout_umanoFadeColor, DEFAULT_FADE_COLOR);
-
-        mDragViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoDragView, -1);
-        mAntiDragViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoAntiDragView, -1);
-        mScrollableViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoScrollableView, -1);
-
-        mOverlayContent = ta.getBoolean(R.styleable.SlidingUpPanelLayout_umanoOverlay, DEFAULT_OVERLAY_FLAG);
-        mClipPanel = ta.getBoolean(R.styleable.SlidingUpPanelLayout_umanoClipPanel, DEFAULT_CLIP_PANEL_FLAG);
-
-        mAnchorPoint = ta.getFloat(R.styleable.SlidingUpPanelLayout_umanoAnchorPoint, DEFAULT_ANCHOR_POINT);
-
-        mSlideState = PanelState.values()[ta.getInt(R.styleable.SlidingUpPanelLayout_umanoInitialState, DEFAULT_SLIDE_STATE.ordinal())];
-
-        int interpolatorResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoScrollInterpolator, -1);
-        if (interpolatorResId != -1) {
-          scrollerInterpolator = AnimationUtils.loadInterpolator(context, interpolatorResId);
-        }
-        ta.recycle();
-      }
-    }
-
-    final float density = context.getResources().getDisplayMetrics().density;
-    if (mPanelHeight == -1) {
-      mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f);
-    }
-    if (mShadowHeight == -1) {
-      mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);
-    }
-    if (mParallaxOffset == -1) {
-      mParallaxOffset = (int) (DEFAULT_PARALLAX_OFFSET * density);
-    }
-    // If the shadow height is zero, don't show the shadow
-    if (mShadowHeight > 0) {
-      if (mIsSlidingUp) {
-        mShadowDrawable = getResources().getDrawable(R.drawable.above_shadow);
-      } else {
-        mShadowDrawable = getResources().getDrawable(R.drawable.below_shadow);
-      }
-    } else {
-      mShadowDrawable = null;
-    }
-
-    setWillNotDraw(false);
-
-    mDragHelper = ViewDragHelper.create(this, 0.5f, scrollerInterpolator, new DragHelperCallback());
-    mDragHelper.setMinVelocity(mMinFlingVelocity * density);
-
-    mIsTouchEnabled = true;
-  }
-
-  private static boolean hasOpaqueBackground(View v) {
-    final Drawable bg = v.getBackground();
-    return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
-  }
-
-  /**
-   * Set the Drag View after the view is inflated
-   */
-  @Override
-  protected void onFinishInflate() {
-    super.onFinishInflate();
-    if (mDragViewResId != -1) {
-      setDragView(findViewById(mDragViewResId));
-    }
-    if (mAntiDragViewResId != -1) {
-      setAntiDragView(findViewById(mAntiDragViewResId));
-    }
-    if (mScrollableViewResId != -1) {
-      setScrollableView(findViewById(mScrollableViewResId));
-    }
-  }
-
-  public void setGravity(int gravity) {
-    if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) {
-      throw new IllegalArgumentException("gravity must be set to either top or bottom");
-    }
-    mIsSlidingUp = gravity == Gravity.BOTTOM;
-    if (!mFirstLayout) {
-      requestLayout();
-    }
-  }
-
-  /**
-   * @return The ARGB-packed color value used to fade the fixed pane
-   */
-  public int getCoveredFadeColor() {
-    return mCoveredFadeColor;
-  }
-
-  /**
-   * Set the color used to fade the pane covered by the sliding pane out when the pane
-   * will become fully covered in the expanded state.
-   *
-   * @param color An ARGB-packed color value
-   */
-  public void setCoveredFadeColor(int color) {
-    mCoveredFadeColor = color;
-    requestLayout();
-  }
-
-  public boolean isTouchEnabled() {
-    return mIsTouchEnabled && mSlideableView != null && mSlideState != PanelState.HIDDEN;
-  }
-
-  /**
-   * Set sliding enabled flag
-   *
-   * @param enabled flag value
-   */
-  public void setTouchEnabled(boolean enabled) {
-    mIsTouchEnabled = enabled;
-  }
-
-  protected void smoothToBottom() {
-    smoothSlideTo(0, 0);
-  }
-
-  /**
-   * @return The current shadow height
-   */
-  public int getShadowHeight() {
-    return mShadowHeight;
-  }
-
-  /**
-   * Set the shadow height
-   *
-   * @param val A height in pixels
-   */
-  public void setShadowHeight(int val) {
-    mShadowHeight = val;
-    if (!mFirstLayout) {
-      invalidate();
-    }
-  }
-
-  /**
-   * @return The current collapsed panel height
-   */
-  public int getPanelHeight() {
-    return mPanelHeight;
-  }
-
-  /**
-   * Set the collapsed panel height in pixels
-   *
-   * @param val A height in pixels
-   */
-  public void setPanelHeight(int val) {
-    if (getPanelHeight() == val) {
-      return;
-    }
-
-    mPanelHeight = val;
-    if (!mFirstLayout) {
-      requestLayout();
-    }
-
-    if (getPanelState() == PanelState.COLLAPSED) {
-      smoothToBottom();
-      invalidate();
-    }
-  }
-
-  /**
-   * @return The current parallax offset
-   */
-  public int getCurrentParallaxOffset() {
-    // Clamp slide offset at zero for parallax computation;
-    int offset = (int) (mParallaxOffset * Math.max(mSlideOffset, 0));
-    return mIsSlidingUp ? -offset : offset;
-  }
-
-  /**
-   * Set parallax offset for the panel
-   *
-   * @param val A height in pixels
-   */
-  public void setParallaxOffset(int val) {
-    mParallaxOffset = val;
-    if (!mFirstLayout) {
-      requestLayout();
-    }
-  }
-
-  /**
-   * @return The current minimin fling velocity
-   */
-  public int getMinFlingVelocity() {
-    return mMinFlingVelocity;
-  }
-
-  /**
-   * Sets the minimum fling velocity for the panel
-   *
-   * @param val the new value
-   */
-  public void setMinFlingVelocity(int val) {
-    mMinFlingVelocity = val;
-  }
-
-  /**
-   * Adds a panel slide listener
-   */
-  public void addPanelSlideListener(PanelSlideListener listener) {
-    synchronized (mPanelSlideListeners) {
-      mPanelSlideListeners.add(listener);
-    }
-  }
-
-  /**
-   * Removes a panel slide listener
-   */
-  public void removePanelSlideListener(PanelSlideListener listener) {
-    synchronized (mPanelSlideListeners) {
-      mPanelSlideListeners.remove(listener);
-    }
-  }
-
-  /**
-   * Provides an on click for the portion of the main view that is dimmed. The listener is not
-   * triggered if the panel is in a collapsed or a hidden position. If the on click listener is
-   * not provided, the clicks on the dimmed area are passed through to the main layout.
-   */
-  public void setFadeOnClickListener(View.OnClickListener listener) {
-    mFadeOnClickListener = listener;
-  }
-
-  /**
-   * Set the draggable view portion. Use to null, to allow the whole panel to be draggable
-   *
-   * @param dragView A view that will be used to drag the panel.
-   */
-  public void setDragView(View dragView) {
-    if (mDragView != null) {
-      mDragView.setOnClickListener(null);
-    }
-    mDragView = dragView;
-    if (mDragView != null) {
-      mDragView.setClickable(true);
-      mDragView.setFocusable(false);
-      mDragView.setFocusableInTouchMode(false);
-      mDragView.setOnClickListener(new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-          if (!isEnabled() || !isTouchEnabled()) return;
-          if (mSlideState != PanelState.EXPANDED && mSlideState != PanelState.ANCHORED) {
-            if (mAnchorPoint < 1.0f) {
-              setPanelState(PanelState.ANCHORED);
-            } else {
-              setPanelState(PanelState.EXPANDED);
-            }
-          } else {
-            setPanelState(PanelState.COLLAPSED);
-          }
-        }
-      });
-    }
-  }
-
-  /**
-   * Set the draggable view portion. Use to null, to allow the whole panel to be draggable
-   *
-   * @param dragViewResId The resource ID of the new drag view
-   */
-  public void setDragView(int dragViewResId) {
-    mDragViewResId = dragViewResId;
-    setDragView(findViewById(dragViewResId));
-  }
-
-  /**
-   * Set a portion of the view that shall not be draggable. Use null to make everything draggable
-   *
-   * @param antiDragViewResId The resource ID of the anti drag view
-   */
-  public void setAntiDragView(int antiDragViewResId) {
-    mAntiDragViewResId = antiDragViewResId;
-    setAntiDragView(findViewById(antiDragViewResId));
-  }
-
-  /**
-   * Set a portion of the view that shall not be draggable. Use null to make everything draggable
-   *
-   * @param antiDragView A view that will be used to drag the panel.
-   */
-  public void setAntiDragView(View antiDragView) {
-    this.mAntiDragView = antiDragView;
-  }
-
-  /**
-   * Set the scrollable child of the sliding layout. If set, scrolling will be transfered between
-   * the panel and the view when necessary
-   *
-   * @param scrollableView The scrollable view
-   */
-  public void setScrollableView(View scrollableView) {
-    mScrollableView = scrollableView;
-  }
-
-  /**
-   * Sets the current scrollable view helper. See ScrollableViewHelper description for details.
-   */
-  public void setScrollableViewHelper(ScrollableViewHelper helper) {
-    mScrollableViewHelper = helper;
-  }
-
-  /**
-   * Gets the currently set anchor point
-   *
-   * @return the currently set anchor point
-   */
-  public float getAnchorPoint() {
-    return mAnchorPoint;
-  }
-
-  /**
-   * Set an anchor point where the panel can stop during sliding
-   *
-   * @param anchorPoint A value between 0 and 1, determining the position of the anchor point
-   *                    starting from the top of the layout.
-   */
-  public void setAnchorPoint(float anchorPoint) {
-    if (anchorPoint > 0 && anchorPoint <= 1) {
-      mAnchorPoint = anchorPoint;
-      mFirstLayout = true;
-      requestLayout();
-    }
-  }
-
-  /**
-   * Check if the panel is set as an overlay.
-   */
-  public boolean isOverlayed() {
-    return mOverlayContent;
-  }
-
-  /**
-   * Sets whether or not the panel overlays the content
-   */
-  public void setOverlayed(boolean overlayed) {
-    mOverlayContent = overlayed;
-  }
-
-  /**
-   * Check whether or not the main content is clipped to the top of the panel
-   */
-  public boolean isClipPanel() {
-    return mClipPanel;
-  }
-
-  /**
-   * Sets whether or not the main content is clipped to the top of the panel
-   */
-  public void setClipPanel(boolean clip) {
-    mClipPanel = clip;
-  }
-
-  void dispatchOnPanelSlide(View panel) {
-    synchronized (mPanelSlideListeners) {
-      for (PanelSlideListener l : mPanelSlideListeners) {
-        l.onPanelSlide(panel, mSlideOffset);
-      }
-    }
-  }
-
-  void dispatchOnPanelStateChanged(View panel, PanelState previousState, PanelState newState) {
-    synchronized (mPanelSlideListeners) {
-      for (PanelSlideListener l : mPanelSlideListeners) {
-        l.onPanelStateChanged(panel, previousState, newState);
-      }
-    }
-    sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-  }
-
-  void updateObscuredViewVisibility() {
-    if (getChildCount() == 0) {
-      return;
-    }
-    final int leftBound = getPaddingLeft();
-    final int rightBound = getWidth() - getPaddingRight();
-    final int topBound = getPaddingTop();
-    final int bottomBound = getHeight() - getPaddingBottom();
-    final int left;
-    final int right;
-    final int top;
-    final int bottom;
-    if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) {
-      left = mSlideableView.getLeft();
-      right = mSlideableView.getRight();
-      top = mSlideableView.getTop();
-      bottom = mSlideableView.getBottom();
-    } else {
-      left = right = top = bottom = 0;
-    }
-    View child = getChildAt(0);
-    final int clampedChildLeft = Math.max(leftBound, child.getLeft());
-    final int clampedChildTop = Math.max(topBound, child.getTop());
-    final int clampedChildRight = Math.min(rightBound, child.getRight());
-    final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
-    final int vis;
-    if (clampedChildLeft >= left && clampedChildTop >= top &&
-      clampedChildRight <= right && clampedChildBottom <= bottom) {
-      vis = INVISIBLE;
-    } else {
-      vis = VISIBLE;
-    }
-    child.setVisibility(vis);
-  }
-
-  void setAllChildrenVisible() {
-    for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
-      final View child = getChildAt(i);
-      if (child.getVisibility() == INVISIBLE) {
-        child.setVisibility(VISIBLE);
-      }
-    }
-  }
-
-  @Override
-  protected void onAttachedToWindow() {
-    super.onAttachedToWindow();
-    mFirstLayout = true;
-  }
-
-  @Override
-  protected void onDetachedFromWindow() {
-    super.onDetachedFromWindow();
-    mFirstLayout = true;
-  }
-
-  @Override
-  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-    if (widthMode != MeasureSpec.EXACTLY && widthMode != MeasureSpec.AT_MOST) {
-      throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
-    } else if (heightMode != MeasureSpec.EXACTLY && heightMode != MeasureSpec.AT_MOST) {
-      throw new IllegalStateException("Height must have an exact value or MATCH_PARENT");
-    }
-
-    final int childCount = getChildCount();
-
-    if (childCount != 2) {
-      throw new IllegalStateException("Sliding up panel layout must have exactly 2 children!");
-    }
-
-    mMainView = getChildAt(0);
-    mSlideableView = getChildAt(1);
-    if (mDragView == null) {
-      setDragView(mSlideableView);
-    }
-
-    // If the sliding panel is not visible, then put the whole view in the hidden state
-    if (mSlideableView.getVisibility() != VISIBLE) {
-      mSlideState = PanelState.HIDDEN;
-    }
-
-    int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
-    int layoutWidth = widthSize - getPaddingLeft() - getPaddingRight();
-
-    // First pass. Measure based on child LayoutParams width/height.
-    for (int i = 0; i < childCount; i++) {
-      final View child = getChildAt(i);
-      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-      // We always measure the sliding panel in order to know it's height (needed for show panel)
-      if (child.getVisibility() == GONE && i == 0) {
-        continue;
-      }
-
-      int height = layoutHeight;
-      int width = layoutWidth;
-      if (child == mMainView) {
-        if (!mOverlayContent && mSlideState != PanelState.HIDDEN) {
-          height -= mPanelHeight;
-        }
-
-        width -= lp.leftMargin + lp.rightMargin;
-      } else if (child == mSlideableView) {
-        // The slideable view should be aware of its top margin.
-        // See https://github.com/umano/AndroidSlidingUpPanel/issues/412.
-        height -= lp.topMargin;
-      }
-
-      int childWidthSpec;
-      switch (lp.width) {
-        case LayoutParams.WRAP_CONTENT:
-          childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
-          break;
-        case LayoutParams.MATCH_PARENT:
-          childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
-          break;
-        default:
-          childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-          break;
-      }
-
-      int childHeightSpec;
-      if (lp.height == LayoutParams.WRAP_CONTENT) {
-        childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
-      } else {
-        // Modify the height based on the weight.
-        if (lp.weight > 0 && lp.weight < 1) {
-          height = (int) (height * lp.weight);
-        } else if (lp.height != LayoutParams.MATCH_PARENT) {
-          height = lp.height;
-        }
-        childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-      }
-
-      child.measure(childWidthSpec, childHeightSpec);
-
-      if (child == mSlideableView) {
-        mSlideRange = mSlideableView.getMeasuredHeight() - mPanelHeight;
-      }
-    }
-
-    setMeasuredDimension(widthSize, heightSize);
-  }
-
-  @Override
-  protected void onLayout(boolean changed, int l, int t, int r, int b) {
-    final int paddingLeft = getPaddingLeft();
-    final int paddingTop = getPaddingTop();
-
-    final int childCount = getChildCount();
-
-    if (mFirstLayout) {
-      switch (mSlideState) {
-        case EXPANDED:
-          mSlideOffset = 1.0f;
-          break;
-        case ANCHORED:
-          mSlideOffset = mAnchorPoint;
-          break;
-        case HIDDEN:
-          int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
-          mSlideOffset = computeSlideOffset(newTop);
-          break;
-        default:
-          mSlideOffset = 0.f;
-          break;
-      }
-    }
-
-    for (int i = 0; i < childCount; i++) {
-      final View child = getChildAt(i);
-      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-      // Always layout the sliding view on the first layout
-      if (child.getVisibility() == GONE && (i == 0 || mFirstLayout)) {
-        continue;
-      }
-
-      final int childHeight = child.getMeasuredHeight();
-      int childTop = paddingTop;
-
-      if (child == mSlideableView) {
-        childTop = computePanelTopPosition(mSlideOffset);
-      }
-
-      if (!mIsSlidingUp) {
-        if (child == mMainView && !mOverlayContent) {
-          childTop = computePanelTopPosition(mSlideOffset) + mSlideableView.getMeasuredHeight();
-        }
-      }
-      final int childBottom = childTop + childHeight;
-      final int childLeft = paddingLeft + lp.leftMargin;
-      final int childRight = childLeft + child.getMeasuredWidth();
-
-      child.layout(childLeft, childTop, childRight, childBottom);
-    }
-
-    if (mFirstLayout) {
-      updateObscuredViewVisibility();
-    }
-    applyParallaxForCurrentSlideOffset();
-
-    mFirstLayout = false;
-  }
-
-  @Override
-  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-    super.onSizeChanged(w, h, oldw, oldh);
-    // Recalculate sliding panes and their details
-    if (h != oldh) {
-      mFirstLayout = true;
-    }
-  }
-
-  @Override
-  public boolean onInterceptTouchEvent(MotionEvent ev) {
-    // If the scrollable view is handling touch, never intercept
-    if (mIsScrollableViewHandlingTouch || !isTouchEnabled()) {
-      mDragHelper.abort();
-      return false;
-    }
-
-    final int action = MotionEventCompat.getActionMasked(ev);
-    final float x = ev.getX();
-    final float y = ev.getY();
-    final float adx = Math.abs(x - mInitialMotionX);
-    final float ady = Math.abs(y - mInitialMotionY);
-    final int dragSlop = mDragHelper.getTouchSlop();
-
-    switch (action) {
-      case MotionEvent.ACTION_DOWN: {
-        mIsUnableToDrag = false;
-        mInitialMotionX = x;
-        mInitialMotionY = y;
-        if (!checkTouchingDragView(mDragView, (int) x, (int) y)) {
-          mDragHelper.cancel();
-          mIsUnableToDrag = true;
-          return false;
-        }
-
-        break;
-      }
-
-      case MotionEvent.ACTION_MOVE: {
-        if (ady > dragSlop && adx > ady) {
-          mDragHelper.cancel();
-          mIsUnableToDrag = true;
-          return false;
-        }
-        break;
-      }
-
-      case MotionEvent.ACTION_CANCEL:
-      case MotionEvent.ACTION_UP:
-        // If the dragView is still dragging when we get here, we need to call processTouchEvent
-        // so that the view is settled
-        // Added to make scrollable views work (tokudu)
-        if (mDragHelper.isDragging()) {
-          mDragHelper.processTouchEvent(ev);
-          return true;
-        }
-        // Check if this was a click on the faded part of the screen, and fire off the listener if there is one.
-        if (ady <= dragSlop
-          && adx <= dragSlop
-          && mSlideOffset > 0 && !checkTouchingDragView(mSlideableView, (int) mInitialMotionX, (int) mInitialMotionY) && mFadeOnClickListener != null) {
-          playSoundEffect(android.view.SoundEffectConstants.CLICK);
-          mFadeOnClickListener.onClick(this);
-          return true;
-        }
-        break;
-    }
-    return mDragHelper.shouldInterceptTouchEvent(ev);
-  }
-
-  @SuppressLint("ClickableViewAccessibility")
-  @Override
-  public boolean onTouchEvent(MotionEvent ev) {
-    if (!isEnabled() || !isTouchEnabled()) {
-      return super.onTouchEvent(ev);
-    }
-    try {
-      mDragHelper.processTouchEvent(ev);
-      return true;
-    } catch (Exception ex) {
-      // Ignore the pointer out of range exception
-      return false;
-    }
-  }
-
-  @Override
-  public boolean dispatchTouchEvent(MotionEvent ev) {
-    final int action = MotionEventCompat.getActionMasked(ev);
-
-    if (!isEnabled() || !isTouchEnabled() || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
-      mDragHelper.abort();
-      return super.dispatchTouchEvent(ev);
-    }
-
-    final float x = ev.getX();
-    final float y = ev.getY();
-
-    switch (action) {
-      case MotionEvent.ACTION_DOWN:
-        mIsScrollableViewHandlingTouch = false;
-        mPrevMotionX = x;
-        mPrevMotionY = y;
-        break;
-      case MotionEvent.ACTION_MOVE:
-        float dx = x - mPrevMotionX;
-        float dy = y - mPrevMotionY;
-        mPrevMotionX = x;
-        mPrevMotionY = y;
-
-        if (Math.abs(dx) > Math.abs(dy)) {
-          // Scrolling horizontally, so ignore
-          return super.dispatchTouchEvent(ev);
-        }
-
-        // If the scroll view isn't under the touch, pass the
-        // event along to the dragView.
-        if (!checkTouchingDragView(mScrollableView, (int) mInitialMotionX, (int) mInitialMotionY)) {
-          return super.dispatchTouchEvent(ev);
-        }
-
-        // Which direction (up or down) is the drag moving?
-        if (dy * (mIsSlidingUp ? 1 : -1) > 0) { // Collapsing
-          // Is the child less than fully scrolled?
-          // Then let the child handle it.
-          if (mScrollableViewHelper.getScrollableViewScrollPosition(mScrollableView, mIsSlidingUp) > 0) {
-            mIsScrollableViewHandlingTouch = true;
-            return super.dispatchTouchEvent(ev);
-          }
-
-          // Was the child handling the touch previously?
-          // Then we need to rejigger things so that the
-          // drag panel gets a proper down event.
-          if (mIsScrollableViewHandlingTouch) {
-            // Send an 'UP' event to the child.
-            MotionEvent up = MotionEvent.obtain(ev);
-            up.setAction(MotionEvent.ACTION_CANCEL);
-            super.dispatchTouchEvent(up);
-            up.recycle();
-
-            // Send a 'DOWN' event to the panel. (We'll cheat
-            // and hijack this one)
-            ev.setAction(MotionEvent.ACTION_DOWN);
-          }
-
-          mIsScrollableViewHandlingTouch = false;
-          return this.onTouchEvent(ev);
-        } else if (dy * (mIsSlidingUp ? 1 : -1) < 0) { // Expanding
-          // Is the panel less than fully expanded?
-          // Then we'll handle the drag here.
-          if (mSlideOffset < 1.0f) {
-            mIsScrollableViewHandlingTouch = false;
-            return this.onTouchEvent(ev);
-          }
-
-          // Was the panel handling the touch previously?
-          // Then we need to rejigger things so that the
-          // child gets a proper down event.
-          if (!mIsScrollableViewHandlingTouch && mDragHelper.isDragging()) {
-            mDragHelper.cancel();
-            ev.setAction(MotionEvent.ACTION_DOWN);
-          }
-
-          mIsScrollableViewHandlingTouch = true;
-          return super.dispatchTouchEvent(ev);
-        }
-        break;
-      case MotionEvent.ACTION_UP:
-        // If the scrollable view was handling the touch and we receive an up
-        // we want to clear any previous dragging state so we don't intercept a touch stream accidentally
-        if (mIsScrollableViewHandlingTouch) {
-          mDragHelper.setDragState(ViewDragHelper.STATE_IDLE);
-        }
-        break;
-    }
-
-    // In all other cases, just let the default behavior take over.
-    return super.dispatchTouchEvent(ev);
-  }
-
-  private boolean checkTouchingDragView(View view, int x, int y) {
-    return isViewUnder(view, x, y) && !isViewUnder(mAntiDragView, x, y);
-  }
-
-  private boolean isViewUnder(View view, int x, int y) {
-    if (view == null) return false;
-    int[] viewLocation = new int[2];
-    view.getLocationOnScreen(viewLocation);
-    int[] parentLocation = new int[2];
-    this.getLocationOnScreen(parentLocation);
-    int screenX = parentLocation[0] + x;
-    int screenY = parentLocation[1] + y;
-    return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
-      screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
-  }
-
-  /*
-   * Computes the top position of the panel based on the slide offset.
-   */
-  private int computePanelTopPosition(float slideOffset) {
-    int slidingViewHeight = mSlideableView != null ? mSlideableView.getMeasuredHeight() : 0;
-    int slidePixelOffset = (int) (slideOffset * mSlideRange);
-    // Compute the top of the panel if its collapsed
-    return mIsSlidingUp
-      ? getMeasuredHeight() - getPaddingBottom() - mPanelHeight - slidePixelOffset
-      : getPaddingTop() - slidingViewHeight + mPanelHeight + slidePixelOffset;
-  }
-
-  /*
-   * Computes the slide offset based on the top position of the panel
-   */
-  private float computeSlideOffset(int topPosition) {
-    // Compute the panel top position if the panel is collapsed (offset 0)
-    final int topBoundCollapsed = computePanelTopPosition(0);
-
-    // Determine the new slide offset based on the collapsed top position and the new required
-    // top position
-    return (mIsSlidingUp
-      ? (float) (topBoundCollapsed - topPosition) / mSlideRange
-      : (float) (topPosition - topBoundCollapsed) / mSlideRange);
-  }
-
-  /**
-   * Returns the current state of the panel as an enum.
-   *
-   * @return the current panel state
-   */
-  public PanelState getPanelState() {
-    return mSlideState;
-  }
-
-  /**
-   * Change panel state to the given state with
-   *
-   * @param state - new panel state
-   */
-  public void setPanelState(PanelState state) {
-
-    // Abort any running animation, to allow state change
-    if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING) {
-      Log.d(TAG, "View is settling. Aborting animation.");
-      mDragHelper.abort();
-    }
-
-    if (state == null || state == PanelState.DRAGGING) {
-      throw new IllegalArgumentException("Panel state cannot be null or DRAGGING.");
-    }
-    if (!isEnabled()
-      || (!mFirstLayout && mSlideableView == null)
-      || state == mSlideState
-      || mSlideState == PanelState.DRAGGING) return;
-
-    if (mFirstLayout) {
-      setPanelStateInternal(state);
-    } else {
-      if (mSlideState == PanelState.HIDDEN) {
-        mSlideableView.setVisibility(View.VISIBLE);
-        requestLayout();
-      }
-      switch (state) {
-        case ANCHORED:
-          smoothSlideTo(mAnchorPoint, 0);
-          break;
-        case COLLAPSED:
-          smoothSlideTo(0, 0);
-          break;
-        case EXPANDED:
-          smoothSlideTo(1.0f, 0);
-          break;
-        case HIDDEN:
-          int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
-          smoothSlideTo(computeSlideOffset(newTop), 0);
-          break;
-      }
-    }
-  }
-
-  private void setPanelStateInternal(PanelState state) {
-    if (mSlideState == state) return;
-    PanelState oldState = mSlideState;
-    mSlideState = state;
-    dispatchOnPanelStateChanged(this, oldState, state);
-  }
-
-  /**
-   * Update the parallax based on the current slide offset.
-   */
-  @SuppressLint("NewApi")
-  private void applyParallaxForCurrentSlideOffset() {
-    if (mParallaxOffset > 0) {
-      int mainViewOffset = getCurrentParallaxOffset();
-      ViewCompat.setTranslationY(mMainView, mainViewOffset);
-    }
-  }
-
-  private void onPanelDragged(int newTop) {
-    if (mSlideState != PanelState.DRAGGING) {
-      mLastNotDraggingSlideState = mSlideState;
-    }
-    setPanelStateInternal(PanelState.DRAGGING);
-    // Recompute the slide offset based on the new top position
-    mSlideOffset = computeSlideOffset(newTop);
-    applyParallaxForCurrentSlideOffset();
-    // Dispatch the slide event
-    dispatchOnPanelSlide(mSlideableView);
-    // If the slide offset is negative, and overlay is not on, we need to increase the
-    // height of the main content
-    LayoutParams lp = (LayoutParams) mMainView.getLayoutParams();
-    int defaultHeight = getHeight() - getPaddingBottom() - getPaddingTop() - mPanelHeight;
-
-    if (mSlideOffset <= 0 && !mOverlayContent) {
-      // expand the main view
-      lp.height = mIsSlidingUp ? (newTop - getPaddingBottom()) : (getHeight() - getPaddingBottom() - mSlideableView.getMeasuredHeight() - newTop);
-      if (lp.height == defaultHeight) {
-        lp.height = LayoutParams.MATCH_PARENT;
-      }
-      mMainView.requestLayout();
-    } else if (lp.height != LayoutParams.MATCH_PARENT && !mOverlayContent) {
-      lp.height = LayoutParams.MATCH_PARENT;
-      mMainView.requestLayout();
-    }
-  }
-
-  @Override
-  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-    boolean result;
-    final int save = canvas.save();
-
-    if (mSlideableView != null && mSlideableView != child) { // if main view
-      // Clip against the slider; no sense drawing what will immediately be covered,
-      // Unless the panel is set to overlay content
-      canvas.getClipBounds(mTmpRect);
-      if (!mOverlayContent) {
-        if (mIsSlidingUp) {
-          mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());
-        } else {
-          mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom());
-        }
-      }
-      if (mClipPanel) {
-        canvas.clipRect(mTmpRect);
-      }
-
-      result = super.drawChild(canvas, child, drawingTime);
-
-      if (mCoveredFadeColor != 0 && mSlideOffset > 0) {
-        final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24;
-        final int imag = (int) (baseAlpha * mSlideOffset);
-        final int color = imag << 24 | (mCoveredFadeColor & 0xffffff);
-        mCoveredFadePaint.setColor(color);
-        canvas.drawRect(mTmpRect, mCoveredFadePaint);
-      }
-    } else {
-      result = super.drawChild(canvas, child, drawingTime);
-    }
-
-    canvas.restoreToCount(save);
-
-    return result;
-  }
-
-  /**
-   * Smoothly animate mDraggingPane to the target X position within its range.
-   *
-   * @param slideOffset position to animate to
-   * @param velocity    initial velocity in case of fling, or 0.
-   */
-  boolean smoothSlideTo(float slideOffset, int velocity) {
-    if (!isEnabled() || mSlideableView == null) {
-      // Nothing to do.
-      return false;
-    }
-
-    int panelTop = computePanelTopPosition(slideOffset);
-
-    if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), panelTop)) {
-      setAllChildrenVisible();
-      ViewCompat.postInvalidateOnAnimation(this);
-      return true;
-    }
-    return false;
-  }
-
-  @Override
-  public void computeScroll() {
-    if (mDragHelper != null && mDragHelper.continueSettling(true)) {
-      if (!isEnabled()) {
-        mDragHelper.abort();
-        return;
-      }
-
-      ViewCompat.postInvalidateOnAnimation(this);
-    }
-  }
-
-  @Override
-  public void draw(Canvas c) {
-    super.draw(c);
-
-    // draw the shadow
-    if (mShadowDrawable != null && mSlideableView != null) {
-      final int right = mSlideableView.getRight();
-      final int top;
-      final int bottom;
-      if (mIsSlidingUp) {
-        top = mSlideableView.getTop() - mShadowHeight;
-        bottom = mSlideableView.getTop();
-      } else {
-        top = mSlideableView.getBottom();
-        bottom = mSlideableView.getBottom() + mShadowHeight;
-      }
-      final int left = mSlideableView.getLeft();
-      mShadowDrawable.setBounds(left, top, right, bottom);
-      mShadowDrawable.draw(c);
-    }
-  }
-
-  /**
-   * Tests scrollability within child views of v given a delta of dx.
-   *
-   * @param v      View to test for horizontal scrollability
-   * @param checkV Whether the view v passed should itself be checked for scrollability (true),
-   *               or just its children (false).
-   * @param dx     Delta scrolled in pixels
-   * @param x      X coordinate of the active touch point
-   * @param y      Y coordinate of the active touch point
-   * @return true if child views of v can be scrolled by delta of dx.
-   */
-  protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
-    if (v instanceof ViewGroup) {
-      final ViewGroup group = (ViewGroup) v;
-      final int scrollX = v.getScrollX();
-      final int scrollY = v.getScrollY();
-      final int count = group.getChildCount();
-      // Count backwards - let topmost views consume scroll distance first.
-      for (int i = count - 1; i >= 0; i--) {
-        final View child = group.getChildAt(i);
-        if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
-          y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
-          canScroll(child, true, dx, x + scrollX - child.getLeft(),
-            y + scrollY - child.getTop())) {
-          return true;
-        }
-      }
-    }
-    return checkV && ViewCompat.canScrollHorizontally(v, -dx);
-  }
-
-  @Override
-  protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-    return new LayoutParams();
-  }
-
-  @Override
-  protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-    return p instanceof MarginLayoutParams
-      ? new LayoutParams((MarginLayoutParams) p)
-      : new LayoutParams(p);
-  }
-
-  @Override
-  protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-    return p instanceof LayoutParams && super.checkLayoutParams(p);
-  }
-
-  @Override
-  public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-    return new LayoutParams(getContext(), attrs);
-  }
-
-  @Override
-  public Parcelable onSaveInstanceState() {
-    Bundle bundle = new Bundle();
-    bundle.putParcelable("superState", super.onSaveInstanceState());
-    bundle.putSerializable(SLIDING_STATE, mSlideState != PanelState.DRAGGING ? mSlideState : mLastNotDraggingSlideState);
-    return bundle;
-  }
-
-  @Override
-  public void onRestoreInstanceState(Parcelable state) {
-    if (state instanceof Bundle) {
-      Bundle bundle = (Bundle) state;
-      mSlideState = (PanelState) bundle.getSerializable(SLIDING_STATE);
-      mSlideState = mSlideState == null ? DEFAULT_SLIDE_STATE : mSlideState;
-      state = bundle.getParcelable("superState");
-    }
-    super.onRestoreInstanceState(state);
-  }
-
-  /**
-   * Current state of the slideable view.
-   */
-  public enum PanelState {
-    EXPANDED,
-    COLLAPSED,
-    ANCHORED,
-    HIDDEN,
-    DRAGGING
-  }
-
-  /**
-   * Listener for monitoring events about sliding panes.
-   */
-  public interface PanelSlideListener {
-    /**
-     * Called when a sliding pane's position changes.
-     *
-     * @param panel       The child view that was moved
-     * @param slideOffset The new offset of this sliding pane within its range, from 0-1
-     */
-    void onPanelSlide(View panel, float slideOffset);
-
-    /**
-     * Called when a sliding panel state changes
-     *
-     * @param panel The child view that was slid to an collapsed position
-     */
-    void onPanelStateChanged(View panel, PanelState previousState, PanelState newState);
-  }
-
-  /**
-   * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset
-   * of the listener methods you can extend this instead of implement the full interface.
-   */
-  public static class SimplePanelSlideListener implements PanelSlideListener {
-    @Override
-    public void onPanelSlide(View panel, float slideOffset) {
-    }
-
-    @Override
-    public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) {
-    }
-  }
-
-  public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-    private static final int[] ATTRS = new int[]{
-      android.R.attr.layout_weight
-    };
-
-    public float weight = 0;
-
-    public LayoutParams() {
-      super(MATCH_PARENT, MATCH_PARENT);
-    }
-
-    public LayoutParams(int width, int height) {
-      super(width, height);
-    }
-
-    public LayoutParams(int width, int height, float weight) {
-      super(width, height);
-      this.weight = weight;
-    }
-
-    public LayoutParams(android.view.ViewGroup.LayoutParams source) {
-      super(source);
-    }
-
-    public LayoutParams(MarginLayoutParams source) {
-      super(source);
-    }
-
-    public LayoutParams(LayoutParams source) {
-      super(source);
-    }
-
-    public LayoutParams(Context c, AttributeSet attrs) {
-      super(c, attrs);
-
-      final TypedArray ta = c.obtainStyledAttributes(attrs, ATTRS);
-      if (ta != null) {
-        this.weight = ta.getFloat(0, 0);
-        ta.recycle();
-      }
-
-
-    }
-  }
-
-  private class DragHelperCallback extends ViewDragHelper.Callback {
-
-    @Override
-    public boolean tryCaptureView(View child, int pointerId) {
-      return !mIsUnableToDrag && child == mSlideableView;
-
-    }
-
-    @Override
-    public void onViewDragStateChanged(int state) {
-      if (mDragHelper != null && mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
-        mSlideOffset = computeSlideOffset(mSlideableView.getTop());
-        applyParallaxForCurrentSlideOffset();
-
-        if (mSlideOffset == 1) {
-          updateObscuredViewVisibility();
-          setPanelStateInternal(PanelState.EXPANDED);
-        } else if (mSlideOffset == 0) {
-          setPanelStateInternal(PanelState.COLLAPSED);
-        } else if (mSlideOffset < 0) {
-          setPanelStateInternal(PanelState.HIDDEN);
-          mSlideableView.setVisibility(View.INVISIBLE);
-        } else {
-          updateObscuredViewVisibility();
-          setPanelStateInternal(PanelState.ANCHORED);
-        }
-      }
-    }
-
-    @Override
-    public void onViewCaptured(View capturedChild, int activePointerId) {
-      setAllChildrenVisible();
-    }
-
-    @Override
-    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
-      onPanelDragged(top);
-      invalidate();
-    }
-
-    @Override
-    public void onViewReleased(View releasedChild, float xvel, float yvel) {
-      int target;
-
-      // direction is always positive if we are sliding in the expanded direction
-      float direction = mIsSlidingUp ? -yvel : yvel;
-
-      if (direction > 0 && mSlideOffset <= mAnchorPoint) {
-        // swipe up -> expand and stop at anchor point
-        target = computePanelTopPosition(mAnchorPoint);
-      } else if (direction > 0 && mSlideOffset > mAnchorPoint) {
-        // swipe up past anchor -> expand
-        target = computePanelTopPosition(1.0f);
-      } else if (direction < 0 && mSlideOffset >= mAnchorPoint) {
-        // swipe down -> collapse and stop at anchor point
-        target = computePanelTopPosition(mAnchorPoint);
-      } else if (direction < 0 && mSlideOffset < mAnchorPoint) {
-        // swipe down past anchor -> collapse
-        target = computePanelTopPosition(0.0f);
-      } else if (mSlideOffset >= (1.f + mAnchorPoint) / 2) {
-        // zero velocity, and far enough from anchor point => expand to the top
-        target = computePanelTopPosition(1.0f);
-      } else if (mSlideOffset >= mAnchorPoint / 2) {
-        // zero velocity, and close enough to anchor point => go to anchor
-        target = computePanelTopPosition(mAnchorPoint);
-      } else {
-        // settle at the bottom
-        target = computePanelTopPosition(0.0f);
-      }
-
-      if (mDragHelper != null) {
-        mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), target);
-      }
-      invalidate();
-    }
-
-    @Override
-    public int getViewVerticalDragRange(View child) {
-      return mSlideRange;
-    }
-
-    @Override
-    public int clampViewPositionVertical(View child, int top, int dy) {
-      final int collapsedTop = computePanelTopPosition(0.f);
-      final int expandedTop = computePanelTopPosition(1.0f);
-      if (mIsSlidingUp) {
-        return Math.min(Math.max(top, expandedTop), collapsedTop);
-      } else {
-        return Math.min(Math.max(top, collapsedTop), expandedTop);
-      }
-    }
-  }
-}
diff --git a/slidingpanel/src/main/java/com/sothree/slidinguppanel/ViewDragHelper.java b/slidingpanel/src/main/java/com/sothree/slidinguppanel/ViewDragHelper.java
deleted file mode 100644
index cea894fbe..000000000
--- a/slidingpanel/src/main/java/com/sothree/slidinguppanel/ViewDragHelper.java
+++ /dev/null
@@ -1,1481 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.sothree.slidinguppanel;
-
-import android.content.Context;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ScrollerCompat;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-
-import java.util.Arrays;
-
-/**
- * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number
- * of useful operations and state tracking for allowing a user to drag and reposition
- * views within their parent ViewGroup.
- */
-public class ViewDragHelper {
-  /**
-   * A null/invalid pointer ID.
-   */
-  public static final int INVALID_POINTER = -1;
-  /**
-   * A view is not currently being dragged or animating as a result of a fling/snap.
-   */
-  public static final int STATE_IDLE = 0;
-  /**
-   * A view is currently being dragged. The position is currently changing as a result
-   * of user input or simulated user input.
-   */
-  public static final int STATE_DRAGGING = 1;
-  /**
-   * A view is currently settling into place as a result of a fling or
-   * predefined non-interactive motion.
-   */
-  public static final int STATE_SETTLING = 2;
-  /**
-   * Edge flag indicating that the left edge should be affected.
-   */
-  public static final int EDGE_LEFT = 1 << 0;
-  /**
-   * Edge flag indicating that the right edge should be affected.
-   */
-  public static final int EDGE_RIGHT = 1 << 1;
-  /**
-   * Edge flag indicating that the top edge should be affected.
-   */
-  public static final int EDGE_TOP = 1 << 2;
-  /**
-   * Edge flag indicating that the bottom edge should be affected.
-   */
-  public static final int EDGE_BOTTOM = 1 << 3;
-  /**
-   * Edge flag set indicating all edges should be affected.
-   */
-  public static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM;
-  /**
-   * Indicates that a check should occur along the horizontal axis
-   */
-  public static final int DIRECTION_HORIZONTAL = 1 << 0;
-  /**
-   * Indicates that a check should occur along the vertical axis
-   */
-  public static final int DIRECTION_VERTICAL = 1 << 1;
-  /**
-   * Indicates that a check should occur along all axes
-   */
-  public static final int DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
-  private static final String TAG = "ViewDragHelper";
-  private static final int EDGE_SIZE = 20; // dp
-
-  private static final int BASE_SETTLE_DURATION = 256; // ms
-  private static final int MAX_SETTLE_DURATION = 600; // ms
-  /**
-   * Interpolator defining the animation curve for mScroller
-   */
-  private static final Interpolator sInterpolator = new Interpolator() {
-    public float getInterpolation(float t) {
-      t -= 1.0f;
-      return t * t * t * t * t + 1.0f;
-    }
-  };
-  private final Callback mCallback;
-  private final ViewGroup mParentView;
-  // Current drag state; idle, dragging or settling
-  private int mDragState;
-  // Distance to travel before a drag may begin
-  private int mTouchSlop;
-  // Last known position/pointer tracking
-  private int mActivePointerId = INVALID_POINTER;
-  private float[] mInitialMotionX;
-  private float[] mInitialMotionY;
-  private float[] mLastMotionX;
-  private float[] mLastMotionY;
-  private int[] mInitialEdgesTouched;
-  private int[] mEdgeDragsInProgress;
-  private int[] mEdgeDragsLocked;
-  private int mPointersDown;
-  private VelocityTracker mVelocityTracker;
-  private float mMaxVelocity;
-  private float mMinVelocity;
-  private int mEdgeSize;
-  private int mTrackingEdges;
-  private ScrollerCompat mScroller;
-  private View mCapturedView;
-  private final Runnable mSetIdleRunnable = new Runnable() {
-    public void run() {
-      setDragState(STATE_IDLE);
-    }
-  };
-  private boolean mReleaseInProgress;
-
-  /**
-   * Apps should use ViewDragHelper.create() to get a new instance.
-   * This will allow VDH to use internal compatibility implementations for different
-   * platform versions.
-   * If the interpolator is null, the default interpolator will be used.
-   *
-   * @param context      Context to initialize config-dependent params from
-   * @param forParent    Parent view to monitor
-   * @param interpolator interpolator for scroller
-   */
-  private ViewDragHelper(Context context, ViewGroup forParent, Interpolator interpolator, Callback cb) {
-    if (forParent == null) {
-      throw new IllegalArgumentException("Parent view may not be null");
-    }
-    if (cb == null) {
-      throw new IllegalArgumentException("Callback may not be null");
-    }
-
-    mParentView = forParent;
-    mCallback = cb;
-
-    final ViewConfiguration vc = ViewConfiguration.get(context);
-    final float density = context.getResources().getDisplayMetrics().density;
-    mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);
-
-    mTouchSlop = vc.getScaledTouchSlop();
-    mMaxVelocity = vc.getScaledMaximumFlingVelocity();
-    mMinVelocity = vc.getScaledMinimumFlingVelocity();
-    mScroller = ScrollerCompat.create(context, interpolator != null ? interpolator : sInterpolator);
-  }
-
-  /**
-   * Factory method to create a new ViewDragHelper.
-   *
-   * @param forParent Parent view to monitor
-   * @param cb        Callback to provide information and receive events
-   * @return a new ViewDragHelper instance
-   */
-  public static ViewDragHelper create(ViewGroup forParent, Callback cb) {
-    return new ViewDragHelper(forParent.getContext(), forParent, null, cb);
-  }
-
-  /**
-   * Factory method to create a new ViewDragHelper with the specified interpolator.
-   *
-   * @param forParent    Parent view to monitor
-   * @param interpolator interpolator for scroller
-   * @param cb           Callback to provide information and receive events
-   * @return a new ViewDragHelper instance
-   */
-  public static ViewDragHelper create(ViewGroup forParent, Interpolator interpolator, Callback cb) {
-    return new ViewDragHelper(forParent.getContext(), forParent, interpolator, cb);
-  }
-
-  /**
-   * Factory method to create a new ViewDragHelper.
-   *
-   * @param forParent   Parent view to monitor
-   * @param sensitivity Multiplier for how sensitive the helper should be about detecting
-   *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
-   * @param cb          Callback to provide information and receive events
-   * @return a new ViewDragHelper instance
-   */
-  public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
-    final ViewDragHelper helper = create(forParent, cb);
-    helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
-    return helper;
-  }
-
-  /**
-   * Factory method to create a new ViewDragHelper with the specified interpolator.
-   *
-   * @param forParent    Parent view to monitor
-   * @param sensitivity  Multiplier for how sensitive the helper should be about detecting
-   *                     the start of a drag. Larger values are more sensitive. 1.0f is normal.
-   * @param interpolator interpolator for scroller
-   * @param cb           Callback to provide information and receive events
-   * @return a new ViewDragHelper instance
-   */
-  public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Interpolator interpolator, Callback cb) {
-    final ViewDragHelper helper = create(forParent, interpolator, cb);
-    helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
-    return helper;
-  }
-
-  /**
-   * Return the currently configured minimum velocity. Any flings with a magnitude less
-   * than this value in pixels per second. Callback methods accepting a velocity will receive
-   * zero as a velocity value if the real detected velocity was below this threshold.
-   *
-   * @return the minimum velocity that will be detected
-   */
-  public float getMinVelocity() {
-    return mMinVelocity;
-  }
-
-  /**
-   * Set the minimum velocity that will be detected as having a magnitude greater than zero
-   * in pixels per second. Callback methods accepting a velocity will be clamped appropriately.
-   *
-   * @param minVel Minimum velocity to detect
-   */
-  public void setMinVelocity(float minVel) {
-    mMinVelocity = minVel;
-  }
-
-  /**
-   * Retrieve the current drag state of this helper. This will return one of
-   * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
-   *
-   * @return The current drag state
-   */
-  public int getViewDragState() {
-    return mDragState;
-  }
-
-  /**
-   * Enable edge tracking for the selected edges of the parent view.
-   * The callback's {@link Callback#onEdgeTouched(int, int)} and
-   * {@link Callback#onEdgeDragStarted(int, int)} methods will only be invoked
-   * for edges for which edge tracking has been enabled.
-   *
-   * @param edgeFlags Combination of edge flags describing the edges to watch
-   * @see #EDGE_LEFT
-   * @see #EDGE_TOP
-   * @see #EDGE_RIGHT
-   * @see #EDGE_BOTTOM
-   */
-  public void setEdgeTrackingEnabled(int edgeFlags) {
-    mTrackingEdges = edgeFlags;
-  }
-
-  /**
-   * Return the size of an edge. This is the range in pixels along the edges of this view
-   * that will actively detect edge touches or drags if edge tracking is enabled.
-   *
-   * @return The size of an edge in pixels
-   * @see #setEdgeTrackingEnabled(int)
-   */
-  public int getEdgeSize() {
-    return mEdgeSize;
-  }
-
-  /**
-   * Capture a specific child view for dragging within the parent. The callback will be notified
-   * but {@link Callback#tryCaptureView(android.view.View, int)} will not be asked permission to
-   * capture this view.
-   *
-   * @param childView       Child view to capture
-   * @param activePointerId ID of the pointer that is dragging the captured child view
-   */
-  public void captureChildView(View childView, int activePointerId) {
-    if (childView.getParent() != mParentView) {
-      throw new IllegalArgumentException("captureChildView: parameter must be a descendant " +
-        "of the ViewDragHelper's tracked parent view (" + mParentView + ")");
-    }
-
-    mCapturedView = childView;
-    mActivePointerId = activePointerId;
-    mCallback.onViewCaptured(childView, activePointerId);
-    setDragState(STATE_DRAGGING);
-  }
-
-  /**
-   * @return The currently captured view, or null if no view has been captured.
-   */
-  public View getCapturedView() {
-    return mCapturedView;
-  }
-
-  /**
-   * @return The ID of the pointer currently dragging the captured view,
-   * or {@link #INVALID_POINTER}.
-   */
-  public int getActivePointerId() {
-    return mActivePointerId;
-  }
-
-  /**
-   * @return The minimum distance in pixels that the user must travel to initiate a drag
-   */
-  public int getTouchSlop() {
-    return mTouchSlop;
-  }
-
-  /**
-   * The result of a call to this method is equivalent to
-   * {@link #processTouchEvent(android.view.MotionEvent)} receiving an ACTION_CANCEL event.
-   */
-  public void cancel() {
-    mActivePointerId = INVALID_POINTER;
-    clearMotionHistory();
-
-    if (mVelocityTracker != null) {
-      mVelocityTracker.recycle();
-      mVelocityTracker = null;
-    }
-  }
-
-  /**
-   * {@link #cancel()}, but also abort all motion in progress and snap to the end of any
-   * animation.
-   */
-  public void abort() {
-    cancel();
-    if (mDragState == STATE_SETTLING) {
-      final int oldX = mScroller.getCurrX();
-      final int oldY = mScroller.getCurrY();
-      mScroller.abortAnimation();
-      final int newX = mScroller.getCurrX();
-      final int newY = mScroller.getCurrY();
-      mCallback.onViewPositionChanged(mCapturedView, newX, newY, newX - oldX, newY - oldY);
-    }
-    setDragState(STATE_IDLE);
-  }
-
-  /**
-   * Animate the view <code>child</code> to the given (left, top) position.
-   * If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
-   * on each subsequent frame to continue the motion until it returns false. If this method
-   * returns false there is no further work to do to complete the movement.
-   * <p>
-   * <p>This operation does not count as a capture event, though {@link #getCapturedView()}
-   * will still report the sliding view while the slide is in progress.</p>
-   *
-   * @param child     Child view to capture and animate
-   * @param finalLeft Final left position of child
-   * @param finalTop  Final top position of child
-   * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-   */
-  public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {
-    mCapturedView = child;
-    mActivePointerId = INVALID_POINTER;
-
-    return forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0);
-  }
-
-  /**
-   * Settle the captured view at the given (left, top) position.
-   * The appropriate velocity from prior motion will be taken into account.
-   * If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
-   * on each subsequent frame to continue the motion until it returns false. If this method
-   * returns false there is no further work to do to complete the movement.
-   *
-   * @param finalLeft Settled left edge position for the captured view
-   * @param finalTop  Settled top edge position for the captured view
-   * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-   */
-  public boolean settleCapturedViewAt(int finalLeft, int finalTop) {
-    if (!mReleaseInProgress) {
-      throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " +
-        "Callback#onViewReleased");
-    }
-
-    return forceSettleCapturedViewAt(finalLeft, finalTop,
-      (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
-      (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId));
-  }
-
-  /**
-   * Settle the captured view at the given (left, top) position.
-   *
-   * @param finalLeft Target left position for the captured view
-   * @param finalTop  Target top position for the captured view
-   * @param xvel      Horizontal velocity
-   * @param yvel      Vertical velocity
-   * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-   */
-  private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {
-    final int startLeft = mCapturedView.getLeft();
-    final int startTop = mCapturedView.getTop();
-    final int dx = finalLeft - startLeft;
-    final int dy = finalTop - startTop;
-
-    if (dx == 0 && dy == 0) {
-      // Nothing to do. Send callbacks, be done.
-      mScroller.abortAnimation();
-      setDragState(STATE_IDLE);
-      return false;
-    }
-
-    final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);
-    mScroller.startScroll(startLeft, startTop, dx, dy, duration);
-
-    setDragState(STATE_SETTLING);
-    return true;
-  }
-
-  private int computeSettleDuration(View child, int dx, int dy, int xvel, int yvel) {
-    xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity);
-    yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity);
-    final int absDx = Math.abs(dx);
-    final int absDy = Math.abs(dy);
-    final int absXVel = Math.abs(xvel);
-    final int absYVel = Math.abs(yvel);
-    final int addedVel = absXVel + absYVel;
-    final int addedDistance = absDx + absDy;
-
-    final float xweight = xvel != 0 ? (float) absXVel / addedVel :
-      (float) absDx / addedDistance;
-    final float yweight = yvel != 0 ? (float) absYVel / addedVel :
-      (float) absDy / addedDistance;
-
-    int xduration = computeAxisDuration(dx, xvel, mCallback.getViewHorizontalDragRange(child));
-    int yduration = computeAxisDuration(dy, yvel, mCallback.getViewVerticalDragRange(child));
-
-    return (int) (xduration * xweight + yduration * yweight);
-  }
-
-  private int computeAxisDuration(int delta, int velocity, int motionRange) {
-    if (delta == 0) {
-      return 0;
-    }
-
-    final int width = mParentView.getWidth();
-    final int halfWidth = width / 2;
-    final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width);
-    final float distance = halfWidth + halfWidth *
-      distanceInfluenceForSnapDuration(distanceRatio);
-
-    int duration;
-    velocity = Math.abs(velocity);
-    if (velocity > 0) {
-      duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
-    } else {
-      final float range = (float) Math.abs(delta) / motionRange;
-      duration = (int) ((range + 1) * BASE_SETTLE_DURATION);
-    }
-    return Math.min(duration, MAX_SETTLE_DURATION);
-  }
-
-  /**
-   * Clamp the magnitude of value for absMin and absMax.
-   * If the value is below the minimum, it will be clamped to zero.
-   * If the value is above the maximum, it will be clamped to the maximum.
-   *
-   * @param value  Value to clamp
-   * @param absMin Absolute value of the minimum significant value to return
-   * @param absMax Absolute value of the maximum value to return
-   * @return The clamped value with the same sign as <code>value</code>
-   */
-  private int clampMag(int value, int absMin, int absMax) {
-    final int absValue = Math.abs(value);
-    if (absValue < absMin) return 0;
-    if (absValue > absMax) return value > 0 ? absMax : -absMax;
-    return value;
-  }
-
-  /**
-   * Clamp the magnitude of value for absMin and absMax.
-   * If the value is below the minimum, it will be clamped to zero.
-   * If the value is above the maximum, it will be clamped to the maximum.
-   *
-   * @param value  Value to clamp
-   * @param absMin Absolute value of the minimum significant value to return
-   * @param absMax Absolute value of the maximum value to return
-   * @return The clamped value with the same sign as <code>value</code>
-   */
-  private float clampMag(float value, float absMin, float absMax) {
-    final float absValue = Math.abs(value);
-    if (absValue < absMin) return 0;
-    if (absValue > absMax) return value > 0 ? absMax : -absMax;
-    return value;
-  }
-
-  private float distanceInfluenceForSnapDuration(float f) {
-    f -= 0.5f; // center the values about 0.
-    f *= 0.3f * Math.PI / 2.0f;
-    return (float) Math.sin(f);
-  }
-
-  /**
-   * Settle the captured view based on standard free-moving fling behavior.
-   * The caller should invoke {@link #continueSettling(boolean)} on each subsequent frame
-   * to continue the motion until it returns false.
-   *
-   * @param minLeft Minimum X position for the view's left edge
-   * @param minTop  Minimum Y position for the view's top edge
-   * @param maxLeft Maximum X position for the view's left edge
-   * @param maxTop  Maximum Y position for the view's top edge
-   */
-  public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) {
-    if (!mReleaseInProgress) {
-      throw new IllegalStateException("Cannot flingCapturedView outside of a call to " +
-        "Callback#onViewReleased");
-    }
-
-    mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),
-      (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
-      (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
-      minLeft, maxLeft, minTop, maxTop);
-
-    setDragState(STATE_SETTLING);
-  }
-
-  /**
-   * Move the captured settling view by the appropriate amount for the current time.
-   * If <code>continueSettling</code> returns true, the caller should call it again
-   * on the next frame to continue.
-   *
-   * @param deferCallbacks true if state callbacks should be deferred via posted message.
-   *                       Set this to true if you are calling this method from
-   *                       {@link android.view.View#computeScroll()} or similar methods
-   *                       invoked as part of layout or drawing.
-   * @return true if settle is still in progress
-   */
-  public boolean continueSettling(boolean deferCallbacks) {
-    // Make sure, there is a captured view
-    if (mCapturedView == null) {
-      return false;
-    }
-    if (mDragState == STATE_SETTLING) {
-      boolean keepGoing = mScroller.computeScrollOffset();
-      final int x = mScroller.getCurrX();
-      final int y = mScroller.getCurrY();
-      final int dx = x - mCapturedView.getLeft();
-      final int dy = y - mCapturedView.getTop();
-
-      if (!keepGoing && dy != 0) { //fix #525
-        //Invalid drag state
-        mCapturedView.setTop(0);
-        return true;
-      }
-
-      if (dx != 0) {
-        mCapturedView.offsetLeftAndRight(dx);
-      }
-      if (dy != 0) {
-        mCapturedView.offsetTopAndBottom(dy);
-      }
-
-      if (dx != 0 || dy != 0) {
-        mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);
-      }
-
-      if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {
-        // Close enough. The interpolator/scroller might think we're still moving
-        // but the user sure doesn't.
-        mScroller.abortAnimation();
-        keepGoing = mScroller.isFinished();
-      }
-
-      if (!keepGoing) {
-        if (deferCallbacks) {
-          mParentView.post(mSetIdleRunnable);
-        } else {
-          setDragState(STATE_IDLE);
-        }
-      }
-    }
-
-    return mDragState == STATE_SETTLING;
-  }
-
-  /**
-   * Like all callback events this must happen on the UI thread, but release
-   * involves some extra semantics. During a release (mReleaseInProgress)
-   * is the only time it is valid to call {@link #settleCapturedViewAt(int, int)}
-   * or {@link #flingCapturedView(int, int, int, int)}.
-   */
-  private void dispatchViewReleased(float xvel, float yvel) {
-    mReleaseInProgress = true;
-    mCallback.onViewReleased(mCapturedView, xvel, yvel);
-    mReleaseInProgress = false;
-
-    if (mDragState == STATE_DRAGGING) {
-      // onViewReleased didn't call a method that would have changed this. Go idle.
-      setDragState(STATE_IDLE);
-    }
-  }
-
-  private void clearMotionHistory() {
-    if (mInitialMotionX == null) {
-      return;
-    }
-    Arrays.fill(mInitialMotionX, 0);
-    Arrays.fill(mInitialMotionY, 0);
-    Arrays.fill(mLastMotionX, 0);
-    Arrays.fill(mLastMotionY, 0);
-    Arrays.fill(mInitialEdgesTouched, 0);
-    Arrays.fill(mEdgeDragsInProgress, 0);
-    Arrays.fill(mEdgeDragsLocked, 0);
-    mPointersDown = 0;
-  }
-
-  private void clearMotionHistory(int pointerId) {
-    if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) {
-      return;
-    }
-    mInitialMotionX[pointerId] = 0;
-    mInitialMotionY[pointerId] = 0;
-    mLastMotionX[pointerId] = 0;
-    mLastMotionY[pointerId] = 0;
-    mInitialEdgesTouched[pointerId] = 0;
-    mEdgeDragsInProgress[pointerId] = 0;
-    mEdgeDragsLocked[pointerId] = 0;
-    mPointersDown &= ~(1 << pointerId);
-  }
-
-  private void ensureMotionHistorySizeForId(int pointerId) {
-    if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) {
-      float[] imx = new float[pointerId + 1];
-      float[] imy = new float[pointerId + 1];
-      float[] lmx = new float[pointerId + 1];
-      float[] lmy = new float[pointerId + 1];
-      int[] iit = new int[pointerId + 1];
-      int[] edip = new int[pointerId + 1];
-      int[] edl = new int[pointerId + 1];
-
-      if (mInitialMotionX != null) {
-        System.arraycopy(mInitialMotionX, 0, imx, 0, mInitialMotionX.length);
-        System.arraycopy(mInitialMotionY, 0, imy, 0, mInitialMotionY.length);
-        System.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length);
-        System.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length);
-        System.arraycopy(mInitialEdgesTouched, 0, iit, 0, mInitialEdgesTouched.length);
-        System.arraycopy(mEdgeDragsInProgress, 0, edip, 0, mEdgeDragsInProgress.length);
-        System.arraycopy(mEdgeDragsLocked, 0, edl, 0, mEdgeDragsLocked.length);
-      }
-
-      mInitialMotionX = imx;
-      mInitialMotionY = imy;
-      mLastMotionX = lmx;
-      mLastMotionY = lmy;
-      mInitialEdgesTouched = iit;
-      mEdgeDragsInProgress = edip;
-      mEdgeDragsLocked = edl;
-    }
-  }
-
-  private void saveInitialMotion(float x, float y, int pointerId) {
-    ensureMotionHistorySizeForId(pointerId);
-    mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;
-    mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;
-    mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);
-    mPointersDown |= 1 << pointerId;
-  }
-
-  private void saveLastMotion(MotionEvent ev) {
-    final int pointerCount = MotionEventCompat.getPointerCount(ev);
-    for (int i = 0; i < pointerCount; i++) {
-      final int pointerId = MotionEventCompat.getPointerId(ev, i);
-      final float x = MotionEventCompat.getX(ev, i);
-      final float y = MotionEventCompat.getY(ev, i);
-      // Sometimes we can try and save last motion for a pointer never recorded in initial motion. In this case we just discard it.
-      if (mLastMotionX != null && mLastMotionY != null
-        && mLastMotionX.length > pointerId && mLastMotionY.length > pointerId) {
-        mLastMotionX[pointerId] = x;
-        mLastMotionY[pointerId] = y;
-      }
-    }
-  }
-
-  /**
-   * Check if the given pointer ID represents a pointer that is currently down (to the best
-   * of the ViewDragHelper's knowledge).
-   * <p>
-   * <p>The state used to report this information is populated by the methods
-   * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-   * {@link #processTouchEvent(android.view.MotionEvent)}. If one of these methods has not
-   * been called for all relevant MotionEvents to track, the information reported
-   * by this method may be stale or incorrect.</p>
-   *
-   * @param pointerId pointer ID to check; corresponds to IDs provided by MotionEvent
-   * @return true if the pointer with the given ID is still down
-   */
-  public boolean isPointerDown(int pointerId) {
-    return (mPointersDown & 1 << pointerId) != 0;
-  }
-
-  void setDragState(int state) {
-    if (mDragState != state) {
-      mDragState = state;
-      mCallback.onViewDragStateChanged(state);
-      if (mDragState == STATE_IDLE) {
-        mCapturedView = null;
-      }
-    }
-  }
-
-  /**
-   * Attempt to capture the view with the given pointer ID. The callback will be involved.
-   * This will put us into the "dragging" state. If we've already captured this view with
-   * this pointer this method will immediately return true without consulting the callback.
-   *
-   * @param toCapture View to capture
-   * @param pointerId Pointer to capture with
-   * @return true if capture was successful
-   */
-  boolean tryCaptureViewForDrag(View toCapture, int pointerId) {
-    if (toCapture == mCapturedView && mActivePointerId == pointerId) {
-      // Already done!
-      return true;
-    }
-    if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {
-      mActivePointerId = pointerId;
-      captureChildView(toCapture, pointerId);
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Tests scrollability within child views of v given a delta of dx.
-   *
-   * @param v      View to test for horizontal scrollability
-   * @param checkV Whether the view v passed should itself be checked for scrollability (true),
-   *               or just its children (false).
-   * @param dx     Delta scrolled in pixels along the X axis
-   * @param dy     Delta scrolled in pixels along the Y axis
-   * @param x      X coordinate of the active touch point
-   * @param y      Y coordinate of the active touch point
-   * @return true if child views of v can be scrolled by delta of dx.
-   */
-  protected boolean canScroll(View v, boolean checkV, int dx, int dy, int x, int y) {
-    if (v instanceof ViewGroup) {
-      final ViewGroup group = (ViewGroup) v;
-      final int scrollX = v.getScrollX();
-      final int scrollY = v.getScrollY();
-      final int count = group.getChildCount();
-      // Count backwards - let topmost views consume scroll distance first.
-      for (int i = count - 1; i >= 0; i--) {
-        // TODO: Add versioned support here for transformed views.
-        // This will not work for transformed views in Honeycomb+
-        final View child = group.getChildAt(i);
-        if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
-          y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
-          canScroll(child, true, dx, dy, x + scrollX - child.getLeft(),
-            y + scrollY - child.getTop())) {
-          return true;
-        }
-      }
-    }
-
-    return checkV && (ViewCompat.canScrollHorizontally(v, -dx) ||
-      ViewCompat.canScrollVertically(v, -dy));
-  }
-
-  /**
-   * Check if this event as provided to the parent view's onInterceptTouchEvent should
-   * cause the parent to intercept the touch event stream.
-   *
-   * @param ev MotionEvent provided to onInterceptTouchEvent
-   * @return true if the parent view should return true from onInterceptTouchEvent
-   */
-  public boolean shouldInterceptTouchEvent(MotionEvent ev) {
-    final int action = MotionEventCompat.getActionMasked(ev);
-    final int actionIndex = MotionEventCompat.getActionIndex(ev);
-
-    if (action == MotionEvent.ACTION_DOWN) {
-      // Reset things for a new event stream, just in case we didn't get
-      // the whole previous stream.
-      cancel();
-    }
-
-    if (mVelocityTracker == null) {
-      mVelocityTracker = VelocityTracker.obtain();
-    }
-    mVelocityTracker.addMovement(ev);
-
-    switch (action) {
-      case MotionEvent.ACTION_DOWN: {
-        final float x = ev.getX();
-        final float y = ev.getY();
-        final int pointerId = MotionEventCompat.getPointerId(ev, 0);
-        saveInitialMotion(x, y, pointerId);
-
-        final View toCapture = findTopChildUnder((int) x, (int) y);
-
-        // Catch a settling view if possible.
-        if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
-          tryCaptureViewForDrag(toCapture, pointerId);
-        }
-
-        final int edgesTouched = mInitialEdgesTouched[pointerId];
-        if ((edgesTouched & mTrackingEdges) != 0) {
-          mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-        }
-        break;
-      }
-
-      case MotionEventCompat.ACTION_POINTER_DOWN: {
-        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
-        final float x = MotionEventCompat.getX(ev, actionIndex);
-        final float y = MotionEventCompat.getY(ev, actionIndex);
-
-        saveInitialMotion(x, y, pointerId);
-
-        // A ViewDragHelper can only manipulate one view at a time.
-        if (mDragState == STATE_IDLE) {
-          final int edgesTouched = mInitialEdgesTouched[pointerId];
-          if ((edgesTouched & mTrackingEdges) != 0) {
-            mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-          }
-        } else if (mDragState == STATE_SETTLING) {
-          // Catch a settling view if possible.
-          final View toCapture = findTopChildUnder((int) x, (int) y);
-          if (toCapture == mCapturedView) {
-            tryCaptureViewForDrag(toCapture, pointerId);
-          }
-        }
-        break;
-      }
-
-      case MotionEvent.ACTION_MOVE: {
-        // First to cross a touch slop over a draggable view wins. Also report edge drags.
-        final int pointerCount = MotionEventCompat.getPointerCount(ev);
-        for (int i = 0; i < pointerCount && mInitialMotionX != null && mInitialMotionY != null; i++) {
-          final int pointerId = MotionEventCompat.getPointerId(ev, i);
-          if (pointerId >= mInitialMotionX.length || pointerId >= mInitialMotionY.length) {
-            continue;
-          }
-          final float x = MotionEventCompat.getX(ev, i);
-          final float y = MotionEventCompat.getY(ev, i);
-          final float dx = x - mInitialMotionX[pointerId];
-          final float dy = y - mInitialMotionY[pointerId];
-
-          reportNewEdgeDrags(dx, dy, pointerId);
-          if (mDragState == STATE_DRAGGING) {
-            // Callback might have started an edge drag
-            break;
-          }
-
-          final View toCapture = findTopChildUnder((int) mInitialMotionX[pointerId], (int) mInitialMotionY[pointerId]);
-          if (toCapture != null && checkTouchSlop(toCapture, dx, dy) &&
-            tryCaptureViewForDrag(toCapture, pointerId)) {
-            break;
-          }
-        }
-        saveLastMotion(ev);
-        break;
-      }
-
-      case MotionEventCompat.ACTION_POINTER_UP: {
-        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
-        clearMotionHistory(pointerId);
-        break;
-      }
-
-      case MotionEvent.ACTION_UP:
-      case MotionEvent.ACTION_CANCEL: {
-        cancel();
-        break;
-      }
-    }
-
-    return mDragState == STATE_DRAGGING;
-  }
-
-  /**
-   * Process a touch event received by the parent view. This method will dispatch callback events
-   * as needed before returning. The parent view's onTouchEvent implementation should call this.
-   *
-   * @param ev The touch event received by the parent view
-   */
-  public void processTouchEvent(MotionEvent ev) {
-    final int action = MotionEventCompat.getActionMasked(ev);
-    final int actionIndex = MotionEventCompat.getActionIndex(ev);
-
-    if (action == MotionEvent.ACTION_DOWN) {
-      // Reset things for a new event stream, just in case we didn't get
-      // the whole previous stream.
-      cancel();
-    }
-
-    if (mVelocityTracker == null) {
-      mVelocityTracker = VelocityTracker.obtain();
-    }
-    mVelocityTracker.addMovement(ev);
-
-    switch (action) {
-      case MotionEvent.ACTION_DOWN: {
-        final float x = ev.getX();
-        final float y = ev.getY();
-        final int pointerId = MotionEventCompat.getPointerId(ev, 0);
-        final View toCapture = findTopChildUnder((int) x, (int) y);
-
-        saveInitialMotion(x, y, pointerId);
-
-        // Since the parent is already directly processing this touch event,
-        // there is no reason to delay for a slop before dragging.
-        // Start immediately if possible.
-        tryCaptureViewForDrag(toCapture, pointerId);
-
-        final int edgesTouched = mInitialEdgesTouched[pointerId];
-        if ((edgesTouched & mTrackingEdges) != 0) {
-          mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-        }
-        break;
-      }
-
-      case MotionEventCompat.ACTION_POINTER_DOWN: {
-        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
-        final float x = MotionEventCompat.getX(ev, actionIndex);
-        final float y = MotionEventCompat.getY(ev, actionIndex);
-
-        saveInitialMotion(x, y, pointerId);
-
-        // A ViewDragHelper can only manipulate one view at a time.
-        if (mDragState == STATE_IDLE) {
-          // If we're idle we can do anything! Treat it like a normal down event.
-
-          final View toCapture = findTopChildUnder((int) x, (int) y);
-          tryCaptureViewForDrag(toCapture, pointerId);
-
-          final int edgesTouched = mInitialEdgesTouched[pointerId];
-          if ((edgesTouched & mTrackingEdges) != 0) {
-            mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-          }
-        } else if (isCapturedViewUnder((int) x, (int) y)) {
-          // We're still tracking a captured view. If the same view is under this
-          // point, we'll swap to controlling it with this pointer instead.
-          // (This will still work if we're "catching" a settling view.)
-
-          tryCaptureViewForDrag(mCapturedView, pointerId);
-        }
-        break;
-      }
-
-      case MotionEvent.ACTION_MOVE: {
-        if (mDragState == STATE_DRAGGING) {
-          final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
-          final float x = MotionEventCompat.getX(ev, index);
-          final float y = MotionEventCompat.getY(ev, index);
-          final int idx = (int) (x - mLastMotionX[mActivePointerId]);
-          final int idy = (int) (y - mLastMotionY[mActivePointerId]);
-
-          dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);
-
-          saveLastMotion(ev);
-        } else {
-          // Check to see if any pointer is now over a draggable view.
-          final int pointerCount = MotionEventCompat.getPointerCount(ev);
-          for (int i = 0; i < pointerCount; i++) {
-            final int pointerId = MotionEventCompat.getPointerId(ev, i);
-            final float x = MotionEventCompat.getX(ev, i);
-            final float y = MotionEventCompat.getY(ev, i);
-            final float dx = x - mInitialMotionX[pointerId];
-            final float dy = y - mInitialMotionY[pointerId];
-
-            reportNewEdgeDrags(dx, dy, pointerId);
-            if (mDragState == STATE_DRAGGING) {
-              // Callback might have started an edge drag.
-              break;
-            }
-
-            final View toCapture = findTopChildUnder((int) mInitialMotionX[pointerId], (int) mInitialMotionY[pointerId]);
-            if (checkTouchSlop(toCapture, dx, dy) &&
-              tryCaptureViewForDrag(toCapture, pointerId)) {
-              break;
-            }
-          }
-          saveLastMotion(ev);
-        }
-        break;
-      }
-
-      case MotionEventCompat.ACTION_POINTER_UP: {
-        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
-        if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
-          // Try to find another pointer that's still holding on to the captured view.
-          int newActivePointer = INVALID_POINTER;
-          final int pointerCount = MotionEventCompat.getPointerCount(ev);
-          for (int i = 0; i < pointerCount; i++) {
-            final int id = MotionEventCompat.getPointerId(ev, i);
-            if (id == mActivePointerId) {
-              // This one's going away, skip.
-              continue;
-            }
-
-            final float x = MotionEventCompat.getX(ev, i);
-            final float y = MotionEventCompat.getY(ev, i);
-            if (findTopChildUnder((int) x, (int) y) == mCapturedView &&
-              tryCaptureViewForDrag(mCapturedView, id)) {
-              newActivePointer = mActivePointerId;
-              break;
-            }
-          }
-
-          if (newActivePointer == INVALID_POINTER) {
-            // We didn't find another pointer still touching the view, release it.
-            releaseViewForPointerUp();
-          }
-        }
-        clearMotionHistory(pointerId);
-        break;
-      }
-
-      case MotionEvent.ACTION_UP: {
-        if (mDragState == STATE_DRAGGING) {
-          releaseViewForPointerUp();
-        }
-        cancel();
-        break;
-      }
-
-      case MotionEvent.ACTION_CANCEL: {
-        if (mDragState == STATE_DRAGGING) {
-          dispatchViewReleased(0, 0);
-        }
-        cancel();
-        break;
-      }
-    }
-  }
-
-  private void reportNewEdgeDrags(float dx, float dy, int pointerId) {
-    int dragsStarted = 0;
-    if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {
-      dragsStarted |= EDGE_LEFT;
-    }
-    if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {
-      dragsStarted |= EDGE_TOP;
-    }
-    if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {
-      dragsStarted |= EDGE_RIGHT;
-    }
-    if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {
-      dragsStarted |= EDGE_BOTTOM;
-    }
-
-    if (dragsStarted != 0) {
-      mEdgeDragsInProgress[pointerId] |= dragsStarted;
-      mCallback.onEdgeDragStarted(dragsStarted, pointerId);
-    }
-  }
-
-  private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) {
-    final float absDelta = Math.abs(delta);
-    final float absODelta = Math.abs(odelta);
-
-    if ((mInitialEdgesTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0 ||
-      (mEdgeDragsLocked[pointerId] & edge) == edge ||
-      (mEdgeDragsInProgress[pointerId] & edge) == edge ||
-      (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {
-      return false;
-    }
-    if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {
-      mEdgeDragsLocked[pointerId] |= edge;
-      return false;
-    }
-    return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop;
-  }
-
-  /**
-   * Check if we've crossed a reasonable touch slop for the given child view.
-   * If the child cannot be dragged along the horizontal or vertical axis, motion
-   * along that axis will not count toward the slop check.
-   *
-   * @param child Child to check
-   * @param dx    Motion since initial position along X axis
-   * @param dy    Motion since initial position along Y axis
-   * @return true if the touch slop has been crossed
-   */
-  private boolean checkTouchSlop(View child, float dx, float dy) {
-    if (child == null) {
-      return false;
-    }
-    final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0;
-    final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;
-
-    if (checkHorizontal && checkVertical) {
-      return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
-    } else if (checkHorizontal) {
-      return Math.abs(dx) > mTouchSlop;
-    } else if (checkVertical) {
-      return Math.abs(dy) > mTouchSlop;
-    }
-    return false;
-  }
-
-  /**
-   * Check if any pointer tracked in the current gesture has crossed
-   * the required slop threshold.
-   * <p>
-   * <p>This depends on internal state populated by
-   * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-   * {@link #processTouchEvent(android.view.MotionEvent)}. You should only rely on
-   * the results of this method after all currently available touch data
-   * has been provided to one of these two methods.</p>
-   *
-   * @param directions Combination of direction flags, see {@link #DIRECTION_HORIZONTAL},
-   *                   {@link #DIRECTION_VERTICAL}, {@link #DIRECTION_ALL}
-   * @return true if the slop threshold has been crossed, false otherwise
-   */
-  public boolean checkTouchSlop(int directions) {
-    final int count = mInitialMotionX.length;
-    for (int i = 0; i < count; i++) {
-      if (checkTouchSlop(directions, i)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Check if the specified pointer tracked in the current gesture has crossed
-   * the required slop threshold.
-   * <p>
-   * <p>This depends on internal state populated by
-   * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-   * {@link #processTouchEvent(android.view.MotionEvent)}. You should only rely on
-   * the results of this method after all currently available touch data
-   * has been provided to one of these two methods.</p>
-   *
-   * @param directions Combination of direction flags, see {@link #DIRECTION_HORIZONTAL},
-   *                   {@link #DIRECTION_VERTICAL}, {@link #DIRECTION_ALL}
-   * @param pointerId  ID of the pointer to slop check as specified by MotionEvent
-   * @return true if the slop threshold has been crossed, false otherwise
-   */
-  public boolean checkTouchSlop(int directions, int pointerId) {
-    if (!isPointerDown(pointerId)) {
-      return false;
-    }
-
-    final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL;
-    final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL;
-
-    final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId];
-    final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId];
-
-    if (checkHorizontal && checkVertical) {
-      return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
-    } else if (checkHorizontal) {
-      return Math.abs(dx) > mTouchSlop;
-    } else if (checkVertical) {
-      return Math.abs(dy) > mTouchSlop;
-    }
-    return false;
-  }
-
-  /**
-   * Check if any of the edges specified were initially touched in the currently active gesture.
-   * If there is no currently active gesture this method will return false.
-   *
-   * @param edges Edges to check for an initial edge touch. See {@link #EDGE_LEFT},
-   *              {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, {@link #EDGE_BOTTOM} and
-   *              {@link #EDGE_ALL}
-   * @return true if any of the edges specified were initially touched in the current gesture
-   */
-  public boolean isEdgeTouched(int edges) {
-    final int count = mInitialEdgesTouched.length;
-    for (int i = 0; i < count; i++) {
-      if (isEdgeTouched(edges, i)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Check if any of the edges specified were initially touched by the pointer with
-   * the specified ID. If there is no currently active gesture or if there is no pointer with
-   * the given ID currently down this method will return false.
-   *
-   * @param edges Edges to check for an initial edge touch. See {@link #EDGE_LEFT},
-   *              {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, {@link #EDGE_BOTTOM} and
-   *              {@link #EDGE_ALL}
-   * @return true if any of the edges specified were initially touched in the current gesture
-   */
-  public boolean isEdgeTouched(int edges, int pointerId) {
-    return isPointerDown(pointerId) && (mInitialEdgesTouched[pointerId] & edges) != 0;
-  }
-
-  public boolean isDragging() {
-    return mDragState == STATE_DRAGGING;
-  }
-
-  private void releaseViewForPointerUp() {
-    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
-    final float xvel = clampMag(
-      VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
-      mMinVelocity, mMaxVelocity);
-    final float yvel = clampMag(
-      VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
-      mMinVelocity, mMaxVelocity);
-    dispatchViewReleased(xvel, yvel);
-  }
-
-  private void dragTo(int left, int top, int dx, int dy) {
-    int clampedX = left;
-    int clampedY = top;
-    final int oldLeft = mCapturedView.getLeft();
-    final int oldTop = mCapturedView.getTop();
-    if (dx != 0) {
-      clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
-      mCapturedView.offsetLeftAndRight(clampedX - oldLeft);
-    }
-    if (dy != 0) {
-      clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
-      mCapturedView.offsetTopAndBottom(clampedY - oldTop);
-    }
-
-    if (dx != 0 || dy != 0) {
-      final int clampedDx = clampedX - oldLeft;
-      final int clampedDy = clampedY - oldTop;
-      mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,
-        clampedDx, clampedDy);
-    }
-  }
-
-  /**
-   * Determine if the currently captured view is under the given point in the
-   * parent view's coordinate system. If there is no captured view this method
-   * will return false.
-   *
-   * @param x X position to test in the parent's coordinate system
-   * @param y Y position to test in the parent's coordinate system
-   * @return true if the captured view is under the given point, false otherwise
-   */
-  public boolean isCapturedViewUnder(int x, int y) {
-    return isViewUnder(mCapturedView, x, y);
-  }
-
-  /**
-   * Determine if the supplied view is under the given point in the
-   * parent view's coordinate system.
-   *
-   * @param view Child view of the parent to hit test
-   * @param x    X position to test in the parent's coordinate system
-   * @param y    Y position to test in the parent's coordinate system
-   * @return true if the supplied view is under the given point, false otherwise
-   */
-  public boolean isViewUnder(View view, int x, int y) {
-    if (view == null) {
-      return false;
-    }
-    return x >= view.getLeft() &&
-      x < view.getRight() &&
-      y >= view.getTop() &&
-      y < view.getBottom();
-  }
-
-  /**
-   * Find the topmost child under the given point within the parent view's coordinate system.
-   * The child order is determined using {@link Callback#getOrderedChildIndex(int)}.
-   *
-   * @param x X position to test in the parent's coordinate system
-   * @param y Y position to test in the parent's coordinate system
-   * @return The topmost child view under (x, y) or null if none found.
-   */
-  public View findTopChildUnder(int x, int y) {
-    final int childCount = mParentView.getChildCount();
-    for (int i = childCount - 1; i >= 0; i--) {
-      final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
-      if (x >= child.getLeft() && x < child.getRight() &&
-        y >= child.getTop() && y < child.getBottom()) {
-        return child;
-      }
-    }
-    return null;
-  }
-
-  private int getEdgesTouched(int x, int y) {
-    int result = 0;
-
-    if (x < mParentView.getLeft() + mEdgeSize) result |= EDGE_LEFT;
-    if (y < mParentView.getTop() + mEdgeSize) result |= EDGE_TOP;
-    if (x > mParentView.getRight() - mEdgeSize) result |= EDGE_RIGHT;
-    if (y > mParentView.getBottom() - mEdgeSize) result |= EDGE_BOTTOM;
-
-    return result;
-  }
-
-  /**
-   * A Callback is used as a communication channel with the ViewDragHelper back to the
-   * parent view using it. <code>on*</code>methods are invoked on siginficant events and several
-   * accessor methods are expected to provide the ViewDragHelper with more information
-   * about the state of the parent view upon request. The callback also makes decisions
-   * governing the range and draggability of child views.
-   */
-  public static abstract class Callback {
-    /**
-     * Called when the drag state changes. See the <code>STATE_*</code> constants
-     * for more information.
-     *
-     * @param state The new drag state
-     * @see #STATE_IDLE
-     * @see #STATE_DRAGGING
-     * @see #STATE_SETTLING
-     */
-    public void onViewDragStateChanged(int state) {
-    }
-
-    /**
-     * Called when the captured view's position changes as the result of a drag or settle.
-     *
-     * @param changedView View whose position changed
-     * @param left        New X coordinate of the left edge of the view
-     * @param top         New Y coordinate of the top edge of the view
-     * @param dx          Change in X position from the last call
-     * @param dy          Change in Y position from the last call
-     */
-    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
-    }
-
-    /**
-     * Called when a child view is captured for dragging or settling. The ID of the pointer
-     * currently dragging the captured view is supplied. If activePointerId is
-     * identified as {@link #INVALID_POINTER} the capture is programmatic instead of
-     * pointer-initiated.
-     *
-     * @param capturedChild   Child view that was captured
-     * @param activePointerId Pointer id tracking the child capture
-     */
-    public void onViewCaptured(View capturedChild, int activePointerId) {
-    }
-
-    /**
-     * Called when the child view is no longer being actively dragged.
-     * The fling velocity is also supplied, if relevant. The velocity values may
-     * be clamped to system minimums or maximums.
-     * <p>
-     * <p>Calling code may decide to fling or otherwise release the view to let it
-     * settle into place. It should do so using {@link #settleCapturedViewAt(int, int)}
-     * or {@link #flingCapturedView(int, int, int, int)}. If the Callback invokes
-     * one of these methods, the ViewDragHelper will enter {@link #STATE_SETTLING}
-     * and the view capture will not fully end until it comes to a complete stop.
-     * If neither of these methods is invoked before <code>onViewReleased</code> returns,
-     * the view will stop in place and the ViewDragHelper will return to
-     * {@link #STATE_IDLE}.</p>
-     *
-     * @param releasedChild The captured child view now being released
-     * @param xvel          X velocity of the pointer as it left the screen in pixels per second.
-     * @param yvel          Y velocity of the pointer as it left the screen in pixels per second.
-     */
-    public void onViewReleased(View releasedChild, float xvel, float yvel) {
-    }
-
-    /**
-     * Called when one of the subscribed edges in the parent view has been touched
-     * by the user while no child view is currently captured.
-     *
-     * @param edgeFlags A combination of edge flags describing the edge(s) currently touched
-     * @param pointerId ID of the pointer touching the described edge(s)
-     * @see #EDGE_LEFT
-     * @see #EDGE_TOP
-     * @see #EDGE_RIGHT
-     * @see #EDGE_BOTTOM
-     */
-    public void onEdgeTouched(int edgeFlags, int pointerId) {
-    }
-
-    /**
-     * Called when the given edge may become locked. This can happen if an edge drag
-     * was preliminarily rejected before beginning, but after {@link #onEdgeTouched(int, int)}
-     * was called. This method should return true to lock this edge or false to leave it
-     * unlocked. The default behavior is to leave edges unlocked.
-     *
-     * @param edgeFlags A combination of edge flags describing the edge(s) locked
-     * @return true to lock the edge, false to leave it unlocked
-     */
-    public boolean onEdgeLock(int edgeFlags) {
-      return false;
-    }
-
-    /**
-     * Called when the user has started a deliberate drag away from one
-     * of the subscribed edges in the parent view while no child view is currently captured.
-     *
-     * @param edgeFlags A combination of edge flags describing the edge(s) dragged
-     * @param pointerId ID of the pointer touching the described edge(s)
-     * @see #EDGE_LEFT
-     * @see #EDGE_TOP
-     * @see #EDGE_RIGHT
-     * @see #EDGE_BOTTOM
-     */
-    public void onEdgeDragStarted(int edgeFlags, int pointerId) {
-    }
-
-    /**
-     * Called to determine the Z-order of child views.
-     *
-     * @param index the ordered position to query for
-     * @return index of the view that should be ordered at position <code>index</code>
-     */
-    public int getOrderedChildIndex(int index) {
-      return index;
-    }
-
-    /**
-     * Return the magnitude of a draggable child view's horizontal range of motion in pixels.
-     * This method should return 0 for views that cannot move horizontally.
-     *
-     * @param child Child view to check
-     * @return range of horizontal motion in pixels
-     */
-    public int getViewHorizontalDragRange(View child) {
-      return 0;
-    }
-
-    /**
-     * Return the magnitude of a draggable child view's vertical range of motion in pixels.
-     * This method should return 0 for views that cannot move vertically.
-     *
-     * @param child Child view to check
-     * @return range of vertical motion in pixels
-     */
-    public int getViewVerticalDragRange(View child) {
-      return 0;
-    }
-
-    /**
-     * Called when the user's input indicates that they want to capture the given child view
-     * with the pointer indicated by pointerId. The callback should return true if the user
-     * is permitted to drag the given view with the indicated pointer.
-     * <p>
-     * <p>ViewDragHelper may call this method multiple times for the same view even if
-     * the view is already captured; this indicates that a new pointer is trying to take
-     * control of the view.</p>
-     * <p>
-     * <p>If this method returns true, a call to {@link #onViewCaptured(android.view.View, int)}
-     * will follow if the capture is successful.</p>
-     *
-     * @param child     Child the user is attempting to capture
-     * @param pointerId ID of the pointer attempting the capture
-     * @return true if capture should be allowed, false otherwise
-     */
-    public abstract boolean tryCaptureView(View child, int pointerId);
-
-    /**
-     * Restrict the motion of the dragged child view along the horizontal axis.
-     * The default implementation does not allow horizontal motion; the extending
-     * class must override this method and provide the desired clamping.
-     *
-     * @param child Child view being dragged
-     * @param left  Attempted motion along the X axis
-     * @param dx    Proposed change in position for left
-     * @return The new clamped position for left
-     */
-    public int clampViewPositionHorizontal(View child, int left, int dx) {
-      return 0;
-    }
-
-    /**
-     * Restrict the motion of the dragged child view along the vertical axis.
-     * The default implementation does not allow vertical motion; the extending
-     * class must override this method and provide the desired clamping.
-     *
-     * @param child Child view being dragged
-     * @param top   Attempted motion along the Y axis
-     * @param dy    Proposed change in position for top
-     * @return The new clamped position for top
-     */
-    public int clampViewPositionVertical(View child, int top, int dy) {
-      return 0;
-    }
-  }
-}
diff --git a/slidingpanel/src/main/res/values/attrs.xml b/slidingpanel/src/main/res/values/attrs.xml
deleted file mode 100644
index 37c95b229..000000000
--- a/slidingpanel/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Quasseldroid - Quassel client for Android
-
-  Copyright (c) 2018 Janne Koschinski
-  Copyright (c) 2018 The Quassel Project
-
-  This program is free software: you can redistribute it and/or modify it
-  under the terms of the GNU General Public License version 3 as published
-  by the Free Software Foundation.
-
-  This program 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.
-
-  You should have received a copy of the GNU General Public License along
-  with this program.  If not, see <http://www.gnu.org/licenses/>.
-  -->
-
-<resources>
-
-  <declare-styleable name="SlidingUpPanelLayout">
-    <attr name="umanoPanelHeight" format="dimension" />
-    <attr name="umanoShadowHeight" format="dimension" />
-    <attr name="umanoParallaxOffset" format="dimension" />
-    <attr name="umanoFadeColor" format="color" />
-    <attr name="umanoFlingVelocity" format="integer" />
-    <attr name="umanoDragView" format="reference" />
-    <attr name="umanoAntiDragView" format="reference" />
-    <attr name="umanoScrollableView" format="reference" />
-    <attr name="umanoOverlay" format="boolean" />
-    <attr name="umanoClipPanel" format="boolean" />
-    <attr name="umanoAnchorPoint" format="float" />
-    <attr name="umanoInitialState" format="enum">
-      <enum name="expanded" value="0" />
-      <enum name="collapsed" value="1" />
-      <enum name="anchored" value="2" />
-      <enum name="hidden" value="3" />
-    </attr>
-    <attr name="umanoScrollInterpolator" format="reference" />
-  </declare-styleable>
-
-</resources>
-- 
GitLab