Extract notification logic
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user