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")