Skip to content

Commit

Permalink
Fixes yggdrasil-network#30, added quick settings icon. (yggdrasil-net…
Browse files Browse the repository at this point in the history
…work#32)

* Fixes yggdrasil-network#30, added quick settings icon.
* Added saving of enabled state, added some fixes and refactorings.
* Fixed a bug with turning on/off the VPN.
* Fixed possibility to add duplicate servers in DNS settings.
  • Loading branch information
Revertron authored Nov 19, 2022
1 parent ee81f4e commit aa94cca
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 58 deletions.
31 changes: 27 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<application
android:name=".GlobalApplication"
Expand All @@ -13,18 +14,28 @@
android:roundIcon="@drawable/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Yggdrasil">
<activity android:name=".SettingsActivity"
android:parentActivityName=".MainActivity" />
<activity android:name=".PeersActivity"
android:parentActivityName=".MainActivity" />
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SettingsActivity" android:parentActivityName=".MainActivity" />
<activity android:name=".PeersActivity" android:parentActivityName=".MainActivity" />
<activity android:name=".DnsActivity" android:exported="false" />
<activity android:name=".TileServiceActivity" android:theme="@android:style/Theme.NoDisplay"
android:allowTaskReparenting="true"
android:alwaysRetainTaskState="false"
android:clearTaskOnLaunch="true"
android:enabled="true"
android:exported="true"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
</activity>

<service
android:name=".PacketTunnelProvider"
Expand All @@ -34,6 +45,18 @@
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service
android:name=".YggTileService"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:icon="@drawable/ic_tile_icon"
android:label="@string/app_name"
android:exported="true">
<meta-data android:name="android.service.quicksettings.ACTIVE_TILE" android:value="true" />
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" />
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
</application>

</manifest>
34 changes: 22 additions & 12 deletions app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
import android.widget.*
import androidx.preference.PreferenceManager
import com.google.android.material.textfield.TextInputEditText

