From d43e991ae84196daa29741c8da98ff7eb947543f Mon Sep 17 00:00:00 2001
From: krlvm <51774833+krlvm@users.noreply.github.com>
Date: Sat, 27 Jul 2024 18:09:45 +0300
Subject: [PATCH] Add support for Quick Tile
---
app/src/main/AndroidManifest.xml | 13 ++
.../byedpi/activities/MainActivity.kt | 45 +-----
.../byedpi/services/QuickTileService.kt | 133 ++++++++++++++++++
.../byedpi/services/ServiceManager.kt | 49 +++++++
.../byedpi/utility/NotificationUtils.kt | 2 +-
.../res/drawable-hdpi/ic_notification.png | Bin 0 -> 820 bytes
.../res/drawable-mdpi/ic_notification.png | Bin 0 -> 486 bytes
.../res/drawable-xhdpi/ic_notification.png | Bin 0 -> 1158 bytes
.../res/drawable-xxhdpi/ic_notification.png | Bin 0 -> 1908 bytes
.../res/drawable-xxxhdpi/ic_notification.png | Bin 0 -> 2773 bytes
10 files changed, 201 insertions(+), 41 deletions(-)
create mode 100644 app/src/main/java/io/github/dovecoteescapee/byedpi/services/QuickTileService.kt
create mode 100644 app/src/main/java/io/github/dovecoteescapee/byedpi/services/ServiceManager.kt
create mode 100644 app/src/main/res/drawable-hdpi/ic_notification.png
create mode 100644 app/src/main/res/drawable-mdpi/ic_notification.png
create mode 100644 app/src/main/res/drawable-xhdpi/ic_notification.png
create mode 100644 app/src/main/res/drawable-xxhdpi/ic_notification.png
create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_notification.png
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 95bf82d..e593f5c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:launchMode="singleInstance">
+
@@ -55,6 +56,18 @@
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="proxy" />
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/activities/MainActivity.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/activities/MainActivity.kt
index c115b1d..58b1cd1 100644
--- a/app/src/main/java/io/github/dovecoteescapee/byedpi/activities/MainActivity.kt
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/activities/MainActivity.kt
@@ -15,8 +15,6 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
-import io.github.dovecoteescapee.byedpi.data.START_ACTION
-import io.github.dovecoteescapee.byedpi.data.STOP_ACTION
import io.github.dovecoteescapee.byedpi.R
import io.github.dovecoteescapee.byedpi.data.AppStatus
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
@@ -27,8 +25,7 @@ import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
import io.github.dovecoteescapee.byedpi.data.Sender
import io.github.dovecoteescapee.byedpi.fragments.SettingsFragment
import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding
-import io.github.dovecoteescapee.byedpi.services.ByeDpiProxyService
-import io.github.dovecoteescapee.byedpi.services.ByeDpiVpnService
+import io.github.dovecoteescapee.byedpi.services.ServiceManager
import io.github.dovecoteescapee.byedpi.services.appStatus
import io.github.dovecoteescapee.byedpi.utility.getPreferences
import io.github.dovecoteescapee.byedpi.utility.mode
@@ -57,7 +54,7 @@ class MainActivity : AppCompatActivity() {
private val vpnRegister =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
- startVpn()
+ ServiceManager.start(this, Mode.VPN)
} else {
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
updateStatus()
@@ -212,48 +209,16 @@ class MainActivity : AppCompatActivity() {
if (intentPrepare != null) {
vpnRegister.launch(intentPrepare)
} else {
- startVpn()
+ ServiceManager.start(this, Mode.VPN)
}
}
- Mode.Proxy -> startProxy()
+ Mode.Proxy -> ServiceManager.start(this, Mode.Proxy)
}
}
- private fun startVpn() {
- Log.i(TAG, "Starting VPN")
- val intent = Intent(this, ByeDpiVpnService::class.java)
- intent.action = START_ACTION
- startService(intent)
- }
-
- private fun startProxy() {
- Log.i(TAG, "Starting proxy")
- val intent = Intent(this, ByeDpiProxyService::class.java)
- intent.action = START_ACTION
- startService(intent)
- }
-
private fun stop() {
- val (_, mode) = appStatus
- when (mode) {
- Mode.VPN -> stopVpn()
- Mode.Proxy -> stopProxy()
- }
- }
-
- private fun stopVpn() {
- Log.i(TAG, "Stopping VPN")
- val intent = Intent(this, ByeDpiVpnService::class.java)
- intent.action = STOP_ACTION
- startService(intent)
- }
-
- private fun stopProxy() {
- Log.i(TAG, "Stopping proxy")
- val intent = Intent(this, ByeDpiProxyService::class.java)
- intent.action = STOP_ACTION
- startService(intent)
+ ServiceManager.stop(this)
}
private fun updateStatus() {
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/QuickTileService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/QuickTileService.kt
new file mode 100644
index 0000000..3647d6b
--- /dev/null
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/QuickTileService.kt
@@ -0,0 +1,133 @@
+package io.github.dovecoteescapee.byedpi.services
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.VpnService
+import android.os.Build
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
+import androidx.core.service.quicksettings.PendingIntentActivityWrapper
+import androidx.core.service.quicksettings.TileServiceCompat
+import io.github.dovecoteescapee.byedpi.R
+import io.github.dovecoteescapee.byedpi.activities.MainActivity
+import io.github.dovecoteescapee.byedpi.data.AppStatus
+import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
+import io.github.dovecoteescapee.byedpi.data.Mode
+import io.github.dovecoteescapee.byedpi.data.SENDER
+import io.github.dovecoteescapee.byedpi.data.STARTED_BROADCAST
+import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
+import io.github.dovecoteescapee.byedpi.data.Sender
+import io.github.dovecoteescapee.byedpi.utility.getPreferences
+import io.github.dovecoteescapee.byedpi.utility.mode
+
+
+@RequiresApi(Build.VERSION_CODES.N)
+class QuickTileService : TileService() {
+
+ companion object {
+ private val TAG: String = QuickTileService::class.java.simpleName
+ }
+
+ private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val senderOrd = intent.getIntExtra(SENDER, -1)
+ val sender = Sender.entries.getOrNull(senderOrd)
+ if (sender == null) {
+ Log.w(TAG, "Received intent with unknown sender: $senderOrd")
+ return
+ }
+
+ when (val action = intent.action) {
+ STARTED_BROADCAST,
+ STOPPED_BROADCAST -> updateStatus()
+
+ FAILED_BROADCAST -> {
+ Toast.makeText(
+ context,
+ getString(R.string.failed_to_start, sender.name),
+ Toast.LENGTH_SHORT,
+ ).show()
+ updateStatus()
+ }
+
+ else -> Log.w(TAG, "Unknown action: $action")
+ }
+ }
+ }
+
+ override fun onStartListening() {
+ updateStatus()
+ ContextCompat.registerReceiver(
+ this,
+ receiver,
+ IntentFilter().apply {
+ addAction(STARTED_BROADCAST)
+ addAction(STOPPED_BROADCAST)
+ addAction(FAILED_BROADCAST)
+ },
+ ContextCompat.RECEIVER_EXPORTED,
+ )
+ }
+
+ override fun onStopListening() {
+ unregisterReceiver(receiver)
+ }
+
+ private fun launchActivity() {
+ TileServiceCompat.startActivityAndCollapse(
+ this, PendingIntentActivityWrapper(
+ this, 0, Intent(this, MainActivity::class.java),
+ PendingIntent.FLAG_UPDATE_CURRENT, false
+ )
+ )
+ }
+
+ override fun onClick() {
+ if (qsTile.state == Tile.STATE_UNAVAILABLE) {
+ return
+ }
+
+ unlockAndRun(this::handleClick)
+ }
+
+ private fun setState(newState: Int) {
+ qsTile.apply {
+ state = newState
+ updateTile()
+ }
+ }
+
+ private fun updateStatus() {
+ val (status) = appStatus
+ setState(if (status == AppStatus.Halted) Tile.STATE_INACTIVE else Tile.STATE_ACTIVE)
+ }
+
+ private fun handleClick() {
+ setState(Tile.STATE_ACTIVE)
+ setState(Tile.STATE_UNAVAILABLE)
+
+ val (status) = appStatus
+ when (status) {
+ AppStatus.Halted -> {
+ val mode = getPreferences(this).mode()
+
+ if (mode == Mode.VPN && VpnService.prepare(this) != null) {
+ updateStatus()
+ launchActivity()
+ return
+ }
+
+ ServiceManager.start(this, mode)
+ }
+
+ AppStatus.Running -> ServiceManager.stop(this)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ServiceManager.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ServiceManager.kt
new file mode 100644
index 0000000..0e82742
--- /dev/null
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ServiceManager.kt
@@ -0,0 +1,49 @@
+package io.github.dovecoteescapee.byedpi.services
+
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import io.github.dovecoteescapee.byedpi.data.Mode
+import io.github.dovecoteescapee.byedpi.data.START_ACTION
+import io.github.dovecoteescapee.byedpi.data.STOP_ACTION
+
+object ServiceManager {
+ private val TAG: String = ServiceManager::class.java.simpleName
+
+ fun start(context: Context, mode: Mode) {
+ when (mode) {
+ Mode.VPN -> {
+ Log.i(TAG, "Starting VPN")
+ val intent = Intent(context, ByeDpiVpnService::class.java)
+ intent.action = START_ACTION
+ context.startService(intent)
+ }
+
+ Mode.Proxy -> {
+ Log.i(TAG, "Starting proxy")
+ val intent = Intent(context, ByeDpiProxyService::class.java)
+ intent.action = START_ACTION
+ context.startService(intent)
+ }
+ }
+ }
+
+ fun stop(context: Context) {
+ val (_, mode) = appStatus
+ when (mode) {
+ Mode.VPN -> {
+ Log.i(TAG, "Stopping VPN")
+ val intent = Intent(context, ByeDpiVpnService::class.java)
+ intent.action = STOP_ACTION
+ context.startService(intent)
+ }
+
+ Mode.Proxy -> {
+ Log.i(TAG, "Stopping proxy")
+ val intent = Intent(context, ByeDpiProxyService::class.java)
+ intent.action = STOP_ACTION
+ context.startService(intent)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt
index e7850a7..4ec01ae 100644
--- a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt
@@ -38,7 +38,7 @@ fun createConnectionNotification(
service: Class<*>,
): Notification =
NotificationCompat.Builder(context, channelId)
- .setSmallIcon(R.mipmap.ic_launcher)
+ .setSmallIcon(R.drawable.ic_notification)
.setSilent(true)
.setContentTitle(context.getString(title))
.setContentText(context.getString(content))
diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ce6f29c22efaf4187c56d52075726c03bbf3b07
GIT binary patch
literal 820
zcmV-41Izr0P)HID2WG($mIbE4>EWp
z5}_nX7?)gf%XRtItp7OMYM;xT&K#xr>Z^0k{?}Un{`dYbr>Ll#Zc-y6v+*|8`D_0K
zI&wvfCV#Eq(1`)~4S!&Rzg}?Y#JVs#XR!{8eVc+o@oIdAUvUP$$2q=T!JzmkJcaAA
z%?3)Q`gSRy@kU&O<^Eb{Ax9++7aJvgRCn<*mio3Sq4C3T7aF_>t9+YmAZ{GKzz_Hi
zSKuHVjT3$Ql+bt~{_N#^TEGd4sQ+6=Ks>XT?mh&2cGy3`ye{BX%QkoW?4=gnfLitRQd*-WQv`y29#e
zn+{zB_xc|L-*GUO`8ruca8EprZ-ck+pxBEZT^bafN%dkAAQdgXMox&mko|Bu9>Q0l
zfObM(e&vqX4d_UwVzbYkGm?BEmCv{fC*Yg#o9oUalJblR%=vt+x13hl^_wBnBv1zWGYm|G~h|RbYYkgdoL9y+B
zbtRq)l84i0Mkq2_Y$_|diYw98`$pvRHXol?C@>IThGK1!JeufYsYh02K0XPb_0&+~
z^W+VZ?+%4@M8=1LiXJ@@XY!*sOKfe@ZKcBJ$s1zRTnp_JJ7*`*$C&ub{1}UIq~!QV
yp;J&~sMvPWAojjo6+77JezqSQeM~q19lrsb%Hx81LX5@$0000@P)NQmMjmczP9N%;p{gc6~&hjs8HNlBC(*iwpv6H4TwkaBPk
z3K7M4#>|`9?`~l^$x~0;JMU*cZ_PXJY(m067Nt}ruE_CUgYqfdk>kGt#i@9My;v;o
z{}@W^aSg*5l=mYbuz(HdllNjlt`Ix102eV^W+EW4qznhxh2@woa~j5T@E!;83Ue_m
za}f|o!XdnPe~jS;&v6;8*B*_s=Tn$8rp!|4r3%`j0rj!lT$<(lPhnCPR$wb$;~kbNvvXV?FjLO|
zGE8n)W|!!%b~d8gY~v0&CoU+evV~spHrC2qTqtv>dEbRzBK@!?OqSWWplHUHdwAc$
zGA7FWx1gZKmaW@gy)^p#nYY(|IpO|W7~Gy;s9={pZ*UhvsH_57`K!;D)gC^
cE35ErM)+DK;QmFSNJpZCHkK`9%%2694urxT|#4Y5IyY*e-oKj
zB_-vdm0L3_NSlH8hVn>rp;+TT1A`M
zG@)VQJ+;M;bAc|SJ!xZmf4VTn0D8;CK0rTr|98?SXbJS;HrlkIV8U*QH!L3+Xyoh2
z8=6Z;(5|$(jgbiGhtxjLiOE!IInD`lb)C-)5`}B8uh<-$e&?Yus
z0Iv=A6O-HYANT)%dKUTx#?sw3eOf?2=NRaxmG%$}^{J?)?QA@O=Qo>s1CG-U(6=v2
z_uAQ}PYVW^Ll;B4x3=_I=qF!J@789U7-^V~^kO{srM=N1jaaJJd-Qc$Zqud>gM8;@
zAdT)@Xx%&rtvacY23psH@qb5p$J`SjFQo}R?2hlzp?-PNh5=rqmxCNKq!O-8@OO>9
z=0ant6=TpZk_suzWZIJcUH{S}jE6piG~}OX8(W@?
zVX$_zH?%!SPapDpJgUvzRf#pKu28}eMiy0(0*H{zZFPaJ>;!+
zhfWZ~u+`?9b(l$e=zJ*{K?9^coS?sW=VXpA^?IFmE*aZwepv@Pp#AG*Dr185Os_(}
zj6aW>37r~T^Ufus!seF~pcAkSk|BMOmePfY&oTvB=eDNrdgqcMFJgerE9WrNE_i$+
zo&MW=ukS5ZgR(EHby~Vfj)?rHxx1CVB01mpi}KcD=r_IANg!Xq1ysn@i
z=(2tY*bm*F36KYr1^&n0xwSTK-hl3=1zdqHKV_pUUisFn@K{y%2&`ap_4oJHeE{l#8VY1QX~@|
Y0&aZ{zBbD<$^ZZW07*qoM6N<$g3~c1u>b%7
literal 0
HcmV?d00001
diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbb4a71244070666abaf60e654677b89224aa5cb
GIT binary patch
literal 1908
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
bnMLORR?`Ff$?Wn}00000NkvXXu0mjf!*qez
literal 0
HcmV?d00001
|