File Operations Notification

This commit is contained in:
Matéo Duparc 2020-12-29 18:39:43 +01:00
parent 754ef3bc5a
commit f00901a717
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
2 changed files with 97 additions and 28 deletions

View File

@ -1,6 +1,10 @@
package sushi.hardcore.droidfs
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.*
@ -13,10 +17,16 @@ import sushi.hardcore.droidfs.util.Wiper
import java.io.File
import java.io.FileNotFoundException
class FileOperationService : Service() {
companion object {
const val NOTIFICATION_ID = 1
const val NOTIFICATION_CHANNEL_ID = "FileOperations"
}
private val binder = LocalBinder()
private lateinit var gocryptfsVolume: GocryptfsVolume
private lateinit var notificationManager: NotificationManager
inner class LocalBinder : Binder() {
fun getService(): FileOperationService = this@FileOperationService
@ -29,6 +39,35 @@ class FileOperationService : Service() {
return binder
}
private fun showNotification(message: String, total: Int): Notification.Builder {
if (!::notificationManager.isInitialized){
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
val notificationBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, getString(R.string.file_operations), NotificationManager.IMPORTANCE_LOW)
notificationManager.createNotificationChannel(channel)
Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
} else {
Notification.Builder(this)
}
notificationBuilder.setOngoing(true)
.setContentTitle(getString(R.string.file_op_notification_title))
.setContentText(message)
.setSmallIcon(R.mipmap.icon_launcher)
.setProgress(total, 0, false)
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
return notificationBuilder
}
private fun updateNotificationProgress(notificationBuilder: Notification.Builder, progress: Int, total: Int){
notificationBuilder.setProgress(total, progress, false)
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}
private fun cancelNotification(){
notificationManager.cancel(NOTIFICATION_ID)
}
private fun copyFile(srcPath: String, dstPath: String, remoteGocryptfsVolume: GocryptfsVolume = gocryptfsVolume): Boolean {
var success = true
val srcHandleId = remoteGocryptfsVolume.openReadMode(srcPath)
@ -60,55 +99,66 @@ class FileOperationService : Service() {
fun copyElements(items: ArrayList<OperationFile>, remoteGocryptfsVolume: GocryptfsVolume = gocryptfsVolume, callback: (String?) -> Unit){
Thread {
val notificationBuilder = showNotification(getString(R.string.file_op_copy_msg), items.size)
var failedItem: String? = null
for (item in items){
if (item.explorerElement.isDirectory){
if (!gocryptfsVolume.pathExists(item.dstPath!!)) {
if (!gocryptfsVolume.mkdir(item.dstPath!!)) {
failedItem = item.explorerElement.fullPath
for (i in 0 until items.size){
if (items[i].explorerElement.isDirectory){
if (!gocryptfsVolume.pathExists(items[i].dstPath!!)) {
if (!gocryptfsVolume.mkdir(items[i].dstPath!!)) {
failedItem = items[i].explorerElement.fullPath
}
}
} else {
if (!copyFile(item.explorerElement.fullPath, item.dstPath!!, remoteGocryptfsVolume)){
failedItem = item.explorerElement.fullPath
if (!copyFile(items[i].explorerElement.fullPath, items[i].dstPath!!, remoteGocryptfsVolume)){
failedItem = items[i].explorerElement.fullPath
}
}
if (failedItem != null){
if (failedItem == null){
updateNotificationProgress(notificationBuilder, i, items.size)
} else {
break
}
}
cancelNotification()
callback(failedItem)
}.start()
}
fun moveElements(items: ArrayList<OperationFile>, callback: (String?) -> Unit){
Thread {
val notificationBuilder = showNotification(getString(R.string.file_op_move_msg), items.size)
val mergedFolders = ArrayList<String>()
var failedItem: String? = null
for (item in items){
if (item.explorerElement.isDirectory && gocryptfsVolume.pathExists(item.dstPath!!)){ //folder will be merged
mergedFolders.add(item.explorerElement.fullPath)
for (i in 0 until items.size){
if (items[i].explorerElement.isDirectory && gocryptfsVolume.pathExists(items[i].dstPath!!)){ //folder will be merged
mergedFolders.add(items[i].explorerElement.fullPath)
} else {
if (!gocryptfsVolume.rename(item.explorerElement.fullPath, item.dstPath!!)){
failedItem = item.explorerElement.fullPath
if (!gocryptfsVolume.rename(items[i].explorerElement.fullPath, items[i].dstPath!!)){
failedItem = items[i].explorerElement.fullPath
break
} else {
updateNotificationProgress(notificationBuilder, i, items.size)
}
}
}
if (failedItem == null){
for (path in mergedFolders) {
if (!gocryptfsVolume.rmdir(path)){
failedItem = path
for (i in 0 until mergedFolders.size) {
if (!gocryptfsVolume.rmdir(mergedFolders[i])){
failedItem = mergedFolders[i]
break
} else {
updateNotificationProgress(notificationBuilder, items.size-(mergedFolders.size-i), items.size)
}
}
}
cancelNotification()
callback(failedItem)
}.start()
}
fun importFilesFromUris(items: ArrayList<OperationFile>, uris: List<Uri>, callback: (String?) -> Unit){
Thread {
val notificationBuilder = showNotification(getString(R.string.file_op_import_msg), items.size)
var failedIndex = -1
for (i in 0 until items.size) {
try {
@ -118,12 +168,16 @@ class FileOperationService : Service() {
} catch (e: FileNotFoundException){
failedIndex = i
}
if (failedIndex != -1){
if (failedIndex == -1) {
updateNotificationProgress(notificationBuilder, i, items.size)
} else {
cancelNotification()
callback(uris[failedIndex].toString())
break
}
}
if (failedIndex == -1){
cancelNotification()
callback(null)
}
}.start()
@ -131,13 +185,17 @@ class FileOperationService : Service() {
fun wipeUris(uris: List<Uri>, callback: (String?) -> Unit){
Thread {
val notificationBuilder = showNotification(getString(R.string.file_op_wiping_msg), uris.size)
var errorMsg: String? = null
for (uri in uris) {
errorMsg = Wiper.wipe(this, uri)
if (errorMsg != null) {
for (i in uris.indices) {
errorMsg = Wiper.wipe(this, uris[i])
if (errorMsg == null) {
updateNotificationProgress(notificationBuilder, i, uris.size)
} else {
break
}
}
cancelNotification()
callback(errorMsg)
}.start()
}
@ -146,10 +204,10 @@ class FileOperationService : Service() {
val outputStream = treeDocumentFile.createFile("*/*", File(srcPath).name)?.uri?.let {
contentResolver.openOutputStream(it)
}
return if (outputStream != null){
gocryptfsVolume.exportFile(srcPath, outputStream)
} else {
return if (outputStream == null) {
false
} else {
gocryptfsVolume.exportFile(srcPath, outputStream)
}
}
@ -176,17 +234,21 @@ class FileOperationService : Service() {
Thread {
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
DocumentFile.fromTreeUri(this, uri)?.let { treeDocumentFile ->
val notificationBuilder = showNotification(getString(R.string.file_op_export_msg), items.size)
var failedItem: String? = null
for (element in items) {
failedItem = if (element.isDirectory) {
recursiveExportDirectory(element.fullPath, treeDocumentFile)
for (i in items.indices) {
failedItem = if (items[i].isDirectory) {
recursiveExportDirectory(items[i].fullPath, treeDocumentFile)
} else {
if (exportFileInto(element.fullPath, treeDocumentFile)) null else element.fullPath
if (exportFileInto(items[i].fullPath, treeDocumentFile)) null else items[i].fullPath
}
if (failedItem != null) {
if (failedItem == null) {
updateNotificationProgress(notificationBuilder, i, items.size)
} else {
break
}
}
cancelNotification()
callback(failedItem)
}
}.start()

View File

@ -186,4 +186,11 @@
<string name="hidden_volume_warning">Hidden volumes are stored in the app\'s internal storage. Other apps can\'t see these volumes without root access. However, if you uninstall DroidFS or clear data of the app, all your hidden volumes will be LOST. Be sure to make backups !</string>
<string name="camera_perm_needed">Camera permission is needed to take photo.</string>
<string name="choose_resolution">Choose a resolution</string>
<string name="file_operations">File Operations</string>
<string name="file_op_notification_title">File Operations running…</string>
<string name="file_op_copy_msg">Copying files…</string>
<string name="file_op_import_msg">Importing files…</string>
<string name="file_op_export_msg">Exporting files…</string>
<string name="file_op_move_msg">Moving files…</string>
<string name="file_op_wiping_msg">Wiping files…</string>
</resources>