UI fixes
* Refine exceptions handling when parsing per-app config string * Remove redundant profiles Preference * Move Network Traffic to title bar * Add ripple to title text * Animate using animateLayoutChanges * Hide connection test in NAT mode * Refine UI. * Change notification action button icon for hidpi device * Fix FAB ripple * Fix usability problem * Fix changes not commited when snackbar being swiped * Show import/export as action * Drop unused drawables * Move TaskerSettings * Fix action icons * Prevent switching to an invalid profile * Fix line wrap when screen width is small
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView android:text="@string/stat" android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@+id/connection_test" android:gravity="end"
|
||||
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
@@ -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">
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView android:text="@string/stat" android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@+id/connection_test"
|
||||
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
|
||||
android:ellipsize="marquee" android:gravity="end"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
<include layout="@layout/layout_main_stat_title"/>
|
||||
<android.support.v7.widget.GridLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" app:columnCount="4">
|
||||
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||
<TextView android:text="@string/stat" android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary" android:ellipsize="marquee"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@+id/connection_test"
|
||||
android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
@@ -6,11 +6,10 @@
|
||||
android:layout_height="match_parent">
|
||||
<include layout="@layout/toolbar_light_dark" />
|
||||
<android.support.design.widget.CoordinatorLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="0dp" android:layout_weight="1">
|
||||
<android.support.v7.widget.RecyclerView android:id="@+id/profilesList"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.AutoPaddingBehavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"/>
|
||||
<com.github.clans.fab.FloatingActionMenu
|
||||
android:id="@+id/menu"
|
||||
android:layout_width="match_parent"
|
||||
@@ -21,8 +20,8 @@
|
||||
android:layout_marginEnd="16dp"
|
||||
app:menu_colorNormal="@color/material_accent_500"
|
||||
app:menu_colorPressed="@color/material_accent_700"
|
||||
app:menu_colorRipple="@color/material_accent_500"
|
||||
>
|
||||
app:fab_colorRipple="@android:color/white"
|
||||
app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior">
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fab_manual_add"
|
||||
@@ -31,7 +30,7 @@
|
||||
android:src="@drawable/ic_create"
|
||||
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_manual_settings"
|
||||
/>
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:id="@+id/action_import"
|
||||
android:title="@string/action_import"
|
||||
android:showAsAction="never"
|
||||
android:alphabeticShortcut="i"
|
||||
android:numericShortcut="1"/>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_export"
|
||||
android:title="@string/action_export"
|
||||
android:showAsAction="never"
|
||||
android:icon="@drawable/ic_content_copy"
|
||||
app:showAsAction="ifRoom"
|
||||
android:alphabeticShortcut="e"
|
||||
android:numericShortcut="1"/>
|
||||
<item android:id="@+id/action_import"
|
||||
android:title="@string/action_import"
|
||||
android:icon="@drawable/ic_content_paste"
|
||||
app:showAsAction="ifRoom"
|
||||
android:alphabeticShortcut="i"
|
||||
android:numericShortcut="2"/>
|
||||
</menu>
|
||||
|
||||
@@ -4,5 +4,7 @@
|
||||
<item android:id="@+id/action_export"
|
||||
android:title="@string/action_export"
|
||||
android:alphabeticShortcut="e"
|
||||
app:showAsAction="never" />
|
||||
android:numericShortcut="1"
|
||||
android:icon="@drawable/ic_content_copy"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 _ =>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 _ =>
|
||||
|
||||
Reference in New Issue
Block a user