diff --git a/src/main/res/layout/layout_main.xml b/src/main/res/layout/layout_main.xml
index 7cbd6231..4909e222 100644
--- a/src/main/res/layout/layout_main.xml
+++ b/src/main/res/layout/layout_main.xml
@@ -1,15 +1,18 @@
-
+
+ app:layout_anchor="@+id/stat"
+ app:layout_behavior="com.github.shadowsocks.widget.BottomAwareBehavior"/>
+ app:layout_behavior="com.github.shadowsocks.widget.BottomBehavior">
+ android:layout_marginBottom="@dimen/fab_margin_bottom"
+ app:layout_behavior="com.github.shadowsocks.widget.BottomBehavior"
+ app:layout_anchor="@+id/stat">
-
+
diff --git a/src/main/res/values-v21/dimen.xml b/src/main/res/values-v21/dimen.xml
index 605379c3..4c95daeb 100644
--- a/src/main/res/values-v21/dimen.xml
+++ b/src/main/res/values-v21/dimen.xml
@@ -1,4 +1,4 @@
- -28dp
+ -28dp
diff --git a/src/main/res/values/dimen.xml b/src/main/res/values/dimen.xml
index c11d2a7a..f8e782e7 100644
--- a/src/main/res/values/dimen.xml
+++ b/src/main/res/values/dimen.xml
@@ -1,5 +1,5 @@
- -44dp
+ -44dp
diff --git a/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala b/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
index c9ba1f65..11706269 100644
--- a/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
+++ b/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
@@ -26,7 +26,7 @@ class GlobalConfigFragment extends PreferenceFragment {
val value = v.asInstanceOf[Boolean]
val result = TcpFastOpen.enabled(value)
if (result != null && result != "Success.")
- Snackbar.make(getActivity.findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show()
+ Snackbar.make(getActivity.findViewById(R.id.content), result, Snackbar.LENGTH_LONG).show()
value == TcpFastOpen.sendEnabled
})
if (!TcpFastOpen.supported) {
diff --git a/src/main/scala/com/github/shadowsocks/MainActivity.scala b/src/main/scala/com/github/shadowsocks/MainActivity.scala
index fae18f81..921af8f5 100644
--- a/src/main/scala/com/github/shadowsocks/MainActivity.scala
+++ b/src/main/scala/com/github/shadowsocks/MainActivity.scala
@@ -105,7 +105,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
fab.setEnabled(true)
changeSwitch(checked = false)
if (m != null) {
- val snackbar = Snackbar.make(findViewById(android.R.id.content),
+ val snackbar = Snackbar.make(findViewById(R.id.content),
getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), Snackbar.LENGTH_LONG)
if (m == getString(R.string.nat_no_root)) addDisableNatToSnackbar(snackbar)
snackbar.show()
@@ -276,7 +276,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
synchronized(if (serviceStarted && testCount == id && app.isVpnEnabled) handler.post(() =>
if (success) statusText.setText(result) else {
statusText.setText(R.string.connection_test_fail)
- Snackbar.make(findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show()
+ Snackbar.make(findViewById(R.id.content), result, Snackbar.LENGTH_LONG).show()
}))
}
}
diff --git a/src/main/scala/com/github/shadowsocks/widget/BottomAwareBehavior.scala b/src/main/scala/com/github/shadowsocks/widget/BottomAwareBehavior.scala
new file mode 100644
index 00000000..1b82f7cf
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/widget/BottomAwareBehavior.scala
@@ -0,0 +1,23 @@
+package com.github.shadowsocks.widget
+
+import android.content.Context
+import android.support.design.widget.CoordinatorLayout
+import android.util.AttributeSet
+import android.view.View
+
+/**
+ * Behavior for content area that's aware of bottom bars.
+ *
+ * @author Mygod
+ */
+class BottomAwareBehavior(context: Context, attrs: AttributeSet)
+ extends CoordinatorLayout.Behavior[View](context, attrs) {
+ override def onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean = {
+ val params = child.getLayoutParams.asInstanceOf[CoordinatorLayout.LayoutParams]
+ val margin = parent.getHeight - dependency.getTop - dependency.getTranslationY.toInt
+ if (params.bottomMargin != margin) {
+ params.bottomMargin = margin
+ true
+ } else false
+ }
+}
diff --git a/src/main/scala/com/github/shadowsocks/widget/BottomBehavior.scala b/src/main/scala/com/github/shadowsocks/widget/BottomBehavior.scala
new file mode 100644
index 00000000..a3a24d86
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/widget/BottomBehavior.scala
@@ -0,0 +1,78 @@
+package com.github.shadowsocks.widget
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.support.design.widget.CoordinatorLayout
+import android.support.design.widget.Snackbar.SnackbarLayout
+import android.support.v4.view.animation.FastOutSlowInInterpolator
+import android.util.AttributeSet
+import android.view.View
+
+import scala.collection.JavaConverters._
+
+/**
+ * A SnackbarLayout-aware behavior.
+ *
+ * TODO: What happened to the animations?
+ *
+ * @author Mygod
+ */
+class BottomBehavior(context: Context, attrs: AttributeSet) extends CoordinatorLayout.Behavior[View](context, attrs) {
+ private var translationYAnimator: ValueAnimator = _
+ private var translationY: Float = _
+
+ override def layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean = dependency match {
+ case _: SnackbarLayout => true
+ case _ => super.layoutDependsOn(parent, child, dependency)
+ }
+
+ override def onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean = {
+ dependency match {
+ case _: SnackbarLayout =>
+ var targetTransY = parent.getDependencies(child).asScala
+ .filter(view => view.isInstanceOf[SnackbarLayout] && parent.doViewsOverlap(child, view))
+ .map(view => view.getTranslationY - view.getHeight).reduceOption(_ min _).getOrElse(0F)
+ if (targetTransY > 0) targetTransY = 0
+ if (translationY != targetTransY) {
+ val currentTransY = child.getTranslationY
+ if (translationYAnimator != null && translationYAnimator.isRunning) translationYAnimator.cancel()
+ if (child.isShown && Math.abs(currentTransY - targetTransY) > child.getHeight * 0.667F) {
+ if (translationYAnimator == null) {
+ translationYAnimator = new ValueAnimator
+ translationYAnimator.setInterpolator(new FastOutSlowInInterpolator())
+ translationYAnimator.addUpdateListener(animation =>
+ child.setTranslationY(animation.getAnimatedValue.asInstanceOf[Float]))
+ }
+ translationYAnimator.setFloatValues(currentTransY, targetTransY)
+ translationYAnimator.start()
+ } else child.setTranslationY(targetTransY)
+ translationY = targetTransY
+ }
+ true
+ case _ => super.onDependentViewChanged(parent, child, dependency)
+ }
+ }
+
+ override def onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View): Unit = {
+ dependency match {
+ case _: SnackbarLayout =>
+ if (translationY != 0) {
+ val currentTransY = child.getTranslationY
+ if (translationYAnimator != null && translationYAnimator.isRunning) translationYAnimator.cancel()
+ if (child.isShown && Math.abs(currentTransY) > child.getHeight * 0.667F) {
+ if (translationYAnimator == null) {
+ translationYAnimator = new ValueAnimator
+ translationYAnimator.setInterpolator(new FastOutSlowInInterpolator())
+ translationYAnimator.addUpdateListener(animation =>
+ child.setTranslationY(animation.getAnimatedValue.asInstanceOf[Float]))
+ }
+ translationYAnimator.setFloatValues(currentTransY, 0)
+ translationYAnimator.start()
+ } else child.setTranslationY(0)
+ translationY = 0
+ }
+ parent.dispatchDependentViewsChanged(child)
+ }
+ super.onDependentViewRemoved(parent, child, dependency)
+ }
+}