Commit ef519a85 authored by Mygod's avatar Mygod

Clean up ServiceButton related code

parent 540b1d37
...@@ -31,14 +31,12 @@ import android.nfc.NfcAdapter ...@@ -31,14 +31,12 @@ import android.nfc.NfcAdapter
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.support.customtabs.CustomTabsIntent import android.support.customtabs.CustomTabsIntent
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar import android.support.design.widget.Snackbar
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.content.res.AppCompatResources import android.support.v7.content.res.AppCompatResources
import android.support.v7.preference.PreferenceDataStore import android.support.v7.preference.PreferenceDataStore
import android.support.v7.widget.TooltipCompat
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
...@@ -87,8 +85,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe ...@@ -87,8 +85,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
} }
// UI // UI
private lateinit var fab: FloatingActionButton private lateinit var fab: ServiceButton
private val fabHelper by lazy { ServiceButton(fab) }
internal var crossfader: Crossfader<CrossFadeSlidingPaneLayout>? = null internal var crossfader: Crossfader<CrossFadeSlidingPaneLayout>? = null
internal lateinit var drawer: Drawer internal lateinit var drawer: Drawer
private var previousSelectedDrawer: Long = 0 // it's actually lateinit private var previousSelectedDrawer: Long = 0 // it's actually lateinit
...@@ -110,9 +107,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe ...@@ -110,9 +107,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
} catch (_: ActivityNotFoundException) { } // ignore } catch (_: ActivityNotFoundException) { } // ignore
fun launchUrl(uri: String) = launchUrl(Uri.parse(uri)) fun launchUrl(uri: String) = launchUrl(Uri.parse(uri))
private val greyTint by lazy { ContextCompat.getColorStateList(this, R.color.material_primary_500) }
private val greenTint by lazy { ContextCompat.getColorStateList(this, R.color.material_green_700) }
// service // service
var state = BaseService.IDLE var state = BaseService.IDLE
override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy { override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy {
...@@ -130,7 +124,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe ...@@ -130,7 +124,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
} }
fun changeState(state: Int, msg: String? = null, animate: Boolean = false) { fun changeState(state: Int, msg: String? = null, animate: Boolean = false) {
fabHelper.changeState(state, animate) fab.changeState(state, animate)
when (state) { when (state) {
BaseService.CONNECTING -> statusText.setText(R.string.connecting) BaseService.CONNECTING -> statusText.setText(R.string.connecting)
BaseService.CONNECTED -> statusText.setText(R.string.vpn_connected) BaseService.CONNECTED -> statusText.setText(R.string.vpn_connected)
...@@ -145,20 +139,12 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe ...@@ -145,20 +139,12 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
} }
} }
this.state = state this.state = state
if (state == BaseService.CONNECTED) { if (state != BaseService.CONNECTED) {
fab.backgroundTintList = greenTint
TooltipCompat.setTooltipText(fab, getString(R.string.stop))
} else {
fab.backgroundTintList = greyTint
TooltipCompat.setTooltipText(fab, getString(R.string.connect))
updateTraffic(-1, 0, 0, 0, 0) updateTraffic(-1, 0, 0, 0, 0)
testCount += 1 // suppress previous test messages testCount += 1 // suppress previous test messages
} }
ProfilesFragment.instance?.profilesAdapter?.notifyDataSetChanged() // refresh button enabled state ProfilesFragment.instance?.profilesAdapter?.notifyDataSetChanged() // refresh button enabled state
stateListener?.invoke(state) stateListener?.invoke(state)
fab.isEnabled = false
if (state == BaseService.CONNECTED || state == BaseService.STOPPED) app.handler.postDelayed(
{ fab.isEnabled = state == BaseService.CONNECTED || state == BaseService.STOPPED }, 1000)
} }
fun updateTraffic(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { fun updateTraffic(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
txText.text = TrafficMonitor.formatTraffic(txTotal) txText.text = TrafficMonitor.formatTraffic(txTotal)
......
...@@ -20,19 +20,38 @@ ...@@ -20,19 +20,38 @@
package com.github.shadowsocks.widget package com.github.shadowsocks.widget
import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import android.support.design.widget.FloatingActionButton import android.support.design.widget.FloatingActionButton
import android.support.graphics.drawable.Animatable2Compat import android.support.graphics.drawable.Animatable2Compat
import android.support.graphics.drawable.AnimatedVectorDrawableCompat import android.support.graphics.drawable.AnimatedVectorDrawableCompat
import android.support.v7.widget.TooltipCompat
import android.util.AttributeSet
import android.view.View
import com.github.shadowsocks.App
import com.github.shadowsocks.R import com.github.shadowsocks.R
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import java.util.* import java.util.*
class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.AnimationCallback() { class ServiceButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
FloatingActionButton(context, attrs, defStyleAttr) {
private val callback = object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable) {
super.onAnimationEnd(drawable)
var next = animationQueue.peek() ?: return
if (next.current == drawable) {
animationQueue.pop()
next = animationQueue.peek() ?: return
}
setImageDrawable(next)
next.start()
}
}
private fun createIcon(@DrawableRes resId: Int): AnimatedVectorDrawableCompat { private fun createIcon(@DrawableRes resId: Int): AnimatedVectorDrawableCompat {
val result = AnimatedVectorDrawableCompat.create(fab.context, resId)!! val result = AnimatedVectorDrawableCompat.create(context, resId)!!
result.registerAnimationCallback(this) result.registerAnimationCallback(callback)
return result return result
} }
...@@ -42,6 +61,14 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A ...@@ -42,6 +61,14 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A
private val iconStopping by lazy { createIcon(R.drawable.ic_service_stopping) } private val iconStopping by lazy { createIcon(R.drawable.ic_service_stopping) }
private val animationQueue = ArrayDeque<AnimatedVectorDrawableCompat>() private val animationQueue = ArrayDeque<AnimatedVectorDrawableCompat>()
private var checked = false
override fun onCreateDrawableState(extraSpace: Int): IntArray {
val drawableState = super.onCreateDrawableState(extraSpace + 1)
if (checked) View.mergeDrawableStates(drawableState, intArrayOf(android.R.attr.state_checked))
return drawableState
}
fun changeState(state: Int, animate: Boolean) { fun changeState(state: Int, animate: Boolean) {
when (state) { when (state) {
BaseService.CONNECTING -> changeState(iconConnecting, animate) BaseService.CONNECTING -> changeState(iconConnecting, animate)
...@@ -49,6 +76,17 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A ...@@ -49,6 +76,17 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A
BaseService.STOPPING -> changeState(iconStopping, animate) BaseService.STOPPING -> changeState(iconStopping, animate)
else -> changeState(iconStopped, animate) else -> changeState(iconStopped, animate)
} }
if (state == BaseService.CONNECTED) {
checked = true
TooltipCompat.setTooltipText(this, context.getString(R.string.stop))
} else {
checked = false
TooltipCompat.setTooltipText(this, context.getString(R.string.connect))
}
refreshDrawableState()
isEnabled = false
if (state == BaseService.CONNECTED || state == BaseService.STOPPED) App.app.handler.postDelayed(
{ isEnabled = state == BaseService.CONNECTED || state == BaseService.STOPPED }, 1000)
} }
private fun counters(a: AnimatedVectorDrawableCompat, b: AnimatedVectorDrawableCompat): Boolean = private fun counters(a: AnimatedVectorDrawableCompat, b: AnimatedVectorDrawableCompat): Boolean =
...@@ -62,26 +100,15 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A ...@@ -62,26 +100,15 @@ class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.A
if (animationQueue.size < 2 || !counters(animationQueue.last, icon)) { if (animationQueue.size < 2 || !counters(animationQueue.last, icon)) {
animationQueue.add(icon) animationQueue.add(icon)
if (animationQueue.size == 1) { if (animationQueue.size == 1) {
fab.setImageDrawable(icon) setImageDrawable(icon)
icon.start() icon.start()
} }
} else animationQueue.removeLast() } else animationQueue.removeLast()
} else { } else {
animationQueue.peekFirst()?.stop() animationQueue.peekFirst()?.stop()
animationQueue.clear() animationQueue.clear()
fab.setImageDrawable(icon) setImageDrawable(icon)
icon.stop() icon.stop()
} }
} }
override fun onAnimationEnd(drawable: Drawable) {
super.onAnimationEnd(drawable)
var next = animationQueue.peek() ?: return
if (next.current == drawable) {
animationQueue.pop()
next = animationQueue.peek() ?: return
}
fab.setImageDrawable(next)
next.start()
}
} }
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/material_green_700" android:state_checked="true"/>
<item android:color="@color/material_primary_500"/>
</selector>
...@@ -77,11 +77,11 @@ ...@@ -77,11 +77,11 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginTop="-28dp" android:layout_marginTop="-28dp"
android:elevation="6dp"> android:elevation="6dp">
<android.support.design.widget.FloatingActionButton <com.github.shadowsocks.widget.ServiceButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:backgroundTint="@color/material_primary_500" app:backgroundTint="@color/background_service"
app:fabSize="normal" app:fabSize="normal"
app:pressedTranslationZ="6dp" app:pressedTranslationZ="6dp"
app:srcCompat="@drawable/ic_service_idle"/> app:srcCompat="@drawable/ic_service_idle"/>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment