Explorer loading dialogs & Logo fix

This commit is contained in:
Hardcore Sushi 2020-07-28 22:25:10 +02:00
parent 2571849bc3
commit 34d7f19927
19 changed files with 482 additions and 455 deletions

View File

@ -15,7 +15,7 @@ android {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.1.0"
versionName "1.1.1"
ndk {
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'

View File

@ -3,7 +3,6 @@ package sushi.hardcore.droidfs
import android.content.SharedPreferences
import android.os.Bundle
import android.view.WindowManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import com.jaredrummler.cyanea.app.CyaneaAppCompatActivity
@ -30,8 +29,4 @@ open class BaseActivity: CyaneaAppCompatActivity() {
}
}
}
protected fun toastFromThread(stringId: Int){
runOnUiThread { Toast.makeText(this, stringId, Toast.LENGTH_SHORT).show() }
}
}

View File

@ -8,8 +8,8 @@ import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.AdapterView.OnItemClickListener
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_change_password.*
import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path
import kotlinx.android.synthetic.main.activity_change_password.checkbox_save_password
@ -18,10 +18,7 @@ import kotlinx.android.synthetic.main.activity_change_password.saved_path_listvi
import kotlinx.android.synthetic.main.toolbar.*
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.util.*
@ -59,8 +56,11 @@ class ChangePasswordActivity : BaseActivity() {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (sharedPrefs.getString(s.toString(), null) == null) {
edit_old_password.hint = null
edit_old_password.isEnabled = true
} else {
edit_old_password.text = null
edit_old_password.hint = getString(R.string.hash_saved_hint)
edit_old_password.isEnabled = false
}
}
})
@ -97,21 +97,12 @@ class ChangePasswordActivity : BaseActivity() {
}
private fun changePassword(givenHash: ByteArray?){
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
dialogTextMessage.text = getString(R.string.loading_msg_change_password)
val dialogLoading = ColoredAlertDialogBuilder(this)
.setView(dialogLoadingView)
.setTitle(R.string.loading)
.setCancelable(false)
.create()
dialogLoading.show()
Thread {
object : LoadingTask(this, R.string.loading_msg_change_password){
override fun doTask(activity: AppCompatActivity) {
val newPassword = edit_new_password.text.toString().toCharArray()
val newPasswordConfirm = edit_new_password_confirm.text.toString().toCharArray()
if (!newPassword.contentEquals(newPasswordConfirm)) {
dialogLoading.dismiss()
toastFromThread(R.string.passwords_mismatch)
stopTaskWithToast(R.string.passwords_mismatch)
} else {
val oldPassword = edit_old_password.text.toString().toCharArray()
var returnedHash: ByteArray? = null
@ -122,8 +113,9 @@ class ChangePasswordActivity : BaseActivity() {
if (givenHash == null){
val cipherText = sharedPrefs.getString(rootCipherDir, null)
if (cipherText != null){ //password hash saved
dialogLoading.dismiss()
stopTask {
fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword)
}
changePasswordImmediately = false
}
}
@ -144,21 +136,18 @@ class ChangePasswordActivity : BaseActivity() {
editor.apply()
}
if (checkbox_save_password.isChecked && returnedHash != null){
dialogLoading.dismiss()
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ ->
onPasswordChanged()
stopTask { onPasswordChanged() }
}
continueImmediately = false
}
}
if (continueImmediately){
dialogLoading.dismiss()
runOnUiThread { onPasswordChanged() }
stopTask { onPasswordChanged() }
}
} else {
dialogLoading.dismiss()
runOnUiThread {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(R.string.change_password_failed)
.setPositiveButton(R.string.ok, null)
@ -170,7 +159,8 @@ class ChangePasswordActivity : BaseActivity() {
}
Arrays.fill(newPassword, 0.toChar())
Arrays.fill(newPasswordConfirm, 0.toChar())
}.start()
}
}
}
private fun onPasswordChanged(){

View File

@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_create.*
import kotlinx.android.synthetic.main.activity_create.checkbox_remember_path
import kotlinx.android.synthetic.main.activity_create.checkbox_save_password
@ -14,10 +14,7 @@ import kotlinx.android.synthetic.main.activity_create.edit_volume_path
import kotlinx.android.synthetic.main.toolbar.*
import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
import java.util.*
@ -64,21 +61,12 @@ class CreateActivity : BaseActivity() {
}
fun onClickCreate(view: View?) {
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
dialogTextMessage.text = getString(R.string.loading_msg_create)
val dialogLoading = ColoredAlertDialogBuilder(this)
.setView(dialogLoadingView)
.setTitle(R.string.loading)
.setCancelable(false)
.create()
dialogLoading.show()
Thread {
object: LoadingTask(this, R.string.loading_msg_create){
override fun doTask(activity: AppCompatActivity) {
val password = edit_password.text.toString().toCharArray()
val passwordConfirm = edit_password_confirm.text.toString().toCharArray()
if (!password.contentEquals(passwordConfirm)) {
dialogLoading.dismiss()
toastFromThread(R.string.passwords_mismatch)
stopTaskWithToast(R.string.passwords_mismatch)
} else {
rootCipherDir = edit_volume_path.text.toString()
val volumePathFile = File(rootCipherDir)
@ -87,8 +75,7 @@ class CreateActivity : BaseActivity() {
if (volumePathFile.mkdirs()) {
goodDirectory = true
} else {
dialogLoading.dismiss()
toastFromThread(R.string.error_mkdir)
stopTaskWithToast(R.string.error_mkdir)
}
} else {
val dirContent = volumePathFile.list()
@ -96,12 +83,10 @@ class CreateActivity : BaseActivity() {
if (dirContent.isEmpty()) {
goodDirectory = true
} else {
dialogLoading.dismiss()
toastFromThread(R.string.dir_not_empty)
stopTaskWithToast(R.string.dir_not_empty)
}
} else {
dialogLoading.dismiss()
toastFromThread(R.string.listdir_null_error_msg)
stopTaskWithToast(R.string.listdir_null_error_msg)
}
}
if (goodDirectory) {
@ -127,25 +112,21 @@ class CreateActivity : BaseActivity() {
}
editor.apply()
if (checkbox_save_password.isChecked && returnedHash != null){
dialogLoading.dismiss()
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ ->
runOnUiThread { startExplorer() }
stopTask { startExplorer() }
}
startExplorerImmediately = false
}
}
if (startExplorerImmediately){
dialogLoading.dismiss()
runOnUiThread { startExplorer() }
stopTask { startExplorer() }
}
} else {
dialogLoading.dismiss()
toastFromThread(R.string.open_volume_failed)
stopTaskWithToast(R.string.open_volume_failed)
}
} else {
dialogLoading.dismiss()
runOnUiThread {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(R.string.create_volume_failed)
.setPositiveButton(R.string.ok, null)
@ -156,7 +137,8 @@ class CreateActivity : BaseActivity() {
}
Arrays.fill(password, 0.toChar())
Arrays.fill(passwordConfirm, 0.toChar())
}.start()
}
}
}
private fun startExplorer(){

View File

@ -7,6 +7,8 @@ import android.os.Bundle
import android.view.View
import android.widget.AdapterView.OnItemClickListener
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_open.checkbox_remember_path
import kotlinx.android.synthetic.main.activity_open.checkbox_save_password
import kotlinx.android.synthetic.main.activity_open.edit_password
@ -18,10 +20,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
import java.util.*
@ -83,20 +82,11 @@ class OpenActivity : BaseActivity() {
}
fun onClickOpen(view: View?) {
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
dialogTextMessage.text = getString(R.string.loading_msg_open)
val dialogLoading = ColoredAlertDialogBuilder(this)
.setView(dialogLoadingView)
.setTitle(R.string.loading)
.setCancelable(false)
.create()
dialogLoading.show()
Thread {
object : LoadingTask(this, R.string.loading_msg_open){
override fun doTask(activity: AppCompatActivity) {
rootCipherDir = edit_volume_path.text.toString() //fresh get in case of manual rewrite
if (rootCipherDir.isEmpty()) {
dialogLoading.dismiss()
toastFromThread(R.string.enter_volume_path)
stopTaskWithToast(R.string.enter_volume_path)
} else {
val password = edit_password.text.toString().toCharArray()
var returnedHash: ByteArray? = null
@ -109,23 +99,18 @@ class OpenActivity : BaseActivity() {
if (checkbox_remember_path.isChecked) {
savedVolumesAdapter.addVolumePath(rootCipherDir)
if (checkbox_save_password.isChecked && returnedHash != null){
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir) { success ->
dialogLoading.dismiss()
if (success){
startExplorer()
}
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir) { _ ->
stopTask { startExplorer() }
}
startExplorerImmediately = false
}
}
if (startExplorerImmediately){
dialogLoading.dismiss()
startExplorer()
stopTask { startExplorer() }
}
} else {
dialogLoading.dismiss()
runOnUiThread {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.open_volume_failed)
.setMessage(R.string.open_volume_failed_msg)
.setPositiveButton(R.string.ok, null)
@ -134,22 +119,29 @@ class OpenActivity : BaseActivity() {
}
Arrays.fill(password, 0.toChar())
}
}.start()
}
}
}
private fun openUsingPasswordHash(passwordHash: ByteArray){
object : LoadingTask(this, R.string.loading_msg_open){
override fun doTask(activity: AppCompatActivity) {
sessionID = GocryptfsVolume.init(rootCipherDir, null, passwordHash, null)
if (sessionID != -1){
startExplorer()
stopTask { startExplorer() }
} else {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.open_volume_failed)
.setMessage(R.string.open_failed_hash_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
Arrays.fill(passwordHash, 0)
}
}
}
private fun startExplorer() {
var explorerIntent: Intent? = null

View File

@ -133,6 +133,11 @@ open class BaseExplorerActivity : BaseActivity() {
invalidateOptionsMenu()
}
protected fun unselectAll(){
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
}
private fun sortExplorerElements() {
when (sortModesValues[currentSortModeIndex]) {
"name" -> {
@ -199,8 +204,7 @@ open class BaseExplorerActivity : BaseActivity() {
setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
}
} else {
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
unselectAll()
}
}
@ -287,8 +291,7 @@ open class BaseExplorerActivity : BaseActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
unselectAll()
true
}
R.id.explorer_menu_sort -> {
@ -331,8 +334,7 @@ open class BaseExplorerActivity : BaseActivity() {
R.id.explorer_menu_external_open -> {
if (usf_open){
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
unselectAll()
}
true
}

View File

@ -8,15 +8,14 @@ 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.FloatingActionMenu
import kotlinx.android.synthetic.main.activity_explorer.*
import sushi.hardcore.droidfs.OpenActivity
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.ExternalProvider
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
import java.util.*
@ -77,7 +76,7 @@ class ExplorerActivity : BaseExplorerActivity() {
fun onClickAddFile(view: View?) {
fam_explorer.close(true)
val i = Intent(Intent.ACTION_GET_CONTENT)
val i = Intent(Intent.ACTION_OPEN_DOCUMENT)
i.type = "*/*"
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
i.addCategory(Intent.CATEGORY_OPENABLE)
@ -95,6 +94,8 @@ class ExplorerActivity : BaseExplorerActivity() {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_FILES_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
object : LoadingTask(this, R.string.loading_msg_import){
override fun doTask(activity: AppCompatActivity) {
val uris: MutableList<Uri> = ArrayList()
val singleUri = data.data
if (singleUri == null) { //multiples choices
@ -107,60 +108,77 @@ class ExplorerActivity : BaseExplorerActivity() {
} else {
uris.add(singleUri)
}
if (uris.isNotEmpty()){
var success = true
for (uri in uris) {
val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))
contentResolver.openInputStream(uri)?.let {
success = gocryptfsVolume.import_file(it, dstPath)
}
if (!success) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, uri))
.setPositiveButton(R.string.ok, null)
.show()
}
break
}
}
if (success) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.success_import)
.setMessage("""
${getString(R.string.success_import_msg)}
${getString(R.string.ask_for_wipe)}
""".trimIndent())
.setPositiveButton(R.string.yes) { _, _ ->
object : LoadingTask(activity, R.string.loading_msg_wipe){
override fun doTask(activity: AppCompatActivity) {
success = true
for (uri in uris) {
if (!Wiper.wipe(this, uri)) {
ColoredAlertDialogBuilder(this)
val errorMsg = Wiper.wipe(activity, uri)
if (errorMsg != null) {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.wipe_failed, uri))
.setMessage(getString(R.string.wipe_failed, errorMsg))
.setPositiveButton(R.string.ok, null)
.show()
}
success = false
break
}
}
if (success) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.wipe_successful)
.setMessage(R.string.wipe_success_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
}
.setNegativeButton(getString(R.string.no), null)
.show()
}
}
}
override fun doFinally(activity: AppCompatActivity){
setCurrentPath(currentDirectoryPath)
}
}
}
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
object : LoadingTask(this, R.string.loading_msg_export){
override fun doTask(activity: AppCompatActivity) {
val uri = data.data
val outputDir = PathUtils.getFullPathFromTreeUri(uri, this)
val outputDir = PathUtils.getFullPathFromTreeUri(uri, activity)
var failedItem: String? = null
for (i in explorerAdapter.selectedItems) {
val element = explorerAdapter.getItem(i)
@ -171,26 +189,35 @@ class ExplorerActivity : BaseExplorerActivity() {
if (gocryptfsVolume.export_file(fullPath, PathUtils.path_join(outputDir, element.name))) null else fullPath
}
if (failedItem != null) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.export_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
break
}
}
if (failedItem == null) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.success_export)
.setMessage(R.string.success_export_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
}
override fun doFinally(activity: AppCompatActivity) {
unselectAll()
}
}
}
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
object : LoadingTask(this, R.string.loading_msg_import){
override fun doTask(activity: AppCompatActivity) {
val remoteSessionID = data.getIntExtra("sessionID", -1)
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID)
val path = data.getStringExtra("path")
@ -214,23 +241,31 @@ class ExplorerActivity : BaseExplorerActivity() {
failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path
}
if (failedItem == null) {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.success_import)
.setMessage(R.string.success_import_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
}
remoteGocryptfsVolume.close()
}
override fun doFinally(activity: AppCompatActivity) {
setCurrentPath(currentDirectoryPath)
}
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu)
@ -285,8 +320,7 @@ class ExplorerActivity : BaseExplorerActivity() {
paths.add(PathUtils.path_join(currentDirectoryPath, e.name))
}
ExternalProvider.share(this, gocryptfsVolume, paths)
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
unselectAll()
true
}
R.id.explorer_menu_decrypt -> {
@ -407,8 +441,7 @@ class ExplorerActivity : BaseExplorerActivity() {
break
}
}
explorerAdapter.unSelectAll()
invalidateOptionsMenu()
unselectAll()
setCurrentPath(currentDirectoryPath) //refresh
}
}

