From 99a6c4ff3e9726d10c30d8cfc87c02bdc0d28b58 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 24 Nov 2015 18:09:27 +0800 Subject: [PATCH] Extract notification logic --- .../com/github/shadowsocks/BaseService.scala | 9 +-- .../shadowsocks/ShadowsocksNatService.scala | 70 ++----------------- .../shadowsocks/ShadowsocksNotification.scala | 67 ++++++++++++++++++ .../shadowsocks/ShadowsocksVpnService.scala | 35 +--------- 4 files changed, 76 insertions(+), 105 deletions(-) create mode 100644 src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index bbd24e30..a372066e 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -41,14 +41,13 @@ package com.github.shadowsocks import java.util.{Timer, TimerTask} -import android.app.Notification +import android.app.Service import android.content.Context import android.os.{Handler, RemoteCallbackList} -import android.util.Log import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.utils.{State, TrafficMonitor, TrafficMonitorThread} -trait BaseService { +trait BaseService extends Service { @volatile private var state = State.INIT @volatile private var callbackCount = 0 @@ -197,8 +196,4 @@ trait BaseService { state = s }) } - - def initSoundVibrateLights(notification: Notification) { - notification.sound = null - } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 75f7cca0..9f87966a 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -44,13 +44,10 @@ import java.lang.Process import java.net.{Inet6Address, InetAddress} import java.util.Locale -import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} import android.net.{ConnectivityManager, Network} import android.os._ -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat import android.util.{Log, SparseArray} import android.widget.Toast import com.github.shadowsocks.aidl.Config @@ -62,7 +59,7 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class ShadowsocksNatService extends Service with BaseService { +class ShadowsocksNatService extends BaseService { val TAG = "ShadowsocksNatService" @@ -70,7 +67,7 @@ class ShadowsocksNatService extends Service with BaseService { val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j DNAT --to-destination 127.0.0.1:8123" - var lockReceiver: BroadcastReceiver = null + private var notification: ShadowsocksNotification = _ var closeReceiver: BroadcastReceiver = null var connReceiver: BroadcastReceiver = null var apps: Array[ProxiedApp] = null @@ -324,33 +321,6 @@ class ShadowsocksNatService extends Service with BaseService { true } - def notifyForegroundAlert(title: String, info: String, visible: Boolean) { - val openIntent = new Intent(this, classOf[Shadowsocks]) - openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) - val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0) - val closeIntent = new Intent(Action.CLOSE) - val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) - val builder = new NotificationCompat.Builder(this) - - builder - .setWhen(0) - .setColor(ContextCompat.getColor(this, R.color.material_accent_500)) - .setTicker(title) - .setContentTitle(getString(R.string.app_name)) - .setContentText(info) - .setContentIntent(contentIntent) - .setSmallIcon(R.drawable.ic_stat_shadowsocks) - .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), - actionIntent) - - if (visible) - builder.setPriority(NotificationCompat.PRIORITY_DEFAULT) - else - builder.setPriority(NotificationCompat.PRIORITY_MIN) - - startForeground(1, builder.build) - } - def onBind(intent: Intent): IBinder = { Log.d(TAG, "onBind") if (Action.SERVICE == intent.getAction) { @@ -443,30 +413,6 @@ class ShadowsocksNatService extends Service with BaseService { } registerReceiver(closeReceiver, filter) - if (Utils.isLollipopOrAbove) { - val screenFilter = new IntentFilter() - screenFilter.addAction(Intent.ACTION_SCREEN_ON) - screenFilter.addAction(Intent.ACTION_SCREEN_OFF) - screenFilter.addAction(Intent.ACTION_USER_PRESENT) - lockReceiver = (context: Context, intent: Intent) => if (getState == State.CONNECTED) { - val action = intent.getAction - if (action == Intent.ACTION_SCREEN_OFF) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false) - } else if (action == Intent.ACTION_SCREEN_ON) { - val keyGuard = getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] - if (!keyGuard.inKeyguardRestrictedInputMode) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) - } - } else if (action == Intent.ACTION_USER_PRESENT) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) - } - } - registerReceiver(lockReceiver, screenFilter) - } - ShadowsocksApplication.track(TAG, "start") changeState(State.CONNECTING) @@ -507,8 +453,7 @@ class ShadowsocksNatService extends Service with BaseService { // Set DNS flushDns() - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), true) + notification = new ShadowsocksNotification(this, config.profileName, true) changeState(State.CONNECTED) } else { changeState(State.STOPPED, getString(R.string.service_failed)) @@ -531,12 +476,7 @@ class ShadowsocksNatService extends Service with BaseService { closeReceiver = null } - if (Utils.isLollipopOrAbove) { - if (lockReceiver != null) { - unregisterReceiver(lockReceiver) - lockReceiver = null - } - } + notification.destroy() ShadowsocksApplication.track(TAG, "stop") @@ -548,8 +488,6 @@ class ShadowsocksNatService extends Service with BaseService { stopSelf() } - stopForeground(true) - // change the state changeState(State.STOPPED) } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala new file mode 100644 index 00000000..8228f9c9 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -0,0 +1,67 @@ +package com.github.shadowsocks + +import java.util.Locale + +import android.app.{KeyguardManager, PendingIntent} +import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} +import android.support.v4.app.NotificationCompat +import android.support.v4.app.NotificationCompat.BigTextStyle +import android.support.v4.content.ContextCompat +import com.github.shadowsocks.utils.{Action, State, Utils} + +/** + * @author Mygod + */ +class ShadowsocksNotification(private val service: BaseService, profileName: String, visible: Boolean = false) { + private lazy val keyGuard = service.getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] + private var lockReceiver: BroadcastReceiver = _ + + private val builder = new NotificationCompat.Builder(service) + .setWhen(0) + .setColor(ContextCompat.getColor(service, R.color.material_accent_500)) + .setTicker(service.getString(R.string.forward_success)) + .setContentTitle(service.getString(R.string.app_name)) + .setContentText(service.getString(R.string.service_running).formatLocal(Locale.ENGLISH, profileName)) + .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), + PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0)) + private val style = new BigTextStyle(builder) + if (visible && Utils.isLollipopOrAbove) { + val screenFilter = new IntentFilter() + screenFilter.addAction(Intent.ACTION_SCREEN_ON) + screenFilter.addAction(Intent.ACTION_SCREEN_OFF) + screenFilter.addAction(Intent.ACTION_USER_PRESENT) + lockReceiver = (context: Context, intent: Intent) => if (service.getState == State.CONNECTED) { + val action = intent.getAction + if (action == Intent.ACTION_SCREEN_OFF) { + setVisible(false).show() + } else if (action == Intent.ACTION_SCREEN_ON) { + if (!keyGuard.inKeyguardRestrictedInputMode) { + setVisible(true).show() + } + } else if (action == Intent.ACTION_USER_PRESENT) { + setVisible(true).show() + } + } + service.registerReceiver(lockReceiver, screenFilter) + } + setVisible(visible).show() + + def setVisible(visible: Boolean) = { + builder.setPriority(if (visible) NotificationCompat.PRIORITY_DEFAULT else NotificationCompat.PRIORITY_MIN) + this + } + + def notification = style.build() + def show() = service.startForeground(1, notification) + + def destroy() = { + if (lockReceiver != null) { + service.unregisterReceiver(lockReceiver) + lockReceiver = null + } + service.stopForeground(true) + } +} diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 7c07e8e2..cc89cdc9 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -43,13 +43,10 @@ import java.io.File import java.lang.Process import java.util.Locale -import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} import android.net.VpnService import android.os._ -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat import android.util.Log import android.widget.Toast import com.github.shadowsocks.aidl.Config @@ -70,6 +67,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { var conn: ParcelFileDescriptor = null var apps: Array[ProxiedApp] = null var vpnThread: ShadowsocksVpnThread = null + private var notification: ShadowsocksNotification = _ var closeReceiver: BroadcastReceiver = null var sslocalProcess: Process = null @@ -108,32 +106,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { null } - def notifyForegroundAlert(title: String, info: String, visible: Boolean) { - val openIntent = new Intent(this, classOf[Shadowsocks]) - val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0) - val closeIntent = new Intent(Action.CLOSE) - val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) - val builder = new NotificationCompat.Builder(this) - - builder - .setWhen(0) - .setColor(ContextCompat.getColor(this, R.color.material_accent_500)) - .setTicker(title) - .setContentTitle(getString(R.string.app_name)) - .setContentText(info) - .setContentIntent(contentIntent) - .setSmallIcon(R.drawable.ic_stat_shadowsocks) - .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop), - actionIntent) - - if (visible) - builder.setPriority(NotificationCompat.PRIORITY_DEFAULT) - else - builder.setPriority(NotificationCompat.PRIORITY_MIN) - - startForeground(1, builder.build) - } - override def onCreate() { super.onCreate() ConfigUtils.refresh(this) @@ -152,7 +124,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { vpnThread = null } - stopForeground(true) + notification.destroy() // channge the state changeState(State.STOPPING) @@ -275,8 +247,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { } if (resolved && handleConnection) { - notifyForegroundAlert(getString(R.string.forward_success), - getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false) + notification = new ShadowsocksNotification(this, config.profileName) changeState(State.CONNECTED) } else { changeState(State.STOPPED, getString(R.string.service_failed))