mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
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:
parent
f7a895e62e
commit
271124dc9c
7 changed files with 100 additions and 45 deletions
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue