diff --git a/app/src/main/java/eu/depau/ddroid/abc/UsbWriteService.kt b/app/src/main/java/eu/depau/ddroid/abc/UsbWriteService.kt
index 4f7624a..916f601 100644
--- a/app/src/main/java/eu/depau/ddroid/abc/UsbWriteService.kt
+++ b/app/src/main/java/eu/depau/ddroid/abc/UsbWriteService.kt
@@ -4,46 +4,78 @@ import android.app.IntentService
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.content.Context
+import android.content.Intent
import android.net.Uri
import android.os.Build
+import android.os.PowerManager
import android.support.v4.app.NotificationCompat
import eu.depau.ddroid.R
import eu.depau.ddroid.utils.getFileName
import eu.depau.ddroid.utils.toHRSize
+import eu.depau.ddroid.utils.toHRTime
abstract class UsbWriteService(name: String) : IntentService(name) {
val TAG = name
val FOREGROUND_ID = 1931
+ val RESULT_NOTIFICATION_ID = 3829
val WRITE_PROGRESS_CHANNEL_ID = "eu.depau.ddroid.notifications.USB_WRITE_PROGRESS"
+ val WRITE_RESULT_CHANNEL_ID = "eu.depau.ddroid.notifications.USB_WRITE_RESULT"
+ val WAKELOCK_TAG = "eu.depau.ddroid.wakelocks.USB_WRITING"
+
private var prevTime = System.currentTimeMillis()
private var prevBytes = 0L
private var notifyChanRegistered = false
+ private var mWakeLock: PowerManager.WakeLock? = null
+ private var wlAcquireTime = -1L
+ private val WL_TIMEOUT = 10 * 60 * 1000L
- fun getNotificationBuilder(): NotificationCompat.Builder {
+ override fun onHandleIntent(intent: Intent?) {
+ startForeground(FOREGROUND_ID, buildForegroundNotification(null, null, -1, -1))
+
+ try {
+ writeImage(intent!!)
+ } finally {
+ stopForeground(true)
+ }
+ }
+
+ abstract fun writeImage(intent: Intent): Long
+
+ fun getNotificationBuilder(channel: String = WRITE_PROGRESS_CHANNEL_ID): NotificationCompat.Builder {
if (!notifyChanRegistered) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val channame = "USB write progress"
- val description = "Displays the status of ongoing USB writes"
- val importance = NotificationManager.IMPORTANCE_LOW
- val channel = NotificationChannel(WRITE_PROGRESS_CHANNEL_ID, channame, importance)
- channel.description = description
+ val statusChannel = NotificationChannel(
+ WRITE_PROGRESS_CHANNEL_ID,
+ getString(R.string.notchan_writestatus_title),
+ NotificationManager.IMPORTANCE_LOW
+ )
+ statusChannel.description = getString(R.string.notchan_writestatus_desc)
+
+ val resultChannel = NotificationChannel(
+ WRITE_RESULT_CHANNEL_ID,
+ "USB write result notifications",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ resultChannel.description = "Used to display the result of a finished write operation"
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java)
- notificationManager!!.createNotificationChannel(channel)
+ notificationManager!!.createNotificationChannel(statusChannel)
+ notificationManager!!.createNotificationChannel(resultChannel)
}
notifyChanRegistered = true
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- NotificationCompat.Builder(this, WRITE_PROGRESS_CHANNEL_ID)
+ NotificationCompat.Builder(this, channel)
else
NotificationCompat.Builder(this)
}
- fun updateNotification(usbDevice: String, uri: Uri, bytes: Long, total: Long) {
+ fun updateNotification(usbDevice: String, filename: String?, bytes: Long, total: Long) {
// Notification rate limiting
val time = System.currentTimeMillis()
if (time <= prevTime + 1000)
@@ -56,24 +88,35 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
val perc: Int = (bytes.toDouble() / total * 100.0).toInt()
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
- notificationManager.notify(FOREGROUND_ID, buildForegroundNotification(usbDevice, uri, bytes, total, "$perc% • $speed/s"))
+ notificationManager.notify(FOREGROUND_ID, buildForegroundNotification(usbDevice, filename, bytes, total, "$perc% • $speed/s"))
}
- fun errorNotification() {
+ fun resultNotification(usbDevice: String, filename: String, success: Boolean, bytes: Long = 0, startTime: Long = 0) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
- val b = getNotificationBuilder()
- .setContentTitle("Write failed")
- .setContentText("The USB drive may have been unplugged while writing.")
+ val b = getNotificationBuilder(WRITE_RESULT_CHANNEL_ID)
.setOngoing(false)
+ val dt = System.currentTimeMillis() - startTime
+
+ if (!success)
+ b.setContentTitle("Write failed")
+ .setContentText("$usbDevice may have been unplugged while writing.")
+ .setSubText(dt.toHRTime())
+ else {
+ val speed = (dt.toDouble() / bytes.toDouble()).toHRSize() + "/s"
+ b.setContentTitle("Write finished")
+ .setContentText("$filename successfully written to $usbDevice")
+ .setSubText("${dt.toHRTime()} • ${bytes.toHRSize()} • $speed")
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
b.setSmallIcon(R.drawable.ic_usb_white_24dp)
- notificationManager.notify(FOREGROUND_ID, b.build())
+ notificationManager.notify(RESULT_NOTIFICATION_ID, b.build())
}
- fun buildForegroundNotification(usbDevice: String, uri: Uri, bytes: Long, total: Long, subText: String? = null): Notification {
+ fun buildForegroundNotification(usbDevice: String?, filename: String?, bytes: Long, total: Long, subText: String? = null): Notification {
val progr: Int
val indet: Boolean
@@ -81,17 +124,21 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
progr = 0
indet = true
} else {
- progr = (bytes.toFloat()/total * 100).toInt()
+ progr = (bytes.toFloat() / total * 100).toInt()
indet = false
}
val b = getNotificationBuilder()
- b.setContentTitle("Writing image")
- .setContentText("${uri.getFileName(this)} to $usbDevice")
+ b.setContentTitle(getString(R.string.notif_writing_img))
.setOngoing(true)
.setProgress(100, progr, indet)
+ if (usbDevice != null && filename != null)
+ b.setContentText("${filename} to $usbDevice")
+ else
+ b.setContentText(getString(R.string.notif_initializing))
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
b.setSmallIcon(R.drawable.ic_usb_white_24dp)
@@ -101,4 +148,31 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
return b.build()
}
+
+ fun wakeLock(acquire: Boolean) {
+ // Do not reacquire wakelock if timeout not expired
+ if (acquire && mWakeLock != null && wlAcquireTime > 0 && System.currentTimeMillis() < wlAcquireTime + WL_TIMEOUT - 5000)
+ return
+
+ wlAcquireTime = if (acquire)
+ System.currentTimeMillis()
+ else
+ -1
+
+ val powerMgr = getSystemService(Context.POWER_SERVICE) as PowerManager
+
+ powerMgr.run {
+ if (mWakeLock == null)
+ mWakeLock = newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG)
+
+ mWakeLock.apply {
+ if (acquire)
+ this!!.acquire(WL_TIMEOUT /*10 minutes*/)
+ else
+ this!!.release()
+ }
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/ddroid/services/UsbAPIWriteService.kt b/app/src/main/java/eu/depau/ddroid/services/UsbAPIWriteService.kt
index 2e2a76f..2d5df85 100644
--- a/app/src/main/java/eu/depau/ddroid/services/UsbAPIWriteService.kt
+++ b/app/src/main/java/eu/depau/ddroid/services/UsbAPIWriteService.kt
@@ -6,6 +6,7 @@ import android.net.Uri
import android.util.Log
import com.github.mjdev.libaums.UsbMassStorageDevice
import eu.depau.ddroid.abc.UsbWriteService
+import eu.depau.ddroid.utils.getFileName
import eu.depau.ddroid.utils.getFileSize
import eu.depau.ddroid.utils.name
import java.nio.ByteBuffer
@@ -18,20 +19,6 @@ class UsbAPIWriteService : UsbWriteService("UsbAPIWriteService") {
val WRITE_CANCEL = "eu.depau.ddroid.action.API_WRITE_CANCEL"
}
- override fun onHandleIntent(intent: Intent?) {
- val uri: Uri = intent!!.data!!
- val usbDevice: UsbDevice = intent.getParcelableExtra("usbDevice")
-
- startForeground(FOREGROUND_ID, buildForegroundNotification(usbDevice.name, uri, -1, -1))
-
- try {
- val notify = { bytes: Long, total: Long -> updateNotification(usbDevice.name, uri, bytes, total) }
- writeImage(usbDevice, uri, notify)
- } finally {
- stopForeground(true)
- }
- }
-
private fun getUsbMSDevice(usbDevice: UsbDevice): UsbMassStorageDevice? {
val msDevs = UsbMassStorageDevice.getMassStorageDevices(this)
@@ -43,34 +30,50 @@ class UsbAPIWriteService : UsbWriteService("UsbAPIWriteService") {
return null
}
- private fun writeImage(usbDevice: UsbDevice, uri: Uri, notify: (Long, Long) -> Unit): Long {
+ override fun writeImage(intent: Intent): Long {
+ val uri: Uri = intent.data!!
+ val usbDevice: UsbDevice = intent.getParcelableExtra("usbDevice")
+
val msDev = getUsbMSDevice(usbDevice)!!
msDev.init()
val blockDev = msDev.blockDevice
- var bsFactor = DD_BLOCKSIZE / blockDev.blockSize
+ val bsFactor = DD_BLOCKSIZE / blockDev.blockSize
val byteBuffer = ByteBuffer.allocate(blockDev.blockSize * bsFactor)
val imageSize = uri.getFileSize(this)
val inputStream = contentResolver.openInputStream(uri)!!
+ val startTime = System.currentTimeMillis()
+
var readBytes: Int
var offset = 0L
+ var writtenBytes: Long = 0
- while (true) {
- readBytes = inputStream.read(byteBuffer.array()!!)
- if (readBytes < 0)
- break
- byteBuffer.position(0)
+ try {
+ while (true) {
+ wakeLock(true)
+ readBytes = inputStream.read(byteBuffer.array()!!)
+ if (readBytes < 0)
+ break
+ byteBuffer.position(0)
- blockDev.write(offset, byteBuffer)
- offset += bsFactor
+ blockDev.write(offset, byteBuffer)
+ offset += bsFactor
+ writtenBytes += readBytes
- notify(offset * blockDev.blockSize * bsFactor, imageSize)
+ updateNotification(usbDevice.name, uri.getFileName(this), offset * blockDev.blockSize, imageSize)
+ }
+
+ resultNotification(usbDevice.name, uri.getFileName(this)!!, true, writtenBytes, startTime)
+ } catch (e: Exception) {
+ resultNotification(usbDevice.name, uri.getFileName(this)!!, false, writtenBytes, startTime)
+ Log.e(TAG, "Could't write image to ${usbDevice.name}")
+ throw e
+ } finally {
+ wakeLock(false)
+ msDev.close()
}
- msDev.close()
-
- val writtenBytes = offset * blockDev.blockSize
Log.d(TAG, "Written $writtenBytes bytes to ${usbDevice.name} using API")
return writtenBytes
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 165a215..1ba3bf5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,4 +25,8 @@
Image is bigger than the USB drive, so it can\'t be written
Cannot read USB device
Tap Next to write the image to the USB drive
+ USB write status
+ Used to display the status of images being written to USB drives
+ Initializing...
+ Writing image