diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a7394e5..49ec10c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -161,6 +161,10 @@ android {
}
}
+ lint {
+ fatal += "NewApi"
+ }
+
applicationVariants.configureEach {
outputs.configureEach {
val output = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
@@ -314,6 +318,9 @@ dependencies {
// Debug/Test dependencies
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
+ "androidTestPlayImplementation"(composeBom23)
+ "androidTestOtherImplementation"(composeBom23)
+ "androidTestOtherLegacyImplementation"(composeBom21)
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
// Common Compose-related libraries
diff --git a/app/lint.xml b/app/lint.xml
new file mode 100644
index 0000000..d4af354
--- /dev/null
+++ b/app/lint.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/io/nekohasekai/sfa/bg/RootClient.kt b/app/src/main/java/io/nekohasekai/sfa/bg/RootClient.kt
index a7b24b9..bd372ad 100644
--- a/app/src/main/java/io/nekohasekai/sfa/bg/RootClient.kt
+++ b/app/src/main/java/io/nekohasekai/sfa/bg/RootClient.kt
@@ -4,6 +4,7 @@ import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageInfo
+import android.os.Build
import android.os.IBinder
import android.os.RemoteException
import androidx.core.content.ContextCompat
@@ -130,7 +131,13 @@ object RootClient {
val list = slice.list as List
list
} catch (e: RemoteException) {
- throw e.rethrowFromSystemServer()
+ throw e.rethrowAsRuntime()
}
}
+
+ private fun RemoteException.rethrowAsRuntime(): RuntimeException = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ rethrowFromSystemServer()
+ } else {
+ RuntimeException(this)
+ }
}
diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt
index 9c94f63..352efdd 100644
--- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt
+++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt
@@ -79,6 +79,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import androidx.core.app.NotificationManagerCompat
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
@@ -195,7 +196,7 @@ fun AppSettingsScreen(navController: NavController) {
val channel = Application.notification.getNotificationChannel("service")
notificationEnabled = channel?.importance != NotificationManager.IMPORTANCE_NONE
} else {
- notificationEnabled = Application.notification.areNotificationsEnabled()
+ notificationEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
}
if (silentInstallEnabled) {
scope.launch {
diff --git a/app/src/main/java/io/nekohasekai/sfa/qrs/QRSDecoder.kt b/app/src/main/java/io/nekohasekai/sfa/qrs/QRSDecoder.kt
index c33c8bb..29008b5 100644
--- a/app/src/main/java/io/nekohasekai/sfa/qrs/QRSDecoder.kt
+++ b/app/src/main/java/io/nekohasekai/sfa/qrs/QRSDecoder.kt
@@ -1,7 +1,7 @@
package io.nekohasekai.sfa.qrs
+import android.util.Base64
import java.io.ByteArrayOutputStream
-import java.util.Base64
import java.util.zip.Inflater
class QRSDecoder {
@@ -44,7 +44,7 @@ class QRSDecoder {
@Synchronized
fun processFrame(base64Content: String): DecodeProgress? {
val payload = try {
- Base64.getDecoder().decode(base64Content)
+ Base64.decode(base64Content, Base64.NO_WRAP)
} catch (e: Exception) {
return null
}
diff --git a/app/src/main/java/io/nekohasekai/sfa/qrs/QRSEncoder.kt b/app/src/main/java/io/nekohasekai/sfa/qrs/QRSEncoder.kt
index bd19bca..f15edf7 100644
--- a/app/src/main/java/io/nekohasekai/sfa/qrs/QRSEncoder.kt
+++ b/app/src/main/java/io/nekohasekai/sfa/qrs/QRSEncoder.kt
@@ -1,7 +1,7 @@
package io.nekohasekai.sfa.qrs
+import android.util.Base64
import java.io.ByteArrayOutputStream
-import java.util.Base64
import java.util.zip.Deflater
class QRSEncoder(private val sliceSize: Int = QRSConstants.DEFAULT_SLICE_SIZE) {
@@ -57,7 +57,7 @@ class QRSEncoder(private val sliceSize: Int = QRSConstants.DEFAULT_SLICE_SIZE) {
return codec.encode(data, compressed, compressed.size).mapIndexed { index, block ->
val payload = buildPayload(block)
- val base64 = Base64.getEncoder().encodeToString(payload)
+ val base64 = Base64.encodeToString(payload, Base64.NO_WRAP)
QRSFrame("$urlPrefix$base64", index, block.totalBlocks)
}
}
diff --git a/app/src/main/java/io/nekohasekai/sfa/utils/VpnDetectionTest.kt b/app/src/main/java/io/nekohasekai/sfa/utils/VpnDetectionTest.kt
index f210ed1..bb974ac 100644
--- a/app/src/main/java/io/nekohasekai/sfa/utils/VpnDetectionTest.kt
+++ b/app/src/main/java/io/nekohasekai/sfa/utils/VpnDetectionTest.kt
@@ -2,8 +2,10 @@ package io.nekohasekai.sfa.utils
import android.content.Context
import android.net.ConnectivityManager
+import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
+import android.os.Build
import java.net.NetworkInterface
import java.util.Collections
@@ -67,7 +69,7 @@ object VpnDetectionTest {
}
// Check activeLinkProperties interface
- val activeLinkProperties = runCatching { cm.getLinkProperties(cm.activeNetwork) }.getOrNull()
+ val activeLinkProperties = readActiveLinkProperties(cm)
if (isVpnInterface(activeLinkProperties?.interfaceName)) {
activeLinkProperties?.interfaceName?.let(frameworkInterfaces::add)
frameworkDetected += "LinkProperties"
@@ -117,7 +119,7 @@ object VpnDetectionTest {
} catch (_: Throwable) {
null
}
- val activeLinkProperties = runCatching { cm.getLinkProperties(cm.activeNetwork) }.getOrNull()
+ val activeLinkProperties = readActiveLinkProperties(cm)
val networks = cm.allNetworks ?: emptyArray()
val proxies = buildList {
add(formatProxyInfo(defaultProxy))
@@ -129,7 +131,12 @@ object VpnDetectionTest {
return proxies.firstOrNull { !it.isNullOrEmpty() }
}
- private fun readProxyFromLinkProperties(lp: android.net.LinkProperties?): android.net.ProxyInfo? {
+ private fun readActiveLinkProperties(cm: ConnectivityManager): LinkProperties? {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return null
+ return runCatching { cm.getLinkProperties(cm.activeNetwork) }.getOrNull()
+ }
+
+ private fun readProxyFromLinkProperties(lp: LinkProperties?): android.net.ProxyInfo? {
if (lp == null) return null
return try {
val method = lp.javaClass.getMethod("getHttpProxy")