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/res/menu/profile_manager_menu.xml b/src/main/res/menu/profile_manager_menu.xml index 14924d01..e087585f 100644 --- a/src/main/res/menu/profile_manager_menu.xml +++ b/src/main/res/menu/profile_manager_menu.xml @@ -4,5 +4,7 @@ + android:numericShortcut="1" + android:icon="@drawable/ic_content_copy" + app:showAsAction="always"/> 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 _ =>