diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9cf3229..e5231bb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + + android:theme="@style/Theme.Yggdrasil" + android:largeHeap="true"> @@ -57,6 +59,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/BootUpReceiver.kt b/app/src/main/java/eu/neilalexander/yggdrasil/BootUpReceiver.kt new file mode 100644 index 0000000..2ca3629 --- /dev/null +++ b/app/src/main/java/eu/neilalexander/yggdrasil/BootUpReceiver.kt @@ -0,0 +1,40 @@ +package eu.neilalexander.yggdrasil + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.net.VpnService +import android.util.Log +import androidx.preference.PreferenceManager + +class BootUpReceiver : BroadcastReceiver() { + + companion object { + const val TAG = "BootUpReceiver" + } + + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_BOOT_COMPLETED) { + Log.w(TAG, "Wrong action: ${intent.action}") + } + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + if (!preferences.getBoolean(PREF_KEY_ENABLED, false)) { + Log.i(TAG, "Yggdrasil disabled, not starting service") + return + } + Log.i(TAG, "Yggdrasil enabled, starting service") + val serviceIntent = Intent(context, PacketTunnelProvider::class.java) + serviceIntent.action = PacketTunnelProvider.ACTION_START + + val vpnIntent = VpnService.prepare(context) + if (vpnIntent != null) { + Log.i(TAG, "Need to ask for VPN permission") + val notification = createPermissionMissingNotification(context) + val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + manager.notify(444, notification) + } else { + context.startService(serviceIntent) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt b/app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt index d95496b..7ef32c4 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/DnsActivity.kt @@ -59,7 +59,7 @@ class DnsActivity : AppCompatActivity() { val builder: AlertDialog.Builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.Theme_MaterialComponents_DayNight_Dialog)) builder.setTitle(getString(R.string.dns_add_server_dialog_title)) builder.setView(view) - builder.setPositiveButton(getString(R.string.add)) { dialog, _ -> + builder.setPositiveButton(getString(R.string.add)) { _, _ -> val server = input.text.toString() if (!servers.contains(server)) { servers.add(server) diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/GlobalApplication.kt b/app/src/main/java/eu/neilalexander/yggdrasil/GlobalApplication.kt index 341cd99..b2f02b4 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/GlobalApplication.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/GlobalApplication.kt @@ -10,6 +10,7 @@ import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat const val PREF_KEY_ENABLED = "enabled" +const val MAIN_CHANNEL_ID = "Yggdrasil Service" class GlobalApplication: Application(), YggStateReceiver.StateReceiver { private lateinit var config: ConfigurationProxy @@ -62,21 +63,7 @@ class GlobalApplication: Application(), YggStateReceiver.StateReceiver { } 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) - } + createNotificationChannels(context) val intent = Intent(context, MainActivity::class.java).apply { this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK @@ -90,11 +77,46 @@ fun createServiceNotification(context: Context, state: State): Notification { else -> context.getText(R.string.tile_disabled) } - return NotificationCompat.Builder(context, channelId) + return NotificationCompat.Builder(context, MAIN_CHANNEL_ID) .setShowWhen(false) .setContentTitle(text) .setSmallIcon(R.drawable.ic_tile_icon) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_MIN) .build() +} + +fun createPermissionMissingNotification(context: Context): Notification { + createNotificationChannels(context) + 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) + + return NotificationCompat.Builder(context, MAIN_CHANNEL_ID) + .setShowWhen(false) + .setContentTitle(context.getText(R.string.app_name)) + .setContentText(context.getText(R.string.permission_notification_text)) + .setSmallIcon(R.drawable.ic_tile_icon) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .build() +} + +private fun createNotificationChannels(context: Context) { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + 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(MAIN_CHANNEL_ID, name, importance).apply { + description = descriptionText + } + // Register the channel with the system + val notificationManager: NotificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } } \ No newline at end of file diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt b/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt index 4923a91..4e7067e 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt @@ -20,7 +20,7 @@ import kotlin.concurrent.thread private const val TAG = "PacketTunnelProvider" const val SERVICE_NOTIFICATION_ID = 1000 -class PacketTunnelProvider: VpnService() { +open class PacketTunnelProvider: VpnService() { companion object { const val STATE_INTENT = "eu.neilalexander.yggdrasil.PacketTunnelProvider.STATE_MESSAGE" @@ -68,9 +68,9 @@ class PacketTunnelProvider: VpnService() { ACTION_CONNECT -> { Log.d(TAG, "Connecting...") if (started.get()) { - connect(); + connect() } else { - start(); + start() } START_STICKY } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e687b53..29f00ea 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -75,4 +75,5 @@ Баффало, США Сервис VPN Главный канал нотификаций сервиса + Нажмите здесь чтобы включить Yggdrasil. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13d0a90..a6d0538 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -75,4 +75,5 @@ Buffalo, US VPN Service Main channel for foreground notification + Tap here to enable Yggdrasil. \ No newline at end of file