Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import android.os.Build
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED
import kotlinx.coroutines.CompletableDeferred
import java.util.Timer
import java.util.TimerTask

private val Context.usbPermissionCallbackAction
get() = "$packageName.USB_PERMISSION_CALLBACK"
Expand All @@ -40,6 +44,10 @@ private object UsbDevicePermissionReceiver : BroadcastReceiver() {
}
}

fun isDeferred(device: UsbDevice): Boolean = synchronized(this) {
return pendingRequests.containsKey(device)
}

fun unregister(context: Context) = synchronized(this) {
if (registered) {
context.unregisterReceiver(this)
Expand Down Expand Up @@ -75,10 +83,39 @@ class UsbDevicePermissionManager(private val context: Context) {
if (context.usbManager?.hasPermission(device) == true) return true
val res = CompletableDeferred<Boolean>()
if (UsbDevicePermissionReceiver.addDeferred(device, res)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d(TAG, "PermissionReceiver added for ${device.productName} (${context.packageName})")
}
UsbDevicePermissionReceiver.register(context)
val intent = PendingIntentCompat.getBroadcast(context, 0, Intent(context.usbPermissionCallbackAction).apply { `package` = context.packageName }, 0, true)
context.usbManager?.requestPermission(device, intent)
schedulePermissionRequest(device, 5)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d(TAG, "PermissionReceiver already added for ${device.productName}")
}
}
requestPermission(device)
return res.await()
}

// In case we couldn't ask for permission, retry every secondes, with a maximum of `maxRetries`
private fun schedulePermissionRequest(device: UsbDevice, maxRetries: Int) {
if (maxRetries < 1) return
Timer().schedule(object : TimerTask() {
override fun run() {
if (UsbDevicePermissionReceiver.isDeferred(device)) {
requestPermission(device)
schedulePermissionRequest(device, maxRetries - 1)
}
}
}, 1000)
}

private fun requestPermission(device: UsbDevice) {
val intent = PendingIntentCompat.getBroadcast(context, 0, Intent(context.usbPermissionCallbackAction).apply { `package` = context.packageName }, 0, true)
context.usbManager?.requestPermission(device, intent)
}

companion object {
private const val TAG = "UsbDevicePermissionMan"
}
}