refactor: implement CompatExtensions (#641)

updates deprecated methods, classes, and flags introduced in Android SDK 33, while ensuring compatibility with previous Android versions
This commit is contained in:
Andre K 2023-05-30 18:30:46 -03:00 committed by GitHub
parent f7a895e62e
commit 271124dc9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 45 deletions

View file

@ -2,6 +2,7 @@ package com.geeksville.mesh
import android.os.Parcel
import android.os.Parcelable
import com.geeksville.mesh.util.readParcelableCompat
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
@ -77,7 +78,7 @@ data class DataPacket(
parcel.readString(),
parcel.readLong(),
parcel.readInt(),
parcel.readParcelable(MessageStatus::class.java.classLoader),
parcel.readParcelableCompat(MessageStatus::class.java.classLoader),
parcel.readInt(),
parcel.readInt(),
)
@ -138,7 +139,7 @@ data class DataPacket(
from = parcel.readString()
time = parcel.readLong()
id = parcel.readInt()
status = parcel.readParcelable(MessageStatus::class.java.classLoader)
status = parcel.readParcelableCompat(MessageStatus::class.java.classLoader)
hopLimit = parcel.readInt()
channel = parcel.readInt()
}

View file

@ -41,8 +41,10 @@ import com.geeksville.mesh.repository.radio.SerialInterface
import com.geeksville.mesh.service.*
import com.geeksville.mesh.ui.*
import com.geeksville.mesh.util.Exceptions
import com.geeksville.mesh.util.getParcelableExtraCompat
import com.geeksville.mesh.util.LanguageUtils
import com.geeksville.mesh.util.exceptionReporter
import com.geeksville.mesh.util.getPackageInfoCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator
@ -264,7 +266,7 @@ class MainActivity : AppCompatActivity(), Logging {
}
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
val device: UsbDevice? = intent.getParcelableExtraCompat(UsbManager.EXTRA_DEVICE)
if (device != null) {
debug("Handle USB device attached! $device")
usbDevice = device
@ -475,12 +477,11 @@ class MainActivity : AppCompatActivity(), Logging {
when (intent.action) {
MeshService.ACTION_NODE_CHANGE -> {
val info: NodeInfo =
intent.getParcelableExtra(EXTRA_NODEINFO)!!
val info: NodeInfo? = intent.getParcelableExtraCompat(EXTRA_NODEINFO)
debug("UI nodechange $info")
// We only care about nodes that have user info
info.user?.id?.let {
info?.user?.id?.let {
val nodes = model.nodeDB.nodes.value!! + Pair(it, info)
model.nodeDB.setNodes(nodes)
}
@ -698,7 +699,7 @@ class MainActivity : AppCompatActivity(), Logging {
return true
}
val handler: Handler by lazy {
private val handler: Handler by lazy {
Handler(Looper.getMainLooper())
}
@ -791,7 +792,7 @@ class MainActivity : AppCompatActivity(), Logging {
private fun getVersionInfo() {
try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, 0)
val packageInfo: PackageInfo = packageManager.getPackageInfoCompat(packageName, 0)
val versionName = packageInfo.versionName
Toast.makeText(this, versionName, Toast.LENGTH_LONG).show()
} catch (e: PackageManager.NameNotFoundException) {

View file

@ -320,7 +320,12 @@ class BTScanModel @Inject constructor(
associationRequest(),
@SuppressLint("NewApi")
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
@Deprecated("Deprecated in Java", ReplaceWith("onAssociationPending(intentSender)"))
override fun onDeviceFound(intentSender: IntentSender) {
onAssociationPending(intentSender)
}
override fun onAssociationPending(chooserLauncher: IntentSender) {
debug("CompanionDeviceManager - device found")
_spinner.value = false
chooserLauncher.let {

View file

@ -8,6 +8,7 @@ import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.util.exceptionReporter
import com.geeksville.mesh.util.getParcelableExtraCompat
import javax.inject.Inject
/**
@ -24,7 +25,9 @@ class UsbBroadcastReceiver @Inject constructor(
}
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
val deviceName: String = intent.getParcelableExtra<UsbDevice?>(UsbManager.EXTRA_DEVICE)?.deviceName ?: "unknown"
val device: UsbDevice? = intent.getParcelableExtraCompat(UsbManager.EXTRA_DEVICE)
val deviceName: String = device?.deviceName ?: "unknown"
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
debug("USB device '$deviceName' was detached")

View file

@ -7,17 +7,18 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.android.notificationManager
import com.geeksville.mesh.util.PendingIntentCompat
import java.io.Closeable
@ -105,28 +106,20 @@ class MeshServiceNotifications(
)
private val openAppIntent: PendingIntent by lazy {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0)
} else {
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE)
}
PendingIntent.getActivity(
context,
0,
Intent(context, MainActivity::class.java),
PendingIntentCompat.FLAG_IMMUTABLE
)
}
/**
* Generate a bitmap from a vector drawable (even on old builds)
* https://stackoverflow.com/questions/33696488/getting-bitmap-from-vector-drawable
*/
private fun getBitmapFromVectorDrawable(drawableId: Int): Bitmap {
val drawable = ContextCompat.getDrawable(context, drawableId)!!
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
private fun getBitmapFromVectorDrawable(drawableId: Int): Bitmap? =
AppCompatResources.getDrawable(context, drawableId)?.toBitmap()
private fun commonBuilder(channel: String): NotificationCompat.Builder {
val builder = NotificationCompat.Builder(context, channel)
@ -142,10 +135,13 @@ class MeshServiceNotifications(
// Newer androids also support a 'large' icon
// We delay making this bitmap until we know we need it
if (largeIcon == null)
largeIcon = getBitmapFromVectorDrawable(R.mipmap.ic_launcher2)
largeIcon = largeIcon ?: getBitmapFromVectorDrawable(R.mipmap.ic_launcher2)
builder.setSmallIcon(if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) R.drawable.app_icon_novect else R.drawable.app_icon) // vector form icons don't work reliably on older androids
builder.setSmallIcon(
// vector form icons don't work reliably on older androids
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) R.drawable.app_icon_novect
else R.drawable.app_icon
)
.setLargeIcon(largeIcon)
}
return builder

View file

@ -3,7 +3,6 @@ package com.geeksville.mesh.ui
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.bluetooth.BluetoothDevice
import android.companion.CompanionDeviceManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -48,9 +47,12 @@ import com.geeksville.mesh.repository.radio.MockInterface
import com.geeksville.mesh.repository.usb.UsbRepository
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.service.SoftwareUpdateService
import com.geeksville.mesh.util.CompanionDeviceManagerCompat
import com.geeksville.mesh.util.PendingIntentCompat
import com.geeksville.mesh.util.anonymize
import com.geeksville.mesh.util.exceptionReporter
import com.geeksville.mesh.util.exceptionToSnackbar
import com.geeksville.mesh.util.getParcelableExtraCompat
import com.geeksville.mesh.util.onEditorAction
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
@ -245,10 +247,8 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
ActivityResultContracts.StartIntentSenderForResult()
) {
it.data
?.getParcelableExtra<BluetoothDevice>(CompanionDeviceManager.EXTRA_DEVICE)
?.let { device ->
onSelected(BTScanModel.BLEDeviceListEntry(device))
}
?.getParcelableExtraCompat<BluetoothDevice>(CompanionDeviceManagerCompat.EXTRA_DEVICE)
?.let { device -> onSelected(BTScanModel.BLEDeviceListEntry(device)) }
}
val requestBackgroundAndCheckLauncher =
@ -558,8 +558,9 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
override fun onReceive(context: Context, intent: Intent) {
if (BTScanModel.ACTION_USB_PERMISSION == intent.action) {
val device: UsbDevice =
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!!
val device: UsbDevice? =
intent.getParcelableExtraCompat(UsbManager.EXTRA_DEVICE)
val deviceName: String = device?.deviceName ?: "unknown"
if (intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED,
@ -569,7 +570,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
info("User approved USB access")
changeDeviceAddress(it.fullAddress)
} else {
errormsg("USB permission denied for device $device")
errormsg("USB permission denied for device $deviceName")
}
}
// We don't need to stay registered
@ -577,12 +578,12 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
}
}
val permissionIntent =
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(activity, 0, Intent(BTScanModel.ACTION_USB_PERMISSION), 0)
} else {
PendingIntent.getBroadcast(activity, 0, Intent(BTScanModel.ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE)
}
val permissionIntent = PendingIntent.getBroadcast(
activity,
0,
Intent(BTScanModel.ACTION_USB_PERMISSION),
PendingIntentCompat.FLAG_IMMUTABLE
)
val filter = IntentFilter(BTScanModel.ACTION_USB_PERMISSION)
requireActivity().registerReceiver(usbReceiver, filter)
requireContext().usbManager.requestPermission(it.usb.device, permissionIntent)

View file

@ -0,0 +1,48 @@
package com.geeksville.mesh.util
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.companion.CompanionDeviceManager
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Parcel
import android.os.Parcelable
import androidx.core.content.IntentCompat
import androidx.core.os.ParcelCompat
object PendingIntentCompat {
val FLAG_MUTABLE = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
0
}
val FLAG_IMMUTABLE = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.FLAG_IMMUTABLE
} else {
0
}
}
object CompanionDeviceManagerCompat {
@SuppressLint("InlinedApi")
val EXTRA_DEVICE = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
CompanionDeviceManager.EXTRA_ASSOCIATION
} else {
@Suppress("DEPRECATION") CompanionDeviceManager.EXTRA_DEVICE
}
}
inline fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? =
ParcelCompat.readParcelable(this, loader, T::class.java)
inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String?): T? =
IntentCompat.getParcelableExtra(this, key, T::class.java)
fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int = 0): PackageInfo =
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags.toLong()))
} else {
@Suppress("DEPRECATION") getPackageInfo(packageName, flags)
}