From 540b1d371044360ce5cc79d6c1c9716a1f2326f2 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 19 Dec 2017 16:59:57 -0800 Subject: [PATCH] Add some sweet animation to replace old circular progress --- mobile/src/main/AndroidManifest.xml | 4 +- .../com/github/shadowsocks/MainActivity.kt | 23 ++--- .../shadowsocks/bg/ServiceNotification.kt | 2 +- .../com/github/shadowsocks/bg/TileService.kt | 6 +- .../shadowsocks/widget/ServiceButton.kt | 87 ++++++++++++++++++ ...rt_connected.xml => ic_service_active.xml} | 1 + ..._connected.webp => ic_service_active.webp} | Bin ..._connected.webp => ic_service_active.webp} | Bin ..._connected.webp => ic_service_active.webp} | Bin ..._connected.webp => ic_service_active.webp} | Bin ...{ic_start_busy.xml => ic_service_busy.xml} | 1 + .../res/drawable/ic_service_connected.xml | 17 ++++ .../res/drawable/ic_service_connecting.xml | 28 ++++++ .../src/main/res/drawable/ic_service_idle.xml | 18 ++++ .../main/res/drawable/ic_service_stopped.xml | 28 ++++++ .../main/res/drawable/ic_service_stopping.xml | 17 ++++ .../src/main/res/drawable/ic_start_idle.xml | 15 --- mobile/src/main/res/layout/layout_main.xml | 2 +- 18 files changed, 212 insertions(+), 37 deletions(-) create mode 100644 mobile/src/main/java/com/github/shadowsocks/widget/ServiceButton.kt rename mobile/src/main/res/drawable-anydpi-v21/{ic_start_connected.xml => ic_service_active.xml} (93%) rename mobile/src/main/res/drawable-hdpi/{ic_start_connected.webp => ic_service_active.webp} (100%) rename mobile/src/main/res/drawable-mdpi/{ic_start_connected.webp => ic_service_active.webp} (100%) rename mobile/src/main/res/drawable-xhdpi/{ic_start_connected.webp => ic_service_active.webp} (100%) rename mobile/src/main/res/drawable-xxhdpi/{ic_start_connected.webp => ic_service_active.webp} (100%) rename mobile/src/main/res/drawable/{ic_start_busy.xml => ic_service_busy.xml} (94%) create mode 100644 mobile/src/main/res/drawable/ic_service_connected.xml create mode 100644 mobile/src/main/res/drawable/ic_service_connecting.xml create mode 100755 mobile/src/main/res/drawable/ic_service_idle.xml create mode 100644 mobile/src/main/res/drawable/ic_service_stopped.xml create mode 100644 mobile/src/main/res/drawable/ic_service_stopping.xml delete mode 100755 mobile/src/main/res/drawable/ic_start_idle.xml diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 6f06ec37..64276304 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -25,7 +25,7 @@ android:label="@string/app_name" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" - android:banner="@drawable/ic_start_connected"> + android:banner="@drawable/ic_service_active"> @@ -139,7 +139,7 @@ diff --git a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt index 8c8125c8..665910dc 100644 --- a/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt +++ b/mobile/src/main/java/com/github/shadowsocks/MainActivity.kt @@ -57,6 +57,7 @@ import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.responseLength import com.github.shadowsocks.utils.thread +import com.github.shadowsocks.widget.ServiceButton import com.mikepenz.crossfader.Crossfader import com.mikepenz.crossfader.view.CrossFadeSlidingPaneLayout import com.mikepenz.materialdrawer.Drawer @@ -87,6 +88,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe // UI private lateinit var fab: FloatingActionButton + private val fabHelper by lazy { ServiceButton(fab) } internal var crossfader: Crossfader? = null internal lateinit var drawer: Drawer private var previousSelectedDrawer: Long = 0 // it's actually lateinit @@ -116,7 +118,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy { object : IShadowsocksServiceCallback.Stub() { override fun stateChanged(state: Int, profileName: String?, msg: String?) { - app.handler.post { changeState(state, msg) } + app.handler.post { changeState(state, msg, true) } } override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { app.handler.post { updateTraffic(profileId, txRate, rxRate, txTotal, rxTotal) } @@ -127,22 +129,13 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe } } - fun changeState(state: Int, msg: String? = null) { + fun changeState(state: Int, msg: String? = null, animate: Boolean = false) { + fabHelper.changeState(state, animate) when (state) { - BaseService.CONNECTING -> { - fab.setImageResource(R.drawable.ic_start_busy) - statusText.setText(R.string.connecting) - } - BaseService.CONNECTED -> { - fab.setImageResource(R.drawable.ic_start_connected) - statusText.setText(R.string.vpn_connected) - } - BaseService.STOPPING -> { - fab.setImageResource(R.drawable.ic_start_busy) - statusText.setText(R.string.stopping) - } + BaseService.CONNECTING -> statusText.setText(R.string.connecting) + BaseService.CONNECTED -> statusText.setText(R.string.vpn_connected) + BaseService.STOPPING -> statusText.setText(R.string.stopping) else -> { - fab.setImageResource(R.drawable.ic_start_idle) if (msg != null) { Snackbar.make(findViewById(R.id.snackbar), getString(R.string.vpn_error).format(Locale.ENGLISH, msg), Snackbar.LENGTH_LONG).show() diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt b/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt index 3338c115..5d00b6d2 100644 --- a/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt +++ b/mobile/src/main/java/com/github/shadowsocks/bg/ServiceNotification.kt @@ -69,7 +69,7 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam .setContentTitle(profileName) .setContentIntent(PendingIntent.getActivity(service, 0, Intent(service, MainActivity::class.java) .setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0)) - .setSmallIcon(R.drawable.ic_start_connected) + .setSmallIcon(R.drawable.ic_service_active) private val style = NotificationCompat.BigTextStyle(builder) private var isVisible = true diff --git a/mobile/src/main/java/com/github/shadowsocks/bg/TileService.kt b/mobile/src/main/java/com/github/shadowsocks/bg/TileService.kt index 39c04852..939e1432 100644 --- a/mobile/src/main/java/com/github/shadowsocks/bg/TileService.kt +++ b/mobile/src/main/java/com/github/shadowsocks/bg/TileService.kt @@ -33,9 +33,9 @@ import android.service.quicksettings.TileService as BaseTileService @RequiresApi(Build.VERSION_CODES.N) class TileService : BaseTileService(), ShadowsocksConnection.Interface { - private val iconIdle by lazy { Icon.createWithResource(this, R.drawable.ic_start_idle).setTint(0x79ffffff) } - private val iconBusy by lazy { Icon.createWithResource(this, R.drawable.ic_start_busy) } - private val iconConnected by lazy { Icon.createWithResource(this, R.drawable.ic_start_connected) } + private val iconIdle by lazy { Icon.createWithResource(this, R.drawable.ic_service_idle).setTint(0x79ffffff) } + private val iconBusy by lazy { Icon.createWithResource(this, R.drawable.ic_service_busy) } + private val iconConnected by lazy { Icon.createWithResource(this, R.drawable.ic_service_active) } override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy { @RequiresApi(Build.VERSION_CODES.N) diff --git a/mobile/src/main/java/com/github/shadowsocks/widget/ServiceButton.kt b/mobile/src/main/java/com/github/shadowsocks/widget/ServiceButton.kt new file mode 100644 index 00000000..8bdda3cb --- /dev/null +++ b/mobile/src/main/java/com/github/shadowsocks/widget/ServiceButton.kt @@ -0,0 +1,87 @@ +/******************************************************************************* + * * + * Copyright (C) 2017 by Max Lv * + * Copyright (C) 2017 by Mygod Studio * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +package com.github.shadowsocks.widget + +import android.graphics.drawable.Drawable +import android.support.annotation.DrawableRes +import android.support.design.widget.FloatingActionButton +import android.support.graphics.drawable.Animatable2Compat +import android.support.graphics.drawable.AnimatedVectorDrawableCompat +import com.github.shadowsocks.R +import com.github.shadowsocks.bg.BaseService +import java.util.* + +class ServiceButton(private val fab: FloatingActionButton) : Animatable2Compat.AnimationCallback() { + private fun createIcon(@DrawableRes resId: Int): AnimatedVectorDrawableCompat { + val result = AnimatedVectorDrawableCompat.create(fab.context, resId)!! + result.registerAnimationCallback(this) + return result + } + + private val iconStopped by lazy { createIcon(R.drawable.ic_service_stopped) } + private val iconConnecting by lazy { createIcon(R.drawable.ic_service_connecting) } + private val iconConnected by lazy { createIcon(R.drawable.ic_service_connected) } + private val iconStopping by lazy { createIcon(R.drawable.ic_service_stopping) } + private val animationQueue = ArrayDeque() + + fun changeState(state: Int, animate: Boolean) { + when (state) { + BaseService.CONNECTING -> changeState(iconConnecting, animate) + BaseService.CONNECTED -> changeState(iconConnected, animate) + BaseService.STOPPING -> changeState(iconStopping, animate) + else -> changeState(iconStopped, animate) + } + } + + private fun counters(a: AnimatedVectorDrawableCompat, b: AnimatedVectorDrawableCompat): Boolean = + a == iconStopped && b == iconConnecting || + a == iconConnecting && b == iconStopped || + a == iconConnected && b == iconStopping || + a == iconStopping && b == iconConnected + + private fun changeState(icon: AnimatedVectorDrawableCompat, animate: Boolean) { + if (animate) { + if (animationQueue.size < 2 || !counters(animationQueue.last, icon)) { + animationQueue.add(icon) + if (animationQueue.size == 1) { + fab.setImageDrawable(icon) + icon.start() + } + } else animationQueue.removeLast() + } else { + animationQueue.peekFirst()?.stop() + animationQueue.clear() + fab.setImageDrawable(icon) + 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() + } +} diff --git a/mobile/src/main/res/drawable-anydpi-v21/ic_start_connected.xml b/mobile/src/main/res/drawable-anydpi-v21/ic_service_active.xml similarity index 93% rename from mobile/src/main/res/drawable-anydpi-v21/ic_start_connected.xml rename to mobile/src/main/res/drawable-anydpi-v21/ic_service_active.xml index 9b59c9ea..33062676 100755 --- a/mobile/src/main/res/drawable-anydpi-v21/ic_start_connected.xml +++ b/mobile/src/main/res/drawable-anydpi-v21/ic_service_active.xml @@ -5,6 +5,7 @@ android:viewportHeight="24"> diff --git a/mobile/src/main/res/drawable-hdpi/ic_start_connected.webp b/mobile/src/main/res/drawable-hdpi/ic_service_active.webp similarity index 100% rename from mobile/src/main/res/drawable-hdpi/ic_start_connected.webp rename to mobile/src/main/res/drawable-hdpi/ic_service_active.webp diff --git a/mobile/src/main/res/drawable-mdpi/ic_start_connected.webp b/mobile/src/main/res/drawable-mdpi/ic_service_active.webp similarity index 100% rename from mobile/src/main/res/drawable-mdpi/ic_start_connected.webp rename to mobile/src/main/res/drawable-mdpi/ic_service_active.webp diff --git a/mobile/src/main/res/drawable-xhdpi/ic_start_connected.webp b/mobile/src/main/res/drawable-xhdpi/ic_service_active.webp similarity index 100% rename from mobile/src/main/res/drawable-xhdpi/ic_start_connected.webp rename to mobile/src/main/res/drawable-xhdpi/ic_service_active.webp diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_start_connected.webp b/mobile/src/main/res/drawable-xxhdpi/ic_service_active.webp similarity index 100% rename from mobile/src/main/res/drawable-xxhdpi/ic_start_connected.webp rename to mobile/src/main/res/drawable-xxhdpi/ic_service_active.webp diff --git a/mobile/src/main/res/drawable/ic_start_busy.xml b/mobile/src/main/res/drawable/ic_service_busy.xml similarity index 94% rename from mobile/src/main/res/drawable/ic_start_busy.xml rename to mobile/src/main/res/drawable/ic_service_busy.xml index 9ff8f251..910b5350 100755 --- a/mobile/src/main/res/drawable/ic_start_busy.xml +++ b/mobile/src/main/res/drawable/ic_service_busy.xml @@ -5,6 +5,7 @@ android:viewportHeight="24"> diff --git a/mobile/src/main/res/drawable/ic_service_connected.xml b/mobile/src/main/res/drawable/ic_service_connected.xml new file mode 100644 index 00000000..e5a739f3 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_service_connected.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_service_connecting.xml b/mobile/src/main/res/drawable/ic_service_connecting.xml new file mode 100644 index 00000000..45f8f155 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_service_connecting.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_service_idle.xml b/mobile/src/main/res/drawable/ic_service_idle.xml new file mode 100755 index 00000000..205b81d4 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_service_idle.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/mobile/src/main/res/drawable/ic_service_stopped.xml b/mobile/src/main/res/drawable/ic_service_stopped.xml new file mode 100644 index 00000000..3d2e28bc --- /dev/null +++ b/mobile/src/main/res/drawable/ic_service_stopped.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_service_stopping.xml b/mobile/src/main/res/drawable/ic_service_stopping.xml new file mode 100644 index 00000000..8fa85ae2 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_service_stopping.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/mobile/src/main/res/drawable/ic_start_idle.xml b/mobile/src/main/res/drawable/ic_start_idle.xml deleted file mode 100755 index 2239050e..00000000 --- a/mobile/src/main/res/drawable/ic_start_idle.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/mobile/src/main/res/layout/layout_main.xml b/mobile/src/main/res/layout/layout_main.xml index 39d4912f..d3c12937 100644 --- a/mobile/src/main/res/layout/layout_main.xml +++ b/mobile/src/main/res/layout/layout_main.xml @@ -84,7 +84,7 @@ app:backgroundTint="@color/material_primary_500" app:fabSize="normal" app:pressedTranslationZ="6dp" - app:srcCompat="@drawable/ic_start_idle"/> + app:srcCompat="@drawable/ic_service_idle"/>