View File

@ -1,33 +0,0 @@
package sushi.hardcore.droidfs.fingerprint_stuff
import android.content.Context
import android.hardware.fingerprint.FingerprintManager
import android.os.Build
import android.os.CancellationSignal
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.M)
class FingerprintHandler(private val context: Context) : FingerprintManager.AuthenticationCallback(){
private lateinit var cancellationSignal: CancellationSignal
private lateinit var onTouched: (resultCode: onTouchedResultCodes) -> Unit
fun startAuth(fingerprintManager: FingerprintManager, cryptoObject: FingerprintManager.CryptoObject, onTouched: (resultCode: onTouchedResultCodes) -> Unit){
cancellationSignal = CancellationSignal()
this.onTouched = onTouched
fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null)
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
onTouched(onTouchedResultCodes.SUCCEED)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
onTouched(onTouchedResultCodes.ERROR)
}
override fun onAuthenticationFailed() {
onTouched(onTouchedResultCodes.FAILED)
}
}

View File

@ -1,7 +0,0 @@
package sushi.hardcore.droidfs.fingerprint_stuff
enum class onTouchedResultCodes {
SUCCEED,
FAILED,
ERROR
}

View File

@ -3,12 +3,14 @@ package sushi.hardcore.droidfs.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.droidfs.R
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 {
private const val content_type_all = "*/*"
@ -37,23 +39,27 @@ object ExternalProvider {
return Pair(tmpFileUri, getContentType(fileName, previous_content_type))
}
}
ColoredAlertDialogBuilder(context)
.setTitle(R.string.error)
.setMessage(context.getString(R.string.export_failed, file_path))
.setPositiveButton(R.string.ok, null)
.show()
return Pair(null, null)
}
fun share(context: Context, gocryptfsVolume: GocryptfsVolume, file_paths: List<String>) {
fun share(activity: AppCompatActivity, gocryptfsVolume: GocryptfsVolume, file_paths: List<String>) {
object : LoadingTask(activity, R.string.loading_msg_export){
override fun doTask(activity: AppCompatActivity) {
var contentType: String? = null
val uris = ArrayList<Uri>()
for (path in file_paths) {
val result = exportFile(context, gocryptfsVolume, path, contentType)
val result = exportFile(activity, gocryptfsVolume, path, contentType)
contentType = if (result.first != null) {
uris.add(result.first!!)
result.second
} else {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(activity.getString(R.string.export_failed, path))
.setPositiveButton(R.string.ok, null)
.show()
}
return
}
}
@ -66,26 +72,45 @@ object ExternalProvider {
shareIntent.action = Intent.ACTION_SEND_MULTIPLE
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
}
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_chooser)))
stopTask {
activity.startActivity(Intent.createChooser(shareIntent, activity.getString(R.string.share_chooser)))
}
}
}
}
fun open(context: Context, gocryptfsVolume: GocryptfsVolume, file_path: String) {
val result = exportFile(context, gocryptfsVolume, file_path, null)
result.first?.let {
val openIntent = Intent()
openIntent.action = Intent.ACTION_VIEW
fun open(activity: AppCompatActivity, gocryptfsVolume: GocryptfsVolume, file_path: String) {
object : LoadingTask(activity, R.string.loading_msg_export) {
override fun doTask(activity: AppCompatActivity) {
val result = exportFile(activity, gocryptfsVolume, file_path, null)
if (result.first != null) {
val openIntent = Intent(Intent.ACTION_VIEW)
openIntent.setDataAndType(result.first, result.second)
context.startActivity(openIntent)
stopTask { activity.startActivity(openIntent) }
} else {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(activity.getString(R.string.export_failed, file_path))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
}
fun removeFiles(context: Context) {
Thread{
val wiped = ArrayList<Uri>()
for (uri in storedFiles) {
if (Wiper.wipe(context, uri)){
storedFiles.remove(uri)
if (Wiper.wipe(context, uri) == null){
wiped.add(uri)
}
}
for (uri in wiped){
storedFiles.remove(uri)
}
}.start()
}
}

View File

@ -0,0 +1,39 @@
package sushi.hardcore.droidfs.util
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
abstract class LoadingTask(private val activity: AppCompatActivity, private val loadingMessageResId: Int) {
private val dialogLoadingView = activity.layoutInflater.inflate(R.layout.dialog_loading, null)
private val dialogLoading: AlertDialog = ColoredAlertDialogBuilder(activity)
.setView(dialogLoadingView)
.setTitle(R.string.loading)
.setCancelable(false)
.create()
init {
dialogLoadingView.findViewById<TextView>(R.id.text_message).text = activity.getString(loadingMessageResId)
startTask()
}
abstract fun doTask(activity: AppCompatActivity)
open fun doFinally(activity: AppCompatActivity){}
private fun startTask() {
dialogLoading.show()
Thread {
doTask(activity)
activity.runOnUiThread { doFinally(activity) }
}.start()
}
protected fun stopTask(onUiThread: () -> Unit){
dialogLoading.dismiss()
activity.runOnUiThread {
onUiThread()
}
}
protected fun stopTaskWithToast(stringId: Int){
stopTask { Toast.makeText(activity, stringId, Toast.LENGTH_SHORT).show() }
}
}

View File

@ -4,7 +4,8 @@ import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import android.widget.EditText
import sushi.hardcore.droidfs.ConstValues.Companion.wipe_passes
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R
import java.io.*
import java.lang.Exception
import java.lang.StringBuilder
@ -14,7 +15,7 @@ import kotlin.math.ceil
object Wiper {
private const val buff_size = 4096
fun wipe(context: Context, uri: Uri): Boolean {
fun wipe(context: Context, uri: Uri): String? {
val cursor = context.contentResolver.query(uri, null, null, null, null)
cursor?.let {
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
@ -26,11 +27,11 @@ object Wiper {
val buff = ByteArray(buff_size)
Arrays.fill(buff, 0.toByte())
val writes = ceil(size.toDouble() / buff_size).toInt()
for (i in 0 until wipe_passes) {
for (i in 0 until ConstValues.wipe_passes) {
for (j in 0 until writes) {
os!!.write(buff)
}
if (i < wipe_passes - 1) {
if (i < ConstValues.wipe_passes - 1) {
//reopening to flush and seek
os!!.close()
os = context.contentResolver.openOutputStream(uri)
@ -42,26 +43,25 @@ object Wiper {
(os as FileOutputStream).channel.truncate(0) //truncate to 0 if cannot delete
}
os!!.close()
return true
return null
} catch (e: Exception) {
e.printStackTrace()
return e.message
}
}
return false
return context.getString(R.string.query_cursor_null_error_msg)
}
@JvmStatic
fun wipe(file: File): Boolean{
fun wipe(file: File): String? {
val size = file.length()
try {
var os = FileOutputStream(file)
val buff = ByteArray(buff_size)
Arrays.fill(buff, 0.toByte())
val writes = ceil(size.toDouble() / buff_size).toInt()
for (i in 0 until wipe_passes) {
for (i in 0 until ConstValues.wipe_passes) {
for (j in 0 until writes) {
os.write(buff)
}
if (i < wipe_passes - 1) {
if (i < ConstValues.wipe_passes - 1) {
//reopening to flush and seek
os.close()
os = FileOutputStream(file)
@ -73,10 +73,9 @@ object Wiper {
os.channel.truncate(0) //truncate to 0 if cannot delete
}
os.close()
return true
return null
} catch (e: Exception) {
e.printStackTrace()
return false
return e.message
}
}
private fun randomString(minSize: Int, maxSize: Int): String {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -126,21 +126,22 @@
android:id="@+id/saved_path_listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="50dp"
android:layout_marginTop="20dp"
android:layout_marginHorizontal="@dimen/action_activity_listview_margin_horizontal"
android:layout_marginTop="@dimen/action_activity_listview_margin_top"
android:background="@drawable/listview_border"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:padding="@dimen/warning_msg_padding"
android:gravity="center"
android:text="@string/create_password_warning"/>
<Button
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
android:onClick="onClickChangePassword"
android:text="@string/change_volume_password"
style="@style/button"/>

View File

@ -105,14 +105,14 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:padding="@dimen/warning_msg_padding"
android:gravity="center"
android:text="@string/create_password_warning"/>
<Button
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:onClick="onClickCreate"
android:text="@string/create_volume"
style="@style/button"/>

View File

@ -86,20 +86,21 @@
android:id="@+id/saved_path_listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="50dp"
android:layout_marginTop="20dp"/>
android:layout_marginHorizontal="@dimen/action_activity_listview_margin_horizontal"
android:layout_marginTop="@dimen/action_activity_listview_margin_top"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:padding="@dimen/warning_msg_padding"
android:gravity="center"
android:text="@string/open_activity_warning"/>
<Button
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
android:onClick="onClickOpen"
android:text="@string/open_volume"
style="@style/button"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -7,7 +7,11 @@
<dimen name="edit_text_label_size">15sp</dimen>
<dimen name="open_activity_label_width">90dp</dimen>
<dimen name="change_password_activity_label_width">100dp</dimen>
<dimen name="action_activity_button_hor_margin">60dp</dimen>
<dimen name="action_activity_button_horizontal_margin">60dp</dimen>
<dimen name="action_activity_button_height">60dp</dimen>
<dimen name="action_activity_listview_margin_horizontal">50dp</dimen>
<dimen name="action_activity_listview_margin_top">20dp</dimen>
<dimen name="warning_msg_padding">20dp</dimen>
<dimen name="action_activity_button_margin_bottom">20dp</dimen>
<dimen name="adapter_text_size">18sp</dimen>
</resources>

View File

@ -50,7 +50,7 @@
<string name="yes">YES</string>
<string name="no">NO</string>
<string name="ask_for_wipe">Do you want to wipe the original files ?</string>
<string name="wipe_failed">Wiping of %1$s failed</string>
<string name="wipe_failed">Wiping failed: %1$s</string>
<string name="wipe_successful">Files successfully wiped !</string>
<string name="wipe_success_msg">The imported files have been successfully wiped from their original locations.</string>
<string name="create_password_warning">Warning !\nThis password will be the only way to decrypt the volume and access the files inside.\nChoose a very strong password (not \"123456\" or \"password\"), do not lose it and keep it secure (preferably only in your mind).\n\nDroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.\nDo not type passwords in insecure environments.</string>
@ -62,6 +62,7 @@
<string name="sort_order">Sort order:</string>
<string name="old_password">Old password:</string>
<string name="new_password">New password:</string>
<string name="new_password_confirmation">New Password (confirmation):</string>
<string name="success_change_password">Password successfully changed !</string>
<string name="success_change_password_msg">The volume\'s password has been successfully changed.</string>
<string name="change_password_failed">Failed to change the volume\'s password. Check the selected volume path and the entered old password.</string>
@ -118,12 +119,15 @@
<string name="discard">Discard</string>
<string name="word_wrap">Word Wrap</string>
<string name="outofmemoryerror_msg">OutOfMemoryError: This file is too large to be loaded in memory.</string>
<string name="new_file">New File</string>
<string name="new_file">Create New File</string>
<string name="enter_file_name">File name:</string>
<string name="file_creation_failed">Failed to create the file.</string>
<string name="loading">Loading...</string>
<string name="loading_msg_create">Creating volume...</string>
<string name="loading_msg_change_password">Changing password...</string>
<string name="new_password_confirmation">New Password (confirmation):</string>
<string name="loading_msg_open">Opening volume...</string>
<string name="loading">Loading…</string>
<string name="loading_msg_create">Creating volume…</string>
<string name="loading_msg_change_password">Changing password…</string>
<string name="loading_msg_open">Opening volume…</string>
<string name="loading_msg_import">Importing selected files…</string>
<string name="loading_msg_wipe">Wiping original files…</string>
<string name="loading_msg_export">Exporting files…</string>
<string name="query_cursor_null_error_msg">Unable to access this file</string>
</resources>