Skip to content

Instantly share code, notes, and snippets.

@LouisCAD
Forked from bj0/UsbPermissions.kt
Last active November 30, 2017 20:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LouisCAD/084fbd6af4d2029047468a962628906c to your computer and use it in GitHub Desktop.
Save LouisCAD/084fbd6af4d2029047468a962628906c to your computer and use it in GitHub Desktop.
Requesting permission to open UsbDevice in Kotlin using coroutines
package com.my.app
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import kotlinx.coroutines.experimental.CompletableDeferred
import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.runBlocking
/**
* action to identify our requests
*/
private const val ACTION = "com.my.app.USB_PERMISSION"
// buffer size of one so the first send() queues instead of blocks
private val queue = Channel<CompletableDeferred<Boolean>>(1)
/**
* request permission for this device from the [UsbManager]
*/
private fun Context.sendPermissionRequest(device: UsbDevice) {
usbManager.requestPermission(device,
PendingIntent.getBroadcast(this, 0, Intent(ACTION), 0))
}
/**
* receive responses to permission requests
*/
private val receiver = object : BroadcastReceiver() {
var isRegistered = false
private set
fun register(context: Context): Intent? = synchronized(this) {
return try {
if (!isRegistered)
context.registerReceiver(this, IntentFilter(ACTION))
else
null
} finally {
isRegistered = true
}
}
fun unregister(context: Context) = synchronized(this) {
if (isRegistered)
try {
context.unregisterReceiver(this)
} finally {
isRegistered = false
}
}
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == ACTION) {
val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
runBlocking {
// the queue should never be empty at this point...
val result = queue.poll() ?: return@runBlocking
// send result
result.complete(device != null && context?.usbManager?.hasPermission(device) == true)
}
}
}
}
/**
* register to receive responses to permission requests
*/
fun Context.registerForUsbPermissions() = receiver.register(this)
/**
* unregister to no longer receive responses to permission requests
*/
fun Context.unregisterForUsbPermissions() = receiver.unregister(this)
/**
* request permission for a device. returns true if permission is granted
*/
suspend fun Context.requestPermission(device: UsbDevice, autoRegister: Boolean = true) = when {
usbManager.hasPermission(device) -> true
!receiver.isRegistered && !autoRegister -> false // throw if not registered?
else -> {
val result = CompletableDeferred<Boolean>()
// post a request in the queue so we receive the result. this will suspend if there's
// already a request in the queue (keeps the order correct)
queue.send(result)
// auto-register if not already registered
if (!receiver.isRegistered) registerForUsbPermissions()
sendPermissionRequest(device)
// wait for result
result.await()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment