File Operations Notification
This commit is contained in:
parent
754ef3bc5a
commit
f00901a717
@ -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()
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user