diff --git a/src/main/res/drawable/ic_file_download.xml b/src/main/res/drawable/ic_file_download.xml
deleted file mode 100644
index 7b463f08..00000000
--- a/src/main/res/drawable/ic_file_download.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/src/main/res/drawable/ic_file_upload.xml b/src/main/res/drawable/ic_file_upload.xml
deleted file mode 100644
index 9edf6e49..00000000
--- a/src/main/res/drawable/ic_file_upload.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/src/main/res/drawable/ic_settings.xml b/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 00000000..f02db1bf
--- /dev/null
+++ b/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/main/res/layout-w400dp/layout_main_stat_title.xml b/src/main/res/layout-w400dp/layout_main_stat_title.xml
new file mode 100644
index 00000000..03370531
--- /dev/null
+++ b/src/main/res/layout-w400dp/layout_main_stat_title.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/main/res/layout/layout_main.xml b/src/main/res/layout/layout_main.xml
index c42b69e8..4f8bf0cb 100644
--- a/src/main/res/layout/layout_main.xml
+++ b/src/main/res/layout/layout_main.xml
@@ -14,16 +14,7 @@
android:paddingLeft="16dip" android:paddingRight="88dip"
android:paddingTop="16dip" android:paddingBottom="16dip"
android:background="@color/material_accent_300" android:orientation="vertical">
-
-
-
-
+
+
+
+
+
diff --git a/src/main/res/layout/layout_profiles.xml b/src/main/res/layout/layout_profiles.xml
index 3e178624..ca804ad8 100644
--- a/src/main/res/layout/layout_profiles.xml
+++ b/src/main/res/layout/layout_profiles.xml
@@ -6,11 +6,10 @@
android:layout_height="match_parent">
+ android:layout_height="0dp" android:layout_weight="1">
+ android:layout_height="match_parent"/>
+ app:fab_colorRipple="@android:color/white"
+ app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior">
@@ -42,7 +41,7 @@
android:src="@drawable/ic_camera_alt"
app:fab_colorNormal="@color/material_accent_500"
app:fab_colorPressed="@color/material_accent_700"
- app:fab_colorRipple="@color/material_accent_500"
+ app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="@string/add_profile_methods_scan_qr_code"
/>
@@ -53,7 +52,7 @@
android:src="@drawable/ic_nfc"
app:fab_colorNormal="@color/material_accent_500"
app:fab_colorPressed="@color/material_accent_700"
- app:fab_colorRipple="@color/material_accent_500"
+ app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="NFC"
/>
@@ -64,7 +63,7 @@
android:src="@drawable/ic_content_paste"
app:fab_colorNormal="@color/material_accent_500"
app:fab_colorPressed="@color/material_accent_700"
- app:fab_colorRipple="@color/material_accent_500"
+ app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="@string/action_import"
/>
diff --git a/src/main/res/menu/app_manager_menu.xml b/src/main/res/menu/app_manager_menu.xml
index ef91b2ce..6eaa911d 100644
--- a/src/main/res/menu/app_manager_menu.xml
+++ b/src/main/res/menu/app_manager_menu.xml
@@ -1,12 +1,15 @@
-
diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala
index 2c4653c2..f3eff9fd 100644
--- a/src/main/scala/com/github/shadowsocks/BaseService.scala
+++ b/src/main/scala/com/github/shadowsocks/BaseService.scala
@@ -44,6 +44,7 @@ import java.util.{Timer, TimerTask}
import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
import android.os.{Handler, RemoteCallbackList}
+import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
@@ -104,9 +105,10 @@ trait BaseService extends Service {
}
override def use(config: Config) = synchronized(state match {
- case State.STOPPED => if (config != null) startRunner(config)
+ case State.STOPPED => if (config != null && checkConfig(config)) startRunner(config)
case State.CONNECTED =>
- if (config == null) stopRunner(true) else if (config.profileId != BaseService.this.config.profileId) {
+ if (config == null) stopRunner(true)
+ else if (config.profileId != BaseService.this.config.profileId && checkConfig(config)) {
stopRunner(false)
startRunner(config)
}
@@ -114,6 +116,12 @@ trait BaseService extends Service {
})
}
+ def checkConfig(config: Config) = if (TextUtils.isEmpty(config.proxy) || TextUtils.isEmpty(config.sitekey)) {
+ changeState(State.STOPPED)
+ stopRunner(true)
+ false
+ } else true
+
def startRunner(config: Config) {
this.config = config
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
index 259c4995..f582e15b 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
@@ -39,11 +39,11 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks])
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
- .addAction(android.R.drawable.ic_menu_close_clear_cancel, service.getString(R.string.stop),
+ .addAction(R.drawable.ic_close, service.getString(R.string.stop),
PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0))
ShadowsocksApplication.profileManager.getAllProfiles match {
case Some(profiles) => if (profiles.length > 1)
- builder.addAction(android.R.drawable.ic_menu_manage, service.getString(R.string.quick_switch),
+ builder.addAction(R.drawable.ic_settings, service.getString(R.string.quick_switch),
PendingIntent.getActivity(service, 0, new Intent(Action.QUICK_SWITCH), 0))
case _ =>
}
diff --git a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
index e40426d8..3bf913ec 100644
--- a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
+++ b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
@@ -44,9 +44,9 @@ import android.os.{Build, Bundle}
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.view.{LayoutInflater, View, ViewGroup}
-import android.widget.{Switch, CheckedTextView}
+import android.widget.{CheckedTextView, Switch}
import com.github.shadowsocks.database.Profile
-import com.github.shadowsocks.helper.TaskerSettings
+import com.github.shadowsocks.utils.TaskerSettings
/**
* @author CzBiX
diff --git a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala
index 1a3ed99d..f321cb5e 100644
--- a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala
+++ b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala
@@ -39,8 +39,7 @@
package com.github.shadowsocks
import android.content.{BroadcastReceiver, Context, Intent}
-import com.github.shadowsocks.helper.TaskerSettings
-import com.github.shadowsocks.utils.Utils
+import com.github.shadowsocks.utils.{TaskerSettings, Utils}
/**
* @author CzBiX
diff --git a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala b/src/main/scala/com/github/shadowsocks/utils/TaskerSettings.scala
similarity index 98%
rename from src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala
rename to src/main/scala/com/github/shadowsocks/utils/TaskerSettings.scala
index 2b2f18e5..bc6fb0ee 100644
--- a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala
+++ b/src/main/scala/com/github/shadowsocks/utils/TaskerSettings.scala
@@ -36,7 +36,7 @@
* HERE BE DRAGONS
*
*/
-package com.github.shadowsocks.helper
+package com.github.shadowsocks.utils
import android.content.{Context, Intent}
import android.os.Bundle
diff --git a/src/main/scala/com/github/shadowsocks/widget/AutoPaddingBehavior.scala b/src/main/scala/com/github/shadowsocks/widget/AutoPaddingBehavior.scala
deleted file mode 100644
index 56211a78..00000000
--- a/src/main/scala/com/github/shadowsocks/widget/AutoPaddingBehavior.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.github.shadowsocks.widget
-
-import android.content.Context
-import android.support.design.widget.CoordinatorLayout
-import android.support.design.widget.CoordinatorLayout.Behavior
-import android.support.design.widget.Snackbar.SnackbarLayout
-import android.util.AttributeSet
-import android.view.View
-
-/**
- * @author Mygod
- */
-class AutoPaddingBehavior(context: Context, attrs: AttributeSet) extends Behavior[View] {
- override def layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View) =
- dependency.isInstanceOf[SnackbarLayout]
-
- override def onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View) =
- dependency match {
- case sl: SnackbarLayout =>
- child.setPadding(0, 0, 0, dependency.getHeight)
- true
- case _ => super.onDependentViewChanged(parent, child, dependency)
- }
-
- override def onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) =
- dependency match {
- case sl: SnackbarLayout => child.setPadding(0, 0, 0, 0)
- case _ => super.onDependentViewRemoved(parent, child, dependency)
- }
-}
diff --git a/src/main/scala/com/github/shadowsocks/widget/FloatingActionMenuBehavior.scala b/src/main/scala/com/github/shadowsocks/widget/FloatingActionMenuBehavior.scala
new file mode 100644
index 00000000..6872668a
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/widget/FloatingActionMenuBehavior.scala
@@ -0,0 +1,64 @@
+package com.github.shadowsocks.widget
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.support.design.widget.CoordinatorLayout
+import android.support.design.widget.CoordinatorLayout.Behavior
+import android.support.design.widget.Snackbar.SnackbarLayout
+import android.support.v4.view.animation.FastOutSlowInInterpolator
+import android.util.AttributeSet
+import android.view.View
+import com.github.clans.fab.FloatingActionMenu
+
+import scala.collection.JavaConverters._
+
+/**
+ * Behavior for com.github.clans.fab.FloatingActionMenu that is aware of Snackbars and scrolling.
+ *
+ * @author Mygod
+ */
+class FloatingActionMenuBehavior(context: Context, attrs: AttributeSet)
+ extends Behavior[FloatingActionMenu](context, attrs) {
+ private var fabTranslationYAnimator: ValueAnimator = _
+ private var fabTranslationY: Float = _
+
+ override def layoutDependsOn(parent: CoordinatorLayout, child: FloatingActionMenu, dependency: View) =
+ dependency.isInstanceOf[SnackbarLayout]
+
+ override def onDependentViewChanged(parent: CoordinatorLayout, child: FloatingActionMenu, dependency: View) = {
+ 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 (fabTranslationY != targetTransY) {
+ val currentTransY = child.getTranslationY
+ if (fabTranslationYAnimator != null && fabTranslationYAnimator.isRunning) fabTranslationYAnimator.cancel
+ if (child.isShown && Math.abs(currentTransY - targetTransY) > child.getHeight * 0.667F) {
+ if (fabTranslationYAnimator == null) {
+ fabTranslationYAnimator = new ValueAnimator
+ fabTranslationYAnimator.setInterpolator(new FastOutSlowInInterpolator)
+ fabTranslationYAnimator.addUpdateListener(animation =>
+ child.setTranslationY(animation.getAnimatedValue.asInstanceOf[Float]))
+ }
+ fabTranslationYAnimator.setFloatValues(currentTransY, targetTransY)
+ fabTranslationYAnimator.start
+ } else child.setTranslationY(targetTransY)
+ fabTranslationY = targetTransY
+ }
+ }
+ false
+ }
+
+ override def onStartNestedScroll(parent: CoordinatorLayout, child: FloatingActionMenu, directTargetChild: View,
+ target: View, nestedScrollAxes: Int) = true
+ override def onNestedScroll(parent: CoordinatorLayout, child: FloatingActionMenu, target: View, dxConsumed: Int,
+ dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) = {
+ super.onNestedScroll(parent, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed)
+ val dy = dyConsumed + dyUnconsumed
+ if (child.isMenuButtonHidden) {
+ if (dy < 0) child.showMenuButton(true)
+ } else if (dy > 0) child.hideMenuButton(true)
+ }
+}
diff --git a/src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala b/src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala
index 914ac0ad..7d64cea1 100644
--- a/src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala
+++ b/src/main/scala/com/github/shadowsocks/widget/UndoSnackbarManager.scala
@@ -19,7 +19,8 @@ class UndoSnackbarManager[T](view: View, undo: Iterator[(Int, T)] => Unit,
private val removedCallback = new Snackbar.Callback {
override def onDismissed(snackbar: Snackbar, event: Int) = {
event match {
- case Snackbar.Callback.DISMISS_EVENT_MANUAL | Snackbar.Callback.DISMISS_EVENT_TIMEOUT =>
+ case Snackbar.Callback.DISMISS_EVENT_SWIPE | Snackbar.Callback.DISMISS_EVENT_MANUAL |
+ Snackbar.Callback.DISMISS_EVENT_TIMEOUT =>
if (commit != null) commit(recycleBin.iterator)
recycleBin.clear
case _ =>