mirror of
https://github.com/dovecoteescapee/ByeDPIAndroid.git
synced 2024-12-22 06:15:44 +00:00
Add support for Quick Tile
This commit is contained in:
parent
48f5edd337
commit
d43e991ae8
@ -23,6 +23,7 @@
|
|||||||
android:launchMode="singleInstance">
|
android:launchMode="singleInstance">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -55,6 +56,18 @@
|
|||||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
android:value="proxy" />
|
android:value="proxy" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".services.QuickTileService"
|
||||||
|
android:icon="@drawable/ic_notification"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
|
<intent-filter>
|
||||||
|
<action
|
||||||
|
android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -15,8 +15,6 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
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.R
|
||||||
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
||||||
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
|
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.data.Sender
|
||||||
import io.github.dovecoteescapee.byedpi.fragments.SettingsFragment
|
import io.github.dovecoteescapee.byedpi.fragments.SettingsFragment
|
||||||
import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding
|
import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding
|
||||||
import io.github.dovecoteescapee.byedpi.services.ByeDpiProxyService
|
import io.github.dovecoteescapee.byedpi.services.ServiceManager
|
||||||
import io.github.dovecoteescapee.byedpi.services.ByeDpiVpnService
|
|
||||||
import io.github.dovecoteescapee.byedpi.services.appStatus
|
import io.github.dovecoteescapee.byedpi.services.appStatus
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
||||||
import io.github.dovecoteescapee.byedpi.utility.mode
|
import io.github.dovecoteescapee.byedpi.utility.mode
|
||||||
@ -57,7 +54,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private val vpnRegister =
|
private val vpnRegister =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
if (it.resultCode == RESULT_OK) {
|
if (it.resultCode == RESULT_OK) {
|
||||||
startVpn()
|
ServiceManager.start(this, Mode.VPN)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
|
||||||
updateStatus()
|
updateStatus()
|
||||||
@ -212,48 +209,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (intentPrepare != null) {
|
if (intentPrepare != null) {
|
||||||
vpnRegister.launch(intentPrepare)
|
vpnRegister.launch(intentPrepare)
|
||||||
} else {
|
} 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() {
|
private fun stop() {
|
||||||
val (_, mode) = appStatus
|
ServiceManager.stop(this)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStatus() {
|
private fun updateStatus() {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ fun createConnectionNotification(
|
|||||||
service: Class<*>,
|
service: Class<*>,
|
||||||
): Notification =
|
): Notification =
|
||||||
NotificationCompat.Builder(context, channelId)
|
NotificationCompat.Builder(context, channelId)
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setContentTitle(context.getString(title))
|
.setContentTitle(context.getString(title))
|
||||||
.setContentText(context.getString(content))
|
.setContentText(context.getString(content))
|
||||||
|
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 820 B |
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 486 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
Loading…
Reference in New Issue
Block a user