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) + } +}