diff --git a/app/build.gradle b/app/build.gradle
index 48fd242..9ffb18e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -26,7 +26,6 @@ android {
release {
minifyEnabled true
shrinkResources true
- useProguard true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
diff --git a/app/libgocryptfs/main.go b/app/libgocryptfs/main.go
index bbc0002..0d392f7 100644
--- a/app/libgocryptfs/main.go
+++ b/app/libgocryptfs/main.go
@@ -120,7 +120,9 @@ func openBackingDir(sessionID int, relPath string) (dirfd int, cName string, err
if i == len(parts)-1 {
cache_dirfd, err := syscall.Dup(dirfd)
if err == nil {
- sessions[sessionID].dirCache[dirRelPath] = Directory{cache_dirfd, iv}
+ var dirRelPathCopy strings.Builder
+ dirRelPathCopy.WriteString(dirRelPath)
+ sessions[sessionID].dirCache[dirRelPathCopy.String()] = Directory{cache_dirfd, iv}
}
break
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 49ae32d..b056198 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,7 +14,7 @@
@@ -39,9 +40,11 @@
= Build.VERSION_CODES.M && usf_fingerprint) {
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
diff --git a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt
index a533484..78f7597 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt
@@ -9,6 +9,7 @@ class ConstValues {
const val saved_volumes_key = "saved_volumes"
const val sort_order_key = "sort_order"
val fakeUri: Uri = Uri.parse("fakeuri://droidfs")
+ const val MAX_KERNEL_WRITE = 128*1024
const val wipe_passes = 2
private val fileExtensions = mapOf(
Pair("image", listOf("png", "jpg", "jpeg")),
diff --git a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt
index 98e8053..fb4afd3 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt
@@ -31,6 +31,7 @@ class CreateActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create)
setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
diff --git a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt
index b96acb9..a58745d 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt
@@ -38,6 +38,7 @@ class OpenActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_open)
setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
diff --git a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt
index cf1963a..dd308d6 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt
@@ -26,7 +26,6 @@ class SettingsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceSt
.beginTransaction()
.replace(R.id.settings, fragment)
.commit()
- setTheme(R.style.AppTheme)
}
class SettingsFragment : PreferenceFragmentCompat() {
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
index 2b10a26..417b420 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
@@ -63,11 +63,7 @@ open class BaseExplorerActivity : BaseActivity() {
setCurrentPath(currentDirectoryPath)
list_explorer.adapter = explorerAdapter
list_explorer.onItemClickListener = OnItemClickListener { _, _, position, _ -> onExplorerItemClick(position) }
- list_explorer.onItemLongClickListener = OnItemLongClickListener { _, _, position, _ ->
- explorerAdapter.onItemLongClick(position)
- invalidateOptionsMenu()
- true
- }
+ list_explorer.onItemLongClickListener = OnItemLongClickListener { _, _, position, _ -> onExplorerItemLongClick(position); true }
refresher.setOnRefreshListener {
setCurrentPath(currentDirectoryPath)
refresher.isRefreshing = false
@@ -90,13 +86,13 @@ open class BaseExplorerActivity : BaseActivity() {
explorerAdapter.onItemClick(position)
if (explorerAdapter.selectedItems.isEmpty()) {
if (!wasSelecting) {
- val fullPath = PathUtils.path_join(currentDirectoryPath, explorerElements[position].name)
+ val fullPath = explorerElements[position].getFullPath()
when {
explorerElements[position].isDirectory -> {
setCurrentPath(fullPath)
}
explorerElements[position].isParentFolder -> {
- setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
+ setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
}
isImage(fullPath) -> {
startFileViewer(ImageViewer::class.java, fullPath)
@@ -128,8 +124,14 @@ open class BaseExplorerActivity : BaseActivity() {
.show()
}
}
+ } else {
+ invalidateOptionsMenu()
}
}
+ }
+
+ protected open fun onExplorerItemLongClick(position: Int) {
+ explorerAdapter.onItemLongClick(position)
invalidateOptionsMenu()
}
@@ -169,7 +171,7 @@ open class BaseExplorerActivity : BaseActivity() {
text_dir_empty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.INVISIBLE
sortExplorerElements()
if (path.isNotEmpty()) { //not root
- explorerElements.add(0, ExplorerElement("..", (-1).toShort(), -1, -1))
+ explorerElements.add(0, ExplorerElement("..", (-1).toShort(), -1, -1, currentDirectoryPath))
}
explorerAdapter.setExplorerElements(explorerElements)
currentDirectoryPath = path
@@ -192,16 +194,16 @@ open class BaseExplorerActivity : BaseActivity() {
protected open fun closeVolumeOnDestroy() {
gocryptfsVolume.close()
- RestrictedFileProvider.wipeAll() //additional security
+ RestrictedFileProvider.wipeAll(this) //additional security
}
override fun onBackPressed() {
if (explorerAdapter.selectedItems.isEmpty()) {
- val parentPath = PathUtils.get_parent_path(currentDirectoryPath)
+ val parentPath = PathUtils.getParentPath(currentDirectoryPath)
if (parentPath == currentDirectoryPath) {
askCloseVolume()
} else {
- setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
+ setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
}
} else {
unselectAll()
@@ -266,23 +268,21 @@ open class BaseExplorerActivity : BaseActivity() {
}
fun handleMenuItems(menu: Menu){
- menu.findItem(R.id.explorer_menu_rename).isVisible = false
+ menu.findItem(R.id.rename).isVisible = false
if (usf_open){
- menu.findItem(R.id.explorer_menu_external_open)?.isVisible = false
+ menu.findItem(R.id.external_open)?.isVisible = false
}
- val selectedItems = explorerAdapter.selectedItems
- if (selectedItems.isEmpty()){
+ val noItemSelected = explorerAdapter.selectedItems.isEmpty()
+ menu.findItem(R.id.sort).isVisible = noItemSelected
+ menu.findItem(R.id.close).isVisible = noItemSelected
+ if (noItemSelected){
toolbar.navigationIcon = null
- menu.findItem(R.id.explorer_menu_close).isVisible = true
- menu.findItem(R.id.explorer_menu_sort).isVisible = true
} else {
toolbar.setNavigationIcon(R.drawable.icon_arrow_back)
- menu.findItem(R.id.explorer_menu_close).isVisible = false
- menu.findItem(R.id.explorer_menu_sort).isVisible = false
- if (selectedItems.size == 1) {
- menu.findItem(R.id.explorer_menu_rename).isVisible = true
- if (usf_open && explorerElements[selectedItems[0]].isRegularFile) {
- menu.findItem(R.id.explorer_menu_external_open)?.isVisible = true
+ if (explorerAdapter.selectedItems.size == 1) {
+ menu.findItem(R.id.rename).isVisible = true
+ if (usf_open && explorerElements[explorerAdapter.selectedItems[0]].isRegularFile) {
+ menu.findItem(R.id.external_open)?.isVisible = true
}
}
}
@@ -294,7 +294,7 @@ open class BaseExplorerActivity : BaseActivity() {
unselectAll()
true
}
- R.id.explorer_menu_sort -> {
+ R.id.sort -> {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.sort_order)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which ->
@@ -306,7 +306,7 @@ open class BaseExplorerActivity : BaseActivity() {
.show()
true
}
- R.id.explorer_menu_rename -> {
+ R.id.rename -> {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
val dialogEditText = dialogEditTextView.findViewById(R.id.dialog_edit_text)
@@ -331,14 +331,14 @@ open class BaseExplorerActivity : BaseActivity() {
dialog.show()
true
}
- R.id.explorer_menu_external_open -> {
+ R.id.external_open -> {
if (usf_open){
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
unselectAll()
}
true
}
- R.id.explorer_menu_close -> {
+ R.id.close -> {
askCloseVolume()
true
}
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt
index 4d3364c..1551823 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt
@@ -8,9 +8,9 @@ import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.widget.EditText
-import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
+import com.github.clans.fab.FloatingActionButton
import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.activity_explorer.*
import sushi.hardcore.droidfs.OpenActivity
@@ -18,7 +18,7 @@ import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
-import java.util.*
+import kotlin.collections.ArrayList
class ExplorerActivity : BaseExplorerActivity() {
private val PICK_DIRECTORY_REQUEST_CODE = 1
@@ -26,12 +26,20 @@ class ExplorerActivity : BaseExplorerActivity() {
private val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
private var usf_decrypt = false
private var usf_share = false
+ private var modeSelectLocation = false
+ private val filesToCopy = ArrayList()
override fun init() {
setContentView(R.layout.activity_explorer)
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
usf_share = sharedPrefs.getBoolean("usf_share", false)
}
+ override fun onExplorerItemLongClick(position: Int) {
+ cancelCopy()
+ explorerAdapter.onItemLongClick(position)
+ invalidateOptionsMenu()
+ }
+
private fun createNewFile(fileName: String){
if (fileName.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
@@ -230,7 +238,7 @@ class ExplorerActivity : BaseExplorerActivity() {
failedItem = if (types[i] == 0) { //directory
recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)
} else {
- if (importFileFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)) null else paths[i]
+ if (importFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.path_join(currentDirectoryPath, File(paths[i]).name))) null else paths[i]
}
if (failedItem != null) {
break
@@ -238,7 +246,7 @@ class ExplorerActivity : BaseExplorerActivity() {
}
}
} else {
- failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path
+ failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name))) null else path
}
if (failedItem == null) {
stopTask {
@@ -269,24 +277,30 @@ class ExplorerActivity : BaseExplorerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu)
- handleMenuItems(menu)
- if (usf_share){
- menu.findItem(R.id.explorer_menu_share).isVisible = false
- }
- val anyItemSelected = explorerAdapter.selectedItems.isNotEmpty()
- menu.findItem(R.id.explorer_menu_select_all).isVisible = anyItemSelected
- menu.findItem(R.id.explorer_menu_delete).isVisible = anyItemSelected
- menu.findItem(R.id.explorer_menu_decrypt).isVisible = anyItemSelected && usf_decrypt
- if (anyItemSelected && usf_share){
- var containsDir = false
- for (i in explorerAdapter.selectedItems) {
- if (explorerElements[i].isDirectory) {
- containsDir = true
- break
- }
+ if (modeSelectLocation) {
+ menu.findItem(R.id.validate).isVisible = true
+ menu.findItem(R.id.close).isVisible = false
+ } else {
+ handleMenuItems(menu)
+ if (usf_share){
+ menu.findItem(R.id.share).isVisible = false
}
- if (!containsDir) {
- menu.findItem(R.id.explorer_menu_share).isVisible = true
+ val anyItemSelected = explorerAdapter.selectedItems.isNotEmpty()
+ menu.findItem(R.id.select_all).isVisible = anyItemSelected
+ menu.findItem(R.id.delete).isVisible = anyItemSelected
+ menu.findItem(R.id.copy).isVisible = anyItemSelected
+ menu.findItem(R.id.decrypt).isVisible = anyItemSelected && usf_decrypt
+ if (anyItemSelected && usf_share){
+ var containsDir = false
+ for (i in explorerAdapter.selectedItems) {
+ if (explorerElements[i].isDirectory) {
+ containsDir = true
+ break
+ }
+ }
+ if (!containsDir) {
+ menu.findItem(R.id.share).isVisible = true
+ }
}
}
return true
@@ -294,12 +308,67 @@ class ExplorerActivity : BaseExplorerActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
- R.id.explorer_menu_select_all -> {
+ android.R.id.home -> {
+ cancelCopy()
+ super.onOptionsItemSelected(item)
+ }
+ R.id.select_all -> {
explorerAdapter.selectAll()
invalidateOptionsMenu()
true
}
- R.id.explorer_menu_delete -> {
+ R.id.copy -> {
+ for (i in explorerAdapter.selectedItems){
+ filesToCopy.add(explorerElements[i])
+ }
+ modeSelectLocation = true
+ unselectAll()
+ findViewById(R.id.fab_add_file).visibility = View.GONE
+ findViewById(R.id.fab_import_file).visibility = View.GONE
+ findViewById(R.id.fab_import_file_from_other_volume).visibility = View.GONE
+ true
+ }
+ R.id.validate -> {
+ object : LoadingTask(this, R.string.loading_msg_copy){
+ override fun doTask(activity: AppCompatActivity) {
+ var failedItem: String? = null
+ for (element in filesToCopy) {
+ val originalPath = element.getFullPath()
+ failedItem = if (element.isDirectory) {
+ recursiveCopyDirectory(originalPath, currentDirectoryPath)
+ } else {
+ if (copyFile(originalPath, PathUtils.path_join(currentDirectoryPath, element.name))) null else originalPath
+ }
+ if (failedItem != null) {
+ stopTask {
+ ColoredAlertDialogBuilder(activity)
+ .setTitle(R.string.error)
+ .setMessage(getString(R.string.copy_failed, failedItem))
+ .setPositiveButton(R.string.ok, null)
+ .show()
+ }
+ break
+ }
+ }
+ if (failedItem == null) {
+ stopTask {
+ ColoredAlertDialogBuilder(activity)
+ .setTitle(getString(R.string.copy_success))
+ .setMessage(getString(R.string.copy_success_msg))
+ .setPositiveButton(R.string.ok, null)
+ .show()
+ }
+ }
+ }
+ override fun doFinally(activity: AppCompatActivity) {
+ cancelCopy()
+ unselectAll()
+ setCurrentPath(currentDirectoryPath)
+ }
+ }
+ true
+ }
+ R.id.delete -> {
val size = explorerAdapter.selectedItems.size
val dialog = ColoredAlertDialogBuilder(this)
dialog.setTitle(R.string.warning)
@@ -313,17 +382,16 @@ class ExplorerActivity : BaseExplorerActivity() {
dialog.show()
true
}
- R.id.explorer_menu_share -> {
+ R.id.share -> {
val paths: MutableList = ArrayList()
for (i in explorerAdapter.selectedItems) {
- val e = explorerElements[i]
- paths.add(PathUtils.path_join(currentDirectoryPath, e.name))
+ paths.add(explorerElements[i].getFullPath())
}
ExternalProvider.share(this, gocryptfsVolume, paths)
unselectAll()
true
}
- R.id.explorer_menu_decrypt -> {
+ R.id.decrypt -> {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
true
@@ -332,12 +400,86 @@ class ExplorerActivity : BaseExplorerActivity() {
}
}
- private fun importFileFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, full_path: String, output_dir: String): Boolean {
- val outputPath = PathUtils.path_join(output_dir, File(full_path).name)
+ private fun recursiveMapFiles(rootPath: String): MutableList {
+ val result = mutableListOf()
+ val explorerElements = gocryptfsVolume.list_dir(rootPath)
+ result.addAll(explorerElements)
+ for (e in explorerElements){
+ if (e.isDirectory){
+ result.addAll(recursiveMapFiles(e.getFullPath()))
+ }
+ }
+ return result
+ }
+
+ private fun cancelCopy() {
+ if (modeSelectLocation){
+ modeSelectLocation = false
+ findViewById(R.id.fab_add_file).visibility = View.VISIBLE
+ findViewById(R.id.fab_import_file).visibility = View.VISIBLE
+ findViewById(R.id.fab_import_file_from_other_volume).visibility = View.VISIBLE
+ filesToCopy.clear()
+ }
+ }
+
+ private fun copyFile(srcPath: String, dstPath: String): Boolean {
var success = true
- val srcHandleID = remote_gocryptfsVolume.open_read_mode(full_path)
+ val originalHandleId = gocryptfsVolume.open_read_mode(srcPath)
+ if (originalHandleId != -1){
+ val newHandleId = gocryptfsVolume.open_write_mode(dstPath)
+ if (newHandleId != -1){
+ var offset: Long = 0
+ val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
+ var length: Int
+ while (gocryptfsVolume.read_file(originalHandleId, offset, ioBuffer).also { length = it } > 0) {
+ val written = gocryptfsVolume.write_file(newHandleId, offset, ioBuffer, length).toLong()
+ if (written == length.toLong()) {
+ offset += written
+ } else {
+ success = false
+ break
+ }
+ }
+ gocryptfsVolume.close_file(newHandleId)
+ } else {
+ success = false
+ }
+ gocryptfsVolume.close_file(originalHandleId)
+ } else {
+ success = false
+ }
+ return success
+ }
+
+ private fun recursiveCopyDirectory(srcDirectoryPath: String, outputPath: String): String? {
+ val mappedElements = recursiveMapFiles(srcDirectoryPath)
+ val dstDirectoryPath = PathUtils.path_join(outputPath, File(srcDirectoryPath).name)
+ if (!gocryptfsVolume.path_exists(dstDirectoryPath)) {
+ if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
+ return dstDirectoryPath
+ }
+ }
+ for (e in mappedElements) {
+ val srcPath = e.getFullPath()
+ val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, srcPath))
+ if (e.isDirectory) {
+ if (!gocryptfsVolume.mkdir(dstPath)){
+ return srcPath
+ }
+ } else {
+ if (!copyFile(srcPath, dstPath)) {
+ return srcPath
+ }
+ }
+ }
+ return null
+ }
+
+ private fun importFileFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean {
+ var success = true
+ val srcHandleID = remote_gocryptfsVolume.open_read_mode(srcPath)
if (srcHandleID != -1) {
- val dstHandleID = gocryptfsVolume.open_write_mode(outputPath)
+ val dstHandleID = gocryptfsVolume.open_write_mode(dstPath)
if (dstHandleID != -1) {
var length: Int
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
@@ -358,22 +500,24 @@ class ExplorerActivity : BaseExplorerActivity() {
return success
}
- private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, output_dir: String): String? {
- val directoryPath = PathUtils.path_join(output_dir, File(remote_directory_path).name)
- if (!gocryptfsVolume.path_exists(directoryPath)) {
- if (!gocryptfsVolume.mkdir(directoryPath)) {
- return directoryPath
+ private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? {
+ val mappedElements = recursiveMapFiles(remote_directory_path)
+ val dstDirectoryPath = PathUtils.path_join(outputPath, File(remote_directory_path).name)
+ if (!gocryptfsVolume.path_exists(dstDirectoryPath)) {
+ if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
+ return dstDirectoryPath
}
}
- val explorerElements = remote_gocryptfsVolume.list_dir(remote_directory_path)
- for (e in explorerElements) {
- val fullPath = PathUtils.path_join(remote_directory_path, e.name)
+ for (e in mappedElements) {
+ val srcPath = e.getFullPath()
+ val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, srcPath))
if (e.isDirectory) {
- val failedItem = recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume, fullPath, directoryPath)
- failedItem?.let { return it }
+ if (!gocryptfsVolume.mkdir(dstPath)){
+ return srcPath
+ }
} else {
- if (!importFileFromOtherVolume(remote_gocryptfsVolume, fullPath, directoryPath)) {
- return fullPath
+ if (!importFileFromOtherVolume(remote_gocryptfsVolume, srcPath, dstPath)) {
+ return srcPath
}
}
}
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt
index efe46ec..a8e926d 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt
@@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.explorers
import android.content.Intent
import android.net.Uri
+import android.util.Log
import android.view.Menu
import android.view.MenuItem
import sushi.hardcore.droidfs.R
@@ -16,48 +17,48 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer_drop, menu)
handleMenuItems(menu)
- menu.findItem(R.id.explorer_menu_validate).isVisible = explorerAdapter.selectedItems.isEmpty()
+ menu.findItem(R.id.validate).isVisible = explorerAdapter.selectedItems.isEmpty()
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
- R.id.explorer_menu_validate -> {
+ R.id.validate -> {
val alertDialog = ColoredAlertDialogBuilder(this)
alertDialog.setCancelable(false)
alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() }
- var error_msg: String? = null
+ var errorMsg: String? = null
val extras = intent.extras
if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){
if (intent.action == Intent.ACTION_SEND) {
val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM)
- val output_path = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
- error_msg = if (gocryptfsVolume.import_file(this, uri, output_path)) null else getString(R.string.import_failed, output_path)
+ val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
+ errorMsg = if (gocryptfsVolume.import_file(this, uri, outputPath)) null else getString(R.string.import_failed, outputPath)
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
val uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)
if (uris != null){
for (uri in uris) {
- val output_path = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
- if (!gocryptfsVolume.import_file(this, uri, output_path)) {
- error_msg = getString(R.string.import_failed, output_path)
+ val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
+ if (!gocryptfsVolume.import_file(this, uri, outputPath)) {
+ errorMsg = getString(R.string.import_failed, outputPath)
break
}
}
} else {
- error_msg = getString(R.string.share_intent_parsing_failed)
+ errorMsg = getString(R.string.share_intent_parsing_failed)
}
} else {
- error_msg = getString(R.string.share_intent_parsing_failed)
+ errorMsg = getString(R.string.share_intent_parsing_failed)
}
} else {
- error_msg = getString(R.string.share_intent_parsing_failed)
+ errorMsg = getString(R.string.share_intent_parsing_failed)
}
- if (error_msg == null) {
+ if (errorMsg == null) {
alertDialog.setTitle(R.string.success_import)
alertDialog.setMessage(R.string.success_import_msg)
} else {
alertDialog.setTitle(R.string.error)
- alertDialog.setMessage(error_msg)
+ alertDialog.setMessage(errorMsg)
}
alertDialog.show()
true
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt
index 52f99c7..2dadd9b 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt
@@ -27,7 +27,7 @@ class ExplorerActivityPick : BaseExplorerActivity() {
setCurrentPath(full_path)
}
explorerElements[position].isParentFolder -> {
- setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
+ setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
}
else -> {
result_intent.putExtra("path", full_path)
@@ -43,19 +43,19 @@ class ExplorerActivityPick : BaseExplorerActivity() {
menuInflater.inflate(R.menu.explorer_pick, menu)
handleMenuItems(menu)
val any_item_selected = explorerAdapter.selectedItems.isNotEmpty()
- menu.findItem(R.id.explorer_menu_select_all).isVisible = any_item_selected
- menu.findItem(R.id.explorer_menu_validate).isVisible = any_item_selected
+ menu.findItem(R.id.select_all).isVisible = any_item_selected
+ menu.findItem(R.id.validate).isVisible = any_item_selected
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
- R.id.explorer_menu_select_all -> {
+ R.id.select_all -> {
explorerAdapter.selectAll()
invalidateOptionsMenu()
true
}
- R.id.explorer_menu_validate -> {
+ R.id.validate -> {
val paths = ArrayList()
val types = ArrayList()
for (i in explorerAdapter.selectedItems) {
@@ -79,7 +79,7 @@ class ExplorerActivityPick : BaseExplorerActivity() {
override fun closeVolumeOnDestroy() {
//don't close volume
- RestrictedFileProvider.wipeAll()
+ RestrictedFileProvider.wipeAll(this)
}
override fun closeVolumeOnUserExit() {
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt
index 4919385..5e81c2d 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt
@@ -1,8 +1,9 @@
package sushi.hardcore.droidfs.explorers
+import sushi.hardcore.droidfs.util.PathUtils
import java.util.*
-class ExplorerElement(val name: String, val elementType: Short, val size: Long, mtime: Long) {
+class ExplorerElement(val name: String, val elementType: Short, val size: Long, mtime: Long, private val parentPath: String) {
val mTime = Date((mtime * 1000).toString().toLong())
val isDirectory: Boolean
@@ -14,4 +15,7 @@ class ExplorerElement(val name: String, val elementType: Short, val size: Long,
val isRegularFile: Boolean
get() = elementType.toInt() == 1
+ fun getFullPath(): String {
+ return PathUtils.path_join(parentPath, name)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt
index ff2cd61..b7f1ce7 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt
@@ -4,6 +4,8 @@ import android.net.Uri
import com.google.android.exoplayer2.upstream.*
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.util.GocryptfsVolume
+import kotlin.math.ceil
+import kotlin.math.min
class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource {
private var handleID = -1
@@ -34,15 +36,20 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
if (fileOffset >= fileSize){
return -1
}
- val tmpBuff = if (fileOffset+readLength > fileSize){
- ByteArray((fileSize-fileOffset).toInt())
- } else {
- ByteArray(readLength)
+ var totalRead = 0
+ for (i in 0 until ceil(readLength.toDouble()/ConstValues.MAX_KERNEL_WRITE).toInt()){
+ val tmpReadLength = min(readLength-totalRead, ConstValues.MAX_KERNEL_WRITE)
+ val tmpBuff = if (fileOffset+tmpReadLength > fileSize){
+ ByteArray((fileSize-fileOffset).toInt())
+ } else {
+ ByteArray(tmpReadLength)
+ }
+ val read = gocryptfsVolume.read_file(handleID, fileOffset, tmpBuff)
+ System.arraycopy(tmpBuff, 0, buffer, offset+totalRead, read)
+ fileOffset += read
+ totalRead += read
}
- val read = gocryptfsVolume.read_file(handleID, fileOffset, tmpBuff)
- fileOffset += read
- System.arraycopy(tmpBuff, 0, buffer, offset, read)
- return read
+ return totalRead
}
class Factory(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource.Factory{
diff --git a/app/src/main/java/sushi/hardcore/droidfs/provider/RestrictedFileProvider.kt b/app/src/main/java/sushi/hardcore/droidfs/provider/RestrictedFileProvider.kt
index 7cbe02d..0f1bfa9 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/provider/RestrictedFileProvider.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/provider/RestrictedFileProvider.kt
@@ -2,12 +2,17 @@ package sushi.hardcore.droidfs.provider
import android.content.ContentProvider
import android.content.ContentValues
+import android.content.Context
import android.database.Cursor
import android.database.MatrixCursor
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.provider.MediaStore
import sushi.hardcore.droidfs.BuildConfig
+import sushi.hardcore.droidfs.util.SQLUtil.appendSelectionArgs
+import sushi.hardcore.droidfs.util.SQLUtil.concatenateWhere
import sushi.hardcore.droidfs.util.Wiper
import java.io.File
import java.util.*
@@ -15,48 +20,105 @@ import java.util.regex.Pattern
class RestrictedFileProvider: ContentProvider() {
companion object {
+ private const val DB_NAME = "temporary_files.db"
+ private const val TABLE_FILES = "files"
+ private const val DB_VERSION = 3
+ private var dbHelper: RestrictedDatabaseHelper? = null
private const val AUTHORITY = BuildConfig.APPLICATION_ID + ".temporary_provider"
private val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
const val TEMPORARY_FILES_DIR_NAME = "temp"
private val UUID_PATTERN = Pattern.compile("[a-fA-F0-9-]+")
private lateinit var tempFilesDir: File
- private val tempFiles = mutableMapOf()
- class TemporaryFile(val fileName: String, val file: File)
+ internal class TemporaryFileColumns {
+ companion object {
+ const val COLUMN_UUID = "uuid"
+ const val COLUMN_NAME = "name"
+ }
+ }
+
+ internal class RestrictedDatabaseHelper(context: Context?): SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
+ override fun onCreate(db: SQLiteDatabase) {
+ db.execSQL(
+ "CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
+ TemporaryFileColumns.COLUMN_UUID + " TEXT PRIMARY KEY, " +
+ TemporaryFileColumns.COLUMN_NAME + " TEXT" +
+ ");"
+ )
+ }
+
+ override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
+ if (oldVersion == 1) {
+ db.execSQL("DROP TABLE IF EXISTS files")
+ db.execSQL(
+ "CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
+ TemporaryFileColumns.COLUMN_UUID + " TEXT PRIMARY KEY, " +
+ TemporaryFileColumns.COLUMN_NAME + " TEXT" +
+ ");"
+ )
+ }
+ }
+ }
fun newFile(fileName: String): Uri? {
val uuid = UUID.randomUUID().toString()
val file = File(tempFilesDir, uuid)
return if (file.createNewFile()){
- tempFiles[uuid] = TemporaryFile(fileName, file)
- Uri.withAppendedPath(CONTENT_URI, uuid)
+ val contentValues = ContentValues()
+ contentValues.put(TemporaryFileColumns.COLUMN_UUID, uuid)
+ contentValues.put(TemporaryFileColumns.COLUMN_NAME, fileName)
+ if (dbHelper?.writableDatabase?.insert(TABLE_FILES, null, contentValues)?.toInt() != -1){
+ Uri.withAppendedPath(CONTENT_URI, uuid)
+ } else {
+ null
+ }
} else {
null
}
}
- fun wipeAll() {
+ fun wipeAll(context: Context) {
tempFilesDir.listFiles()?.let{
for (file in it) {
Wiper.wipe(file)
}
}
+ context.deleteDatabase(DB_NAME)
}
- private fun getFileFromUri(uri: Uri): TemporaryFile? {
+ private fun isValidUUID(uuid: String): Boolean {
+ return UUID_PATTERN.matcher(uuid).matches()
+ }
+
+ private fun getUuidFromUri(uri: Uri): String? {
val uuid = uri.lastPathSegment
if (uuid != null) {
- if (UUID_PATTERN.matcher(uuid).matches()) {
- return tempFiles[uuid]
+ if (isValidUUID(uuid)) {
+ return uuid
}
}
return null
}
+
+ private fun getFileFromUUID(uuid: String): File? {
+ if (isValidUUID(uuid)){
+ return File(tempFilesDir, uuid)
+ }
+ return null
+ }
+
+ private fun getFileFromUri(uri: Uri): File? {
+ getUuidFromUri(uri)?.let {
+ return getFileFromUUID(it)
+ }
+ return null
+ }
}
override fun onCreate(): Boolean {
context?.let {
+ dbHelper = RestrictedDatabaseHelper(it)
tempFilesDir = File(it.cacheDir, TEMPORARY_FILES_DIR_NAME)
return tempFilesDir.mkdirs()
}
@@ -73,28 +135,47 @@ class RestrictedFileProvider: ContentProvider() {
override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? {
val temporaryFile = getFileFromUri(uri)
- if (temporaryFile != null) {
- val cursor = MatrixCursor(
- arrayOf(
- MediaStore.MediaColumns.DISPLAY_NAME,
- MediaStore.MediaColumns.SIZE
- )
- )
- cursor.newRow()
- .add(temporaryFile.fileName)
- .add(temporaryFile.file.length())
- return cursor
+ temporaryFile?.let{
+ val fileName =
+ dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_NAME), TemporaryFileColumns.COLUMN_UUID + "=?", arrayOf(uri.lastPathSegment), null, null, null)
+ fileName?.let{
+ if (fileName.moveToNext()) {
+ val cursor = MatrixCursor(
+ arrayOf(
+ MediaStore.MediaColumns.DISPLAY_NAME,
+ MediaStore.MediaColumns.SIZE
+ )
+ )
+ cursor.newRow()
+ .add(fileName.getString(0))
+ .add(temporaryFile.length())
+ fileName.close()
+ return cursor
+ }
+ fileName.close()
+ }
}
return null
}
- override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
- val temporaryFile = getFileFromUri(uri)
- if (temporaryFile != null) {
- Wiper.wipe(temporaryFile.file)
- tempFiles.remove(uri.lastPathSegment)
+ override fun delete(uri: Uri, givenSelection: String?, givenSelectionArgs: Array?): Int {
+ val uuid = getUuidFromUri(uri)
+ uuid?.let{
+ val selection = concatenateWhere(givenSelection ?: "" , TemporaryFileColumns.COLUMN_UUID + "=?")
+ val selectionArgs = appendSelectionArgs(givenSelectionArgs, arrayOf(it))
+
+ val files = dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_UUID), selection, selectionArgs, null, null, null)
+ if (files != null) {
+ while (files.moveToNext()) {
+ getFileFromUUID(files.getString(0))?.let { file ->
+ Wiper.wipe(file)
+ }
+ }
+ files.close()
+ return dbHelper?.writableDatabase?.delete(TABLE_FILES, selection, selectionArgs) ?: 0
+ }
}
- return 1
+ return 0
}
override fun getType(uri: Uri): String {
@@ -104,7 +185,7 @@ class RestrictedFileProvider: ContentProvider() {
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
if (("w" in mode && callingPackage == BuildConfig.APPLICATION_ID) || "w" !in mode) {
getFileFromUri(uri)?.let{
- return ParcelFileDescriptor.open(it.file, ParcelFileDescriptor.parseMode(mode))
+ return ParcelFileDescriptor.open(it, ParcelFileDescriptor.parseMode(mode))
}
} else {
throw SecurityException("Read-only access")
diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt b/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt
index a8e00c7..0081dc0 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt
@@ -9,7 +9,6 @@ import sushi.hardcore.droidfs.provider.RestrictedFileProvider
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
import java.net.URLConnection
-import java.util.*
import kotlin.collections.ArrayList
object ExternalProvider {
@@ -102,13 +101,13 @@ object ExternalProvider {
fun removeFiles(context: Context) {
Thread{
- val wiped = ArrayList()
- for (uri in storedFiles) {
- if (Wiper.wipe(context, uri) == null){
- wiped.add(uri)
+ val success = ArrayList()
+ for (uri in storedFiles){
+ if (context.contentResolver.delete(uri, null, null) == 1){
+ success.add(uri)
}
}
- for (uri in wiped){
+ for (uri in success){
storedFiles.remove(uri)
}
}.start()
diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt
index 13f8a7a..f1f101e 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt
@@ -2,7 +2,6 @@ package sushi.hardcore.droidfs.util
import android.content.Context
import android.net.Uri
-import android.util.Log
import sushi.hardcore.droidfs.explorers.ExplorerElement
import java.io.*
@@ -93,10 +92,10 @@ class GocryptfsVolume(var sessionID: Int) {
fun export_file(handleID: Int, os: OutputStream): Boolean {
var offset: Long = 0
- val io_buffer = ByteArray(DefaultBS)
+ val ioBuffer = ByteArray(DefaultBS)
var length: Int
- while (native_read_file(sessionID, handleID, offset, io_buffer).also { length = it } > 0){
- os.write(io_buffer, 0, length)
+ while (native_read_file(sessionID, handleID, offset, ioBuffer).also { length = it } > 0){
+ os.write(ioBuffer, 0, length)
offset += length.toLong()
}
os.close()
@@ -105,10 +104,10 @@ class GocryptfsVolume(var sessionID: Int) {
fun export_file(src_path: String, os: OutputStream): Boolean {
var success = false
- val src_handleID = open_read_mode(src_path)
- if (src_handleID != -1) {
- success = export_file(src_handleID, os)
- close_file(src_handleID)
+ val srcHandleId = open_read_mode(src_path)
+ if (srcHandleId != -1) {
+ success = export_file(srcHandleId, os)
+ close_file(srcHandleId)
}
return success
}
@@ -127,10 +126,10 @@ class GocryptfsVolume(var sessionID: Int) {
fun import_file(inputStream: InputStream, handleID: Int): Boolean {
var offset: Long = 0
- val io_buffer = ByteArray(DefaultBS)
+ val ioBuffer = ByteArray(DefaultBS)
var length: Int
- while (inputStream.read(io_buffer).also { length = it } > 0) {
- val written = native_write_file(sessionID, handleID, offset, io_buffer, length).toLong()
+ while (inputStream.read(ioBuffer).also { length = it } > 0) {
+ val written = native_write_file(sessionID, handleID, offset, ioBuffer, length).toLong()
if (written == length.toLong()) {
offset += written
} else {
@@ -145,10 +144,10 @@ class GocryptfsVolume(var sessionID: Int) {
fun import_file(inputStream: InputStream, dst_path: String): Boolean {
var success = false
- val dst_handleID = open_write_mode(dst_path)
- if (dst_handleID != -1) {
- success = import_file(inputStream, dst_handleID)
- close_file(dst_handleID)
+ val dstHandleId = open_write_mode(dst_path)
+ if (dstHandleId != -1) {
+ success = import_file(inputStream, dstHandleId)
+ close_file(dstHandleId)
}
return success
}
diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java
index 8647394..523fc52 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java
+++ b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java
@@ -14,7 +14,7 @@ import java.text.DecimalFormat;
public class PathUtils {
- public static String get_parent_path(String path){
+ public static String getParentPath(String path){
if (path.endsWith("/")){
String a = path.substring(0, path.length()-2);
if (a.contains("/")){
@@ -44,16 +44,22 @@ public class PathUtils {
return result.substring(0, result.length()-1);
}
+ public static String getRelativePath(String parentPath, String childPath){
+ return childPath.substring(parentPath.length()+1);
+ }
+
public static String getFilenameFromURI(Context context, Uri uri){
String result = null;
if (uri.getScheme().equals("content")){
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()){
- result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ if (cursor != null){
+ try {
+ if (cursor.moveToFirst()){
+ result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
}
- } finally {
- cursor.close();
}
}
if (result == null){
diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/SQLUtil.kt b/app/src/main/java/sushi/hardcore/droidfs/util/SQLUtil.kt
new file mode 100644
index 0000000..b727298
--- /dev/null
+++ b/app/src/main/java/sushi/hardcore/droidfs/util/SQLUtil.kt
@@ -0,0 +1,24 @@
+package sushi.hardcore.droidfs.util
+
+object SQLUtil {
+ @JvmStatic
+ fun concatenateWhere(a: String, b: String): String {
+ if (a.isEmpty()) {
+ return b
+ }
+ return if (b.isEmpty()) {
+ a
+ } else "($a) AND ($b)"
+ }
+
+ @JvmStatic
+ fun appendSelectionArgs(originalValues: Array?, newValues: Array): Array {
+ if (originalValues == null || originalValues.isEmpty()) {
+ return newValues
+ }
+ val result = Array(originalValues.size + newValues.size){ "it = $it" }
+ System.arraycopy(originalValues, 0, result, 0, originalValues.size)
+ System.arraycopy(newValues, 0, result, originalValues.size, newValues.size)
+ return result
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ThemeColor.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ThemeColor.kt
index 8418c79..2b4abcb 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ThemeColor.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ThemeColor.kt
@@ -1,8 +1,6 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
-import android.graphics.Color
-import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.preference.Preference
@@ -20,9 +18,8 @@ object ThemeColor {
for (i in 0 until preference.preferenceCount) {
tintPreferenceIcons(preference.getPreference(i), color)
}
- } else {
- val icon: Drawable = preference.icon
- DrawableCompat.setTint(icon, color)
+ } else if (preference.icon != null) {
+ DrawableCompat.setTint(preference.icon, color)
}
}
}
diff --git a/app/src/main/native/gocryptfs_jni.c b/app/src/main/native/gocryptfs_jni.c
index bfba488..fae1cec 100644
--- a/app/src/main/native/gocryptfs_jni.c
+++ b/app/src/main/native/gocryptfs_jni.c
@@ -227,7 +227,7 @@ Java_sushi_hardcore_droidfs_util_GocryptfsVolume_native_1list_1dir(JNIEnv *env,
jmethodID java_util_ArrayList_add = (*env)->GetMethodID(env, java_util_ArrayList, "add", "(Ljava/lang/Object;)Z");
jclass classExplorerElement = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "sushi/hardcore/droidfs/explorers/ExplorerElement"));
- jmethodID classExplorerElement_init = (*env)->GetMethodID(env, classExplorerElement, "", "(Ljava/lang/String;SJJ)V");
+ jmethodID classExplorerElement_init = (*env)->GetMethodID(env, classExplorerElement, "", "(Ljava/lang/String;SJJLjava/lang/String;)V");
jobject element_list = (*env)->NewObject(env, java_util_ArrayList, java_util_ArrayList_init, elements.r2);
unsigned int c = 0;
@@ -254,7 +254,7 @@ Java_sushi_hardcore_droidfs_util_GocryptfsVolume_native_1list_1dir(JNIEnv *env,
type = 1; //regular file
}
jstring jname = (*env)->NewStringUTF(env, name);
- jobject explorerElement = (*env)->NewObject(env, classExplorerElement, classExplorerElement_init, jname, type, (long long)attrs.r0, attrs.r1);
+ jobject explorerElement = (*env)->NewObject(env, classExplorerElement, classExplorerElement_init, jname, type, (long long)attrs.r0, attrs.r1, jplain_dir);
(*env)->CallBooleanMethod(env, element_list, java_util_ArrayList_add, explorerElement);
c += name_len+1;
}
diff --git a/app/src/main/res/drawable/icon_copy.xml b/app/src/main/res/drawable/icon_copy.xml
new file mode 100644
index 0000000..1a638c9
--- /dev/null
+++ b/app/src/main/res/drawable/icon_copy.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/icon_decrypt.xml b/app/src/main/res/drawable/icon_decrypt.xml
index f48247a..f954e2c 100644
--- a/app/src/main/res/drawable/icon_decrypt.xml
+++ b/app/src/main/res/drawable/icon_decrypt.xml
@@ -1,12 +1,4 @@
-
-
-
-
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/drawable/icon_encrypt.xml b/app/src/main/res/drawable/icon_encrypt.xml
new file mode 100644
index 0000000..46412c1
--- /dev/null
+++ b/app/src/main/res/drawable/icon_encrypt.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/icon_github.xml b/app/src/main/res/drawable/icon_github.xml
new file mode 100644
index 0000000..0ffbbbc
--- /dev/null
+++ b/app/src/main/res/drawable/icon_github.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/icon_transfert.xml b/app/src/main/res/drawable/icon_transfert.xml
new file mode 100644
index 0000000..f867984
--- /dev/null
+++ b/app/src/main/res/drawable/icon_transfert.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/logo.png b/app/src/main/res/drawable/logo.png
index 127b9cc..426babc 100644
Binary files a/app/src/main/res/drawable/logo.png and b/app/src/main/res/drawable/logo.png differ
diff --git a/app/src/main/res/layout/activity_explorer.xml b/app/src/main/res/layout/activity_explorer.xml
index 81550ca..586737e 100644
--- a/app/src/main/res/layout/activity_explorer.xml
+++ b/app/src/main/res/layout/activity_explorer.xml
@@ -47,37 +47,39 @@
app:menu_labels_style="@style/fab_label">
diff --git a/app/src/main/res/layout/activity_explorer_drop.xml b/app/src/main/res/layout/activity_explorer_drop.xml
index ce8ea80..d723756 100644
--- a/app/src/main/res/layout/activity_explorer_drop.xml
+++ b/app/src/main/res/layout/activity_explorer_drop.xml
@@ -45,12 +45,12 @@
app:menu_labels_style="@style/fab_label">
diff --git a/app/src/main/res/menu/explorer.xml b/app/src/main/res/menu/explorer.xml
index 67e320c..9b98333 100644
--- a/app/src/main/res/menu/explorer.xml
+++ b/app/src/main/res/menu/explorer.xml
@@ -3,52 +3,63 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+
+
-
-
+ android:title="@string/decrypt"/>
+
+
+ android:id="@+id/validate"
+ app:showAsAction="ifRoom"
+ android:visible="false"
+ android:icon="@drawable/icon_check"/>
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/menu/explorer_drop.xml b/app/src/main/res/menu/explorer_drop.xml
index 6ee8a12..3577a36 100644
--- a/app/src/main/res/menu/explorer_drop.xml
+++ b/app/src/main/res/menu/explorer_drop.xml
@@ -3,31 +3,30 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+ android:icon="@drawable/icon_sort"/>
-
\ No newline at end of file
+
diff --git a/app/src/main/res/menu/explorer_pick.xml b/app/src/main/res/menu/explorer_pick.xml
index c19758c..d01e433 100644
--- a/app/src/main/res/menu/explorer_pick.xml
+++ b/app/src/main/res/menu/explorer_pick.xml
@@ -3,32 +3,31 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+ android:icon="@drawable/icon_sort"/>
-
\ No newline at end of file
+
diff --git a/app/src/main/res/mipmap/ic_launcher.png b/app/src/main/res/mipmap/ic_launcher.png
deleted file mode 100644
index 0fd4976..0000000
Binary files a/app/src/main/res/mipmap/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/mipmap/icon_launcher.png b/app/src/main/res/mipmap/icon_launcher.png
new file mode 100644
index 0000000..164d6d7
Binary files /dev/null and b/app/src/main/res/mipmap/icon_launcher.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 8d7955d..28d12b9 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -12,7 +12,6 @@
#FFFFFF
#5B5A5C
#66666666
- #FFFFFF
#111111
#FFFFFF
#6CC341
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e16c41a..6727659 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,7 +21,7 @@
The selected files have been successfully imported.
Import of %1$s failed.
Export of %1$s failed.
- Export Successful !
+ Export successful !
The selected files have been successfully exported.
Deletion of %1$s failed
Passwords don\'t match
@@ -130,4 +130,13 @@
Wiping original files…
Exporting files…
Unable to access this file
+ About
+ GitHub
+ Want to read the documentation, request feature, report bug, read the source code… Check the DroidFS\'s repository !
+ Share
+ Decrypt
+ Copying selected items...
+ Copy of %1$s failed.
+ The selected items have been successfully copied.
+ Copy successful !
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index e802898..8c317c4 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -24,11 +24,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file