const val KEY_DNS_SERVERS = "dns_servers"
Expand Down Expand Up @@ -59,13 +60,17 @@ class DnsActivity : AppCompatActivity() {
builder.setTitle(getString(R.string.dns_add_server_dialog_title))
builder.setView(view)
builder.setPositiveButton(getString(R.string.add)) { dialog, _ ->
servers.add(input.text.toString())
preferences.edit().apply {
putString(KEY_DNS_SERVERS, servers.joinToString(","))
commit()
val server = input.text.toString()
if (!servers.contains(server)) {
servers.add(server)
preferences.edit().apply {
putString(KEY_DNS_SERVERS, servers.joinToString(","))
commit()
}
updateConfiguredServers()
} else {
Toast.makeText(this, R.string.dns_already_added_server, Toast.LENGTH_SHORT).show()
}
dialog.dismiss()
updateConfiguredServers()
}
builder.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
dialog.cancel()
Expand All @@ -85,7 +90,7 @@ class DnsActivity : AppCompatActivity() {
enableChromeFix.toggle()
}

preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext)
preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext)
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
servers = if (serverString!!.isNotEmpty()) {
serverString.split(",").toMutableList()
Expand Down Expand Up @@ -157,12 +162,17 @@ class DnsActivity : AppCompatActivity() {
addButton.tag = server

addButton.setOnClickListener { button ->
servers.add(button.tag as String)
preferences.edit().apply {
this.putString(KEY_DNS_SERVERS, servers.joinToString(","))
this.commit()
val serverString = button.tag as String
if (!servers.contains(serverString)) {
servers.add(serverString)
preferences.edit().apply {
this.putString(KEY_DNS_SERVERS, servers.joinToString(","))
this.commit()
}
updateConfiguredServers()
} else {
Toast.makeText(this, R.string.dns_already_added_server, Toast.LENGTH_SHORT).show()
}
updateConfiguredServers()
}
view.setOnLongClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
Expand Down
76 changes: 74 additions & 2 deletions app/src/main/java/eu/neilalexander/yggdrasil/GlobalApplication.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package eu.neilalexander.yggdrasil

import android.app.Application
import android.app.*
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.service.quicksettings.TileService
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat

class GlobalApplication: Application() {
const val PREF_KEY_ENABLED = "enabled"

class GlobalApplication: Application(), YggStateReceiver.StateReceiver {
private lateinit var config: ConfigurationProxy
private var currentState: State = State.Disabled
var updaterConnections: Int = 0

override fun onCreate() {
super.onCreate()
config = ConfigurationProxy(applicationContext)
val callback = NetworkStateCallback(this)
callback.register()
val receiver = YggStateReceiver(this)
receiver.register(this)
}

fun subscribe() {
Expand All @@ -25,4 +38,63 @@ class GlobalApplication: Application() {
fun needUiUpdates(): Boolean {
return updaterConnections > 0
}

fun getCurrentState(): State {
return currentState
}

@RequiresApi(Build.VERSION_CODES.N)
override fun onStateChange(state: State) {
if (state != currentState) {
val componentName = ComponentName(this, YggTileService::class.java)
TileService.requestListeningState(this, componentName)

if (state != State.Disabled) {
val notification = createServiceNotification(this, state)
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(SERVICE_NOTIFICATION_ID, notification)
}

currentState = state
}
}
}

fun createServiceNotification(context: Context, state: State): Notification {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
val channelId = "Foreground Service"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = context.getString(R.string.channel_name)
val descriptionText = context.getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_MIN
val channel = NotificationChannel(channelId, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}

val intent = Intent(context, MainActivity::class.java).apply {
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

val text = when (state) {
State.Disabled -> context.getText(R.string.tile_disabled)
State.Enabled -> context.getText(R.string.tile_enabled)
State.Connected -> context.getText(R.string.tile_connected)
else -> context.getText(R.string.tile_disabled)
}

return NotificationCompat.Builder(context, channelId)
.setContentTitle(context.getText(R.string.app_name))
.setContentText(text)
.setSmallIcon(R.drawable.ic_tile_icon)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MIN)
.build()
}
9 changes: 6 additions & 3 deletions app/src/main/java/eu/neilalexander/yggdrasil/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
import eu.neilalexander.yggdrasil.PacketTunnelProvider.Companion.STATE_INTENT
import mobile.Mobile
import org.json.JSONArray
Expand Down Expand Up @@ -76,6 +78,8 @@ class MainActivity : AppCompatActivity() {
startService(intent)
}
}
val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext)
preferences.edit(commit = true) { putBoolean(PREF_KEY_ENABLED, isChecked) }
}

val enableYggdrasilPanel = findViewById<TableRow>(R.id.enableYggdrasilPanel)
Expand Down Expand Up @@ -123,7 +127,8 @@ class MainActivity : AppCompatActivity() {
LocalBroadcastManager.getInstance(this).registerReceiver(
receiver, IntentFilter(STATE_INTENT)
)
val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext)
val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext)
enabledSwitch.isChecked = preferences.getBoolean(PREF_KEY_ENABLED, false)
val serverString = preferences.getString(KEY_DNS_SERVERS, "")
if (serverString!!.isNotEmpty()) {
val servers = serverString.split(",")
Expand All @@ -149,7 +154,6 @@ class MainActivity : AppCompatActivity() {
when (intent.getStringExtra("type")) {
"state" -> {
enabledLabel.text = if (intent.getBooleanExtra("started", false)) {
enabledSwitch.isChecked = true
var count = 0
if (intent.hasExtra("dht")) {
val dht = intent.getStringExtra("dht")
Expand All @@ -166,7 +170,6 @@ class MainActivity : AppCompatActivity() {
getString(R.string.main_enabled)
}
} else {
enabledSwitch.isChecked = false
enabledLabel.setTextColor(Color.GRAY)
getString(R.string.main_disabled)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,50 @@ package eu.neilalexander.yggdrasil
import android.content.Context
import android.content.Intent
import android.net.*
import android.os.Build
import android.util.Log
import androidx.preference.PreferenceManager


private const val TAG = "Network"

class NetworkStateCallback(val context: Context) : ConnectivityManager.NetworkCallback() {

init {
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.build()

val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
manager.registerNetworkCallback(request, this)
}

override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.d(TAG, "onAvailable")

Thread {
// The message often arrives before the connection is fully established
Thread.sleep(1000)
val intent = Intent(context, PacketTunnelProvider::class.java)
intent.action = PacketTunnelProvider.ACTION_CONNECT
context.startService(intent)
}.start()
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
if (preferences.getBoolean(PREF_KEY_ENABLED, false)) {
Thread {
// The message often arrives before the connection is fully established
Thread.sleep(1000)
val intent = Intent(context, PacketTunnelProvider::class.java)
intent.action = PacketTunnelProvider.ACTION_CONNECT
try {
context.startService(intent)
} catch (e: IllegalStateException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
}
}
}.start()
}
}

override fun onLost(network: Network) {
super.onLost(network)
Log.d(TAG, "onLost")
}

fun register() {
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.build()

val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
manager.registerNetworkCallback(request, this)
}
}
Loading

0 comments on commit aa94cca

Please sign in to comment.