diff options
| author | Albin <albin@mullvad.net> | 2023-07-27 13:56:12 +0200 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2023-07-27 13:56:12 +0200 |
| commit | 7a1c9dba446651f06b00c80178407b34120cede9 (patch) | |
| tree | 72328e2b0966106e46583ff2b2a539f3b8cf27db /android/lib/common/src/main | |
| parent | 6d57800a24833e53c5cc161d9afb4dffe4381365 (diff) | |
| parent | ea51297f2a3bbac44351147df78ae4f3ff2bc6e8 (diff) | |
| download | mullvadvpn-7a1c9dba446651f06b00c80178407b34120cede9.tar.xz mullvadvpn-7a1c9dba446651f06b00c80178407b34120cede9.zip | |
Merge branch 'move-tile-service-to-its-own-module-droid-225'
Diffstat (limited to 'android/lib/common/src/main')
4 files changed, 134 insertions, 0 deletions
diff --git a/android/lib/common/src/main/AndroidManifest.xml b/android/lib/common/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..d2bb18d6f3 --- /dev/null +++ b/android/lib/common/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> +</manifest> diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt new file mode 100644 index 0000000000..42f0663967 --- /dev/null +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt @@ -0,0 +1,47 @@ +package net.mullvad.mullvadvpn.lib.common.util + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.IBinder +import android.util.Log +import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import net.mullvad.mullvadvpn.model.ServiceResult + +fun <T> SendChannel<T>.safeOffer(element: T): Boolean { + return runCatching { trySend(element).isSuccess }.getOrDefault(false) +} + +fun Context.bindServiceFlow(intent: Intent, flags: Int = 0): Flow<ServiceResult> = callbackFlow { + val connectionCallback = + object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + safeOffer(ServiceResult(binder)) + } + + override fun onServiceDisconnected(className: ComponentName) { + safeOffer(ServiceResult.NOT_CONNECTED) + bindService(intent, this, flags) + } + } + + bindService(intent, connectionCallback, flags) + + awaitClose { + safeOffer(ServiceResult.NOT_CONNECTED) + + Dispatchers.Default.dispatch(EmptyCoroutineContext) { + try { + unbindService(connectionCallback) + } catch (e: IllegalArgumentException) { + Log.e("mullvad", "Cannot unbind as no binding exists.") + } + } + } +} diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt new file mode 100644 index 0000000000..7fc37a752c --- /dev/null +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt @@ -0,0 +1,45 @@ +package net.mullvad.mullvadvpn.lib.common.util + +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ClosedSendChannelException +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.consumeAsFlow + +class DispatchingFlow<T : Any>(private val upstream: Flow<T>) : Flow<T> { + private val subscribers = ConcurrentHashMap<KClass<out T>, SendChannel<T>>() + + fun <V : T> subscribe(variant: KClass<V>, capacity: Int = Channel.CONFLATED): Flow<V> { + val channel = Channel<V>(capacity) + + // This is safe because `collect` will only send to this channel if the instance class is V + @Suppress("UNCHECKED_CAST") + subscribers[variant] = channel as SendChannel<T> + + return channel.consumeAsFlow() + } + + fun <V : T> unsubscribe(variant: KClass<V>) = subscribers.remove(variant) + + @InternalCoroutinesApi + override suspend fun collect(collector: FlowCollector<T>) { + upstream.collect { event -> + try { + subscribers[event::class]?.send(event) + } catch (closedException: ClosedSendChannelException) { + subscribers.remove(event::class) + } + + collector.emit(event) + } + + subscribers.clear() + } +} + +fun <T : Any> Flow<T>.dispatchTo(configureSubscribers: DispatchingFlow<T>.() -> Unit) = + DispatchingFlow(this).also(configureSubscribers) diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt new file mode 100644 index 0000000000..11394ca8dd --- /dev/null +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/SdkUtils.kt @@ -0,0 +1,38 @@ +package net.mullvad.mullvadvpn.lib.common.util + +import android.Manifest +import android.app.PendingIntent +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.Build +import android.service.quicksettings.Tile + +object SdkUtils { + fun getSupportedPendingIntentFlags(): Int { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + } + + fun Context.isNotificationPermissionGranted(): Boolean { + return (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) || + checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + } + + fun Tile.setSubtitleIfSupported(subtitleText: CharSequence) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + this.subtitle = subtitleText + } + } + + fun PackageManager.getInstalledPackagesList(flags: Int = 0): List<PackageInfo> = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getInstalledPackages(PackageManager.PackageInfoFlags.of(flags.toLong())) + } else { + @Suppress("DEPRECATION") getInstalledPackages(flags) + } +} |
