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