diff --git a/app/build.gradle b/app/build.gradle index f857036..d19ffd1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,9 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true + shrinkResources true + useProguard true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } diff --git a/app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt index 7e3e069..e00052e 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt @@ -3,6 +3,7 @@ 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 @@ -29,4 +30,8 @@ open class BaseActivity: CyaneaAppCompatActivity() { } } } + + protected fun toastFromThread(stringId: Int){ + runOnUiThread { Toast.makeText(this, stringId, Toast.LENGTH_SHORT).show() } + } } \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt index 1127668..ca18eec 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt @@ -8,6 +8,7 @@ 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 kotlinx.android.synthetic.main.activity_change_password.* import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path @@ -21,7 +22,7 @@ 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.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.util.* class ChangePasswordActivity : BaseActivity() { @@ -29,7 +30,7 @@ class ChangePasswordActivity : BaseActivity() { private const val PICK_DIRECTORY_REQUEST_CODE = 1 } private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver - private lateinit var root_cipher_dir: String + private lateinit var rootCipherDir: String private var usf_fingerprint = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -87,75 +88,93 @@ class ChangePasswordActivity : BaseActivity() { } fun onClickChangePassword(view: View?) { - root_cipher_dir = edit_volume_path.text.toString() - if (root_cipher_dir.isEmpty()) { + rootCipherDir = edit_volume_path.text.toString() + if (rootCipherDir.isEmpty()) { Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show() } else { changePassword(null) } } - fun changePassword(givenHash: ByteArray?){ - val new_password = edit_new_password.text.toString().toCharArray() - val new_password_confirm = edit_new_password_confirm.text.toString().toCharArray() - if (!new_password.contentEquals(new_password_confirm)) { - Toast.makeText(applicationContext, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() - } else { - val old_password = edit_old_password.text.toString().toCharArray() - var returnedHash: ByteArray? = null - if (usf_fingerprint && checkbox_save_password.isChecked){ - returnedHash = ByteArray(GocryptfsVolume.KeyLen) - } - var changePasswordImmediately = true - if (givenHash == null){ - val cipherText = sharedPrefs.getString(root_cipher_dir, null) - if (cipherText != null){ //password hash saved - fingerprintPasswordHashSaver.decrypt(cipherText, root_cipher_dir, ::changePassword) - changePasswordImmediately = false + private fun changePassword(givenHash: ByteArray?){ + val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null) + val dialogTextMessage = dialogLoadingView.findViewById(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 { + 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) + } else { + val oldPassword = edit_old_password.text.toString().toCharArray() + var returnedHash: ByteArray? = null + if (usf_fingerprint && checkbox_save_password.isChecked){ + returnedHash = ByteArray(GocryptfsVolume.KeyLen) } - } - if (changePasswordImmediately){ - if (GocryptfsVolume.change_password(root_cipher_dir, old_password, givenHash, new_password, returnedHash)) { - val editor = sharedPrefs.edit() - if (sharedPrefs.getString(root_cipher_dir, null) != null){ - editor.remove(root_cipher_dir) - editor.apply() + var changePasswordImmediately = true + if (givenHash == null){ + val cipherText = sharedPrefs.getString(rootCipherDir, null) + if (cipherText != null){ //password hash saved + dialogLoading.dismiss() + fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword) + changePasswordImmediately = false } - var continueImmediately = true - if (checkbox_remember_path.isChecked) { - val old_saved_volumes_paths = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set - val new_saved_volumes_paths = old_saved_volumes_paths.toMutableList() - if (!old_saved_volumes_paths.contains(root_cipher_dir)) { - new_saved_volumes_paths.add(root_cipher_dir) - editor.putStringSet(ConstValues.saved_volumes_key, new_saved_volumes_paths.toSet()) + } + if (changePasswordImmediately){ + if (GocryptfsVolume.change_password(rootCipherDir, oldPassword, givenHash, newPassword, returnedHash)) { + val editor = sharedPrefs.edit() + if (sharedPrefs.getString(rootCipherDir, null) != null){ + editor.remove(rootCipherDir) editor.apply() } - if (checkbox_save_password.isChecked && returnedHash != null){ - fingerprintPasswordHashSaver.encryptAndSave(returnedHash, root_cipher_dir){ _ -> - onPasswordChanged() + var continueImmediately = true + if (checkbox_remember_path.isChecked) { + val oldSavedVolumesPaths = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set + val newSavedVolumesPaths = oldSavedVolumesPaths.toMutableList() + if (!oldSavedVolumesPaths.contains(rootCipherDir)) { + newSavedVolumesPaths.add(rootCipherDir) + editor.putStringSet(ConstValues.saved_volumes_key, newSavedVolumesPaths.toSet()) + editor.apply() } - continueImmediately = false + if (checkbox_save_password.isChecked && returnedHash != null){ + dialogLoading.dismiss() + fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ -> + onPasswordChanged() + } + continueImmediately = false + } + } + if (continueImmediately){ + dialogLoading.dismiss() + runOnUiThread { onPasswordChanged() } + } + } else { + dialogLoading.dismiss() + runOnUiThread { + ColoredAlertDialogBuilder(this) + .setTitle(R.string.error) + .setMessage(R.string.change_password_failed) + .setPositiveButton(R.string.ok, null) + .show() } } - if (continueImmediately){ - onPasswordChanged() - } - } else { - ColoredAlertDialog(this) - .setTitle(R.string.error) - .setMessage(R.string.change_password_failed) - .setPositiveButton(R.string.ok, null) - .show() } + Arrays.fill(oldPassword, 0.toChar()) } - Arrays.fill(old_password, 0.toChar()) - } - Arrays.fill(new_password, 0.toChar()) - Arrays.fill(new_password_confirm, 0.toChar()) + Arrays.fill(newPassword, 0.toChar()) + Arrays.fill(newPasswordConfirm, 0.toChar()) + }.start() } - fun onPasswordChanged(){ - ColoredAlertDialog(this) + private fun onPasswordChanged(){ + ColoredAlertDialogBuilder(this) .setTitle(R.string.success_change_password) .setMessage(R.string.success_change_password_msg) .setCancelable(false) diff --git a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt index 9fc4aae..a533484 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt @@ -8,9 +8,8 @@ class ConstValues { const val creator = "DroidFS" const val saved_volumes_key = "saved_volumes" const val sort_order_key = "sort_order" - val fakeUri = Uri.parse("fakeuri://droidfs") + val fakeUri: Uri = Uri.parse("fakeuri://droidfs") const val wipe_passes = 2 - const val seek_bar_inc = 200 private val fileExtensions = mapOf( Pair("image", listOf("png", "jpg", "jpeg")), Pair("video", listOf("mp4", "webm")), diff --git a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt index 250b7fb..4722e60 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt @@ -5,7 +5,7 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.view.View -import android.widget.Toast +import android.widget.TextView 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 @@ -18,7 +18,7 @@ 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.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.File import java.util.* @@ -27,7 +27,7 @@ class CreateActivity : BaseActivity() { private const val PICK_DIRECTORY_REQUEST_CODE = 1 } private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver - private lateinit var root_cipher_dir: String + private lateinit var rootCipherDir: String private var sessionID = -1 private var usf_fingerprint = false override fun onCreate(savedInstanceState: Bundle?) { @@ -64,89 +64,110 @@ class CreateActivity : BaseActivity() { } fun onClickCreate(view: View?) { - val password = edit_password.text.toString().toCharArray() - val password_confirm = edit_password_confirm.text.toString().toCharArray() - if (!password.contentEquals(password_confirm)) { - Toast.makeText(applicationContext, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() - } else { - root_cipher_dir = edit_volume_path.text.toString() - val volume_path_file = File(root_cipher_dir) - var good_directory = false - if (!volume_path_file.isDirectory) { - if (volume_path_file.mkdirs()) { - good_directory = true - } else { - Toast.makeText(applicationContext, R.string.error_mkdir, Toast.LENGTH_SHORT).show() - } + val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null) + val dialogTextMessage = dialogLoadingView.findViewById(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 { + 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) } else { - val dir_content = volume_path_file.list() - if (dir_content != null){ - if (dir_content.isEmpty()) { - good_directory = true + rootCipherDir = edit_volume_path.text.toString() + val volumePathFile = File(rootCipherDir) + var goodDirectory = false + if (!volumePathFile.isDirectory) { + if (volumePathFile.mkdirs()) { + goodDirectory = true } else { - Toast.makeText(applicationContext, R.string.dir_not_empty, Toast.LENGTH_SHORT).show() + dialogLoading.dismiss() + toastFromThread(R.string.error_mkdir) } } else { - Toast.makeText(applicationContext, getString(R.string.listdir_null_error_msg), Toast.LENGTH_SHORT).show() - } - } - if (good_directory) { - if (GocryptfsVolume.create_volume(root_cipher_dir, password, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator)) { - var returnedHash: ByteArray? = null - if (usf_fingerprint && checkbox_save_password.isChecked){ - returnedHash = ByteArray(GocryptfsVolume.KeyLen) - } - sessionID = GocryptfsVolume.init(root_cipher_dir, password, null, returnedHash) - if (sessionID != -1) { - var startExplorerImmediately = true - if (checkbox_remember_path.isChecked) { - val old_saved_volumes_paths = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set - val editor = sharedPrefs.edit() - val new_saved_volumes_paths = old_saved_volumes_paths.toMutableList() - if (old_saved_volumes_paths.contains(root_cipher_dir)) { - if (sharedPrefs.getString(root_cipher_dir, null) != null){ - editor.remove(root_cipher_dir) - } - } else { - new_saved_volumes_paths.add(root_cipher_dir) - editor.putStringSet(ConstValues.saved_volumes_key, new_saved_volumes_paths.toSet()) - } - editor.apply() - if (checkbox_save_password.isChecked && returnedHash != null){ - fingerprintPasswordHashSaver.encryptAndSave(returnedHash, root_cipher_dir){ _ -> - startExplorer() - } - startExplorerImmediately = false - } - } - if (startExplorerImmediately){ - startExplorer() + val dirContent = volumePathFile.list() + if (dirContent != null){ + if (dirContent.isEmpty()) { + goodDirectory = true + } else { + dialogLoading.dismiss() + toastFromThread(R.string.dir_not_empty) } } else { - Toast.makeText(this, R.string.open_volume_failed, Toast.LENGTH_SHORT).show() + dialogLoading.dismiss() + toastFromThread(R.string.listdir_null_error_msg) + } + } + if (goodDirectory) { + if (GocryptfsVolume.create_volume(rootCipherDir, password, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator)) { + var returnedHash: ByteArray? = null + if (usf_fingerprint && checkbox_save_password.isChecked){ + returnedHash = ByteArray(GocryptfsVolume.KeyLen) + } + sessionID = GocryptfsVolume.init(rootCipherDir, password, null, returnedHash) + if (sessionID != -1) { + var startExplorerImmediately = true + if (checkbox_remember_path.isChecked) { + val oldSavedVolumesPaths = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set + val editor = sharedPrefs.edit() + val newSavedVolumesPaths = oldSavedVolumesPaths.toMutableList() + if (oldSavedVolumesPaths.contains(rootCipherDir)) { + if (sharedPrefs.getString(rootCipherDir, null) != null){ + editor.remove(rootCipherDir) + } + } else { + newSavedVolumesPaths.add(rootCipherDir) + editor.putStringSet(ConstValues.saved_volumes_key, newSavedVolumesPaths.toSet()) + } + editor.apply() + if (checkbox_save_password.isChecked && returnedHash != null){ + dialogLoading.dismiss() + fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ -> + runOnUiThread { startExplorer() } + } + startExplorerImmediately = false + } + } + if (startExplorerImmediately){ + dialogLoading.dismiss() + runOnUiThread { startExplorer() } + } + } else { + dialogLoading.dismiss() + toastFromThread(R.string.open_volume_failed) + } + } else { + dialogLoading.dismiss() + runOnUiThread { + ColoredAlertDialogBuilder(this) + .setTitle(R.string.error) + .setMessage(R.string.create_volume_failed) + .setPositiveButton(R.string.ok, null) + .show() + } } - } else { - ColoredAlertDialog(this) - .setTitle(R.string.error) - .setMessage(R.string.create_volume_failed) - .setPositiveButton(R.string.ok, null) - .show() } } - } - Arrays.fill(password, 0.toChar()) - Arrays.fill(password_confirm, 0.toChar()) + Arrays.fill(password, 0.toChar()) + Arrays.fill(passwordConfirm, 0.toChar()) + }.start() } - fun startExplorer(){ - ColoredAlertDialog(this) + private fun startExplorer(){ + ColoredAlertDialogBuilder(this) .setTitle(R.string.success_volume_create) .setMessage(R.string.success_volume_create_msg) .setCancelable(false) .setPositiveButton(R.string.ok) { _, _ -> val intent = Intent(applicationContext, ExplorerActivity::class.java) intent.putExtra("sessionID", sessionID) - intent.putExtra("volume_name", File(root_cipher_dir).name) + intent.putExtra("volume_name", File(rootCipherDir).name) startActivity(intent) finish() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt index 975c52d..a40d63d 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt @@ -9,11 +9,10 @@ import android.os.Environment import android.util.DisplayMetrics import android.view.* import androidx.core.content.ContextCompat -import androidx.preference.PreferenceManager import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.toolbar.* -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder class MainActivity : BaseActivity() { companion object { @@ -33,14 +32,14 @@ class MainActivity : BaseActivity() { val state = Environment.getExternalStorageState() val storageAvailable = Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state if (!storageAvailable) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.storage_unavailable) .setMessage(R.string.storage_unavailable_msg) .setPositiveButton(R.string.ok ) { _, _ -> finish() }.show() } if (!sharedPrefs.getBoolean("alreadyLaunched", false)){ - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.warning) .setMessage(R.string.usf_home_warning_msg) .setCancelable(false) @@ -63,7 +62,7 @@ class MainActivity : BaseActivity() { when (requestCode) { STORAGE_PERMISSIONS_REQUEST -> if (grantResults.size == 2) { if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.storage_perm_denied) .setMessage(R.string.storage_perm_denied_msg) .setPositiveButton(R.string.ok diff --git a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt index 65f978b..ebbe83f 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt @@ -6,7 +6,7 @@ import android.os.Build import android.os.Bundle import android.view.View import android.widget.AdapterView.OnItemClickListener -import android.widget.Toast +import android.widget.TextView 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 @@ -22,7 +22,7 @@ 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.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.File import java.util.* @@ -32,7 +32,7 @@ class OpenActivity : BaseActivity() { } private lateinit var savedVolumesAdapter: SavedVolumesAdapter private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver - private lateinit var root_cipher_dir: String + private lateinit var rootCipherDir: String private var sessionID = -1 private var usf_fingerprint = false override fun onCreate(savedInstanceState: Bundle?) { @@ -49,11 +49,11 @@ class OpenActivity : BaseActivity() { if (savedVolumesAdapter.count > 0){ saved_path_listview.adapter = savedVolumesAdapter saved_path_listview.onItemClickListener = OnItemClickListener { _, _, position, _ -> - root_cipher_dir = savedVolumesAdapter.getItem(position) - edit_volume_path.setText(root_cipher_dir) - val cipherText = sharedPrefs.getString(root_cipher_dir, null) + rootCipherDir = savedVolumesAdapter.getItem(position) + edit_volume_path.setText(rootCipherDir) + val cipherText = sharedPrefs.getString(rootCipherDir, null) if (cipherText != null){ //password hash saved - fingerprintPasswordHashSaver.decrypt(cipherText, root_cipher_dir, ::openUsingPasswordHash) + fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::openUsingPasswordHash) } } } else { @@ -83,49 +83,66 @@ class OpenActivity : BaseActivity() { } fun onClickOpen(view: View?) { - root_cipher_dir = edit_volume_path.text.toString() //fresh get in case of manual rewrite - if (root_cipher_dir.isEmpty()) { - Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show() - } else { - val password = edit_password.text.toString().toCharArray() - var returnedHash: ByteArray? = null - if (usf_fingerprint && checkbox_save_password.isChecked){ - returnedHash = ByteArray(GocryptfsVolume.KeyLen) - } - sessionID = GocryptfsVolume.init(root_cipher_dir, password, null, returnedHash) - if (sessionID != -1) { - var startExplorerImmediately = true - if (checkbox_remember_path.isChecked) { - savedVolumesAdapter.addVolumePath(root_cipher_dir) - if (checkbox_save_password.isChecked && returnedHash != null){ - fingerprintPasswordHashSaver.encryptAndSave(returnedHash, root_cipher_dir) {success -> - if (success){ - startExplorer() + val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null) + val dialogTextMessage = dialogLoadingView.findViewById(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 { + rootCipherDir = edit_volume_path.text.toString() //fresh get in case of manual rewrite + if (rootCipherDir.isEmpty()) { + dialogLoading.dismiss() + toastFromThread(R.string.enter_volume_path) + } else { + val password = edit_password.text.toString().toCharArray() + var returnedHash: ByteArray? = null + if (usf_fingerprint && checkbox_save_password.isChecked){ + returnedHash = ByteArray(GocryptfsVolume.KeyLen) + } + sessionID = GocryptfsVolume.init(rootCipherDir, password, null, returnedHash) + if (sessionID != -1) { + var startExplorerImmediately = true + 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() + } } + startExplorerImmediately = false } - startExplorerImmediately = false + } + if (startExplorerImmediately){ + dialogLoading.dismiss() + startExplorer() + } + } else { + dialogLoading.dismiss() + runOnUiThread { + ColoredAlertDialogBuilder(this) + .setTitle(R.string.open_volume_failed) + .setMessage(R.string.open_volume_failed_msg) + .setPositiveButton(R.string.ok, null) + .show() } } - if (startExplorerImmediately){ - startExplorer() - } - } else { - ColoredAlertDialog(this) - .setTitle(R.string.open_volume_failed) - .setMessage(R.string.open_volume_failed_msg) - .setPositiveButton(R.string.ok, null) - .show() + Arrays.fill(password, 0.toChar()) } - Arrays.fill(password, 0.toChar()) - } + }.start() } private fun openUsingPasswordHash(passwordHash: ByteArray){ - sessionID = GocryptfsVolume.init(root_cipher_dir, null, passwordHash, null) + sessionID = GocryptfsVolume.init(rootCipherDir, null, passwordHash, null) if (sessionID != -1){ startExplorer() } else { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.open_volume_failed) .setMessage(R.string.open_failed_hash_msg) .setPositiveButton(R.string.ok, null) @@ -135,24 +152,24 @@ class OpenActivity : BaseActivity() { } private fun startExplorer() { - var explorer_intent: Intent? = null - val current_intent_action = intent.action - if (current_intent_action != null) { - if ((current_intent_action == Intent.ACTION_SEND || current_intent_action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null) { //import via android share menu - explorer_intent = Intent(this, ExplorerActivityDrop::class.java) - explorer_intent.action = current_intent_action //forward action - explorer_intent.putExtras(intent.extras!!) //forward extras - } else if (current_intent_action == "pick") { //pick items to import - explorer_intent = Intent(this, ExplorerActivityPick::class.java) - explorer_intent.flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT + var explorerIntent: Intent? = null + val currentIntentAction = intent.action + if (currentIntentAction != null) { + if ((currentIntentAction == Intent.ACTION_SEND || currentIntentAction == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null) { //import via android share menu + explorerIntent = Intent(this, ExplorerActivityDrop::class.java) + explorerIntent.action = currentIntentAction //forward action + explorerIntent.putExtras(intent.extras!!) //forward extras + } else if (currentIntentAction == "pick") { //pick items to import + explorerIntent = Intent(this, ExplorerActivityPick::class.java) + explorerIntent.flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT } } - if (explorer_intent == null) { - explorer_intent = Intent(this, ExplorerActivity::class.java) //default opening + if (explorerIntent == null) { + explorerIntent = Intent(this, ExplorerActivity::class.java) //default opening } - explorer_intent.putExtra("sessionID", sessionID) - explorer_intent.putExtra("volume_name", File(root_cipher_dir).name) - startActivity(explorer_intent) + explorerIntent.putExtra("sessionID", sessionID) + explorerIntent.putExtra("volume_name", File(rootCipherDir).name) + startActivity(explorerIntent) finish() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt index dd308d6..cf1963a 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt @@ -26,6 +26,7 @@ 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/adapters/DialogSingleChoiceAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/DialogSingleChoiceAdapter.kt new file mode 100644 index 0000000..ce023f3 --- /dev/null +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/DialogSingleChoiceAdapter.kt @@ -0,0 +1,33 @@ +package sushi.hardcore.droidfs.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.CheckedTextView +import sushi.hardcore.droidfs.R +import sushi.hardcore.droidfs.widgets.ThemeColor + +class DialogSingleChoiceAdapter(private val context: Context, private val entries: Array): BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val view: View = convertView ?: inflater.inflate(R.layout.adapter_colored_dialog_single_choice, parent, false) + val checkedTextView = view.findViewById(android.R.id.text1) + checkedTextView.text = getItem(position) + val typedArray = context.theme.obtainStyledAttributes(arrayOf(android.R.attr.listChoiceIndicatorSingle).toIntArray()) + val drawable = typedArray.getDrawable(0) + typedArray.recycle() + drawable?.setTint(ThemeColor.getThemeColor(context)) + checkedTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) + return view + } + + override fun getItem(position: Int): String { + return entries[position] + } + + override fun getItemId(position: Int): Long { return 0 } + + override fun getCount(): Int { return entries.size } +} \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/droidfs/adapters/OpenAsDialogAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/OpenAsDialogAdapter.kt index e85657e..10859ed 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/adapters/OpenAsDialogAdapter.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/OpenAsDialogAdapter.kt @@ -18,7 +18,7 @@ class OpenAsDialogAdapter(private val context: Context): BaseAdapter() { listOf("text", context.getString(R.string.text), R.drawable.icon_file_text) ) override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { - val view: View = convertView ?: inflater.inflate(R.layout.adapter_dialog_listview, parent, false) + val view: View = convertView ?: inflater.inflate(R.layout.adapter_dialog_icon_text, parent, false) val text = view.findViewById(R.id.text) text.text = items[position][1] as String val icon = view.findViewById(R.id.icon) diff --git a/app/src/main/java/sushi/hardcore/droidfs/adapters/SavedVolumesAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/SavedVolumesAdapter.kt index 584fcd2..733425f 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/adapters/SavedVolumesAdapter.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/SavedVolumesAdapter.kt @@ -1,10 +1,8 @@ package sushi.hardcore.droidfs.adapters -import androidx.appcompat.app.AlertDialog import android.content.Context import android.content.SharedPreferences import android.content.SharedPreferences.Editor -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,7 +12,7 @@ import android.widget.TextView import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.util.WidgetUtil -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.util.* class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferences) : BaseAdapter() { @@ -56,7 +54,7 @@ class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferen val delete_imageview = view.findViewById(R.id.delete_imageview) delete_imageview.setOnClickListener { val volume_path = saved_volumes_paths[position] - val dialog = ColoredAlertDialog(context) + val dialog = ColoredAlertDialogBuilder(context) dialog.setTitle(R.string.warning) if (shared_prefs.getString(volume_path, null) != null){ dialog.setMessage(context.getString(R.string.delete_hash_or_all)) 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 03f8845..d99070f 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -9,7 +9,6 @@ import android.view.WindowManager import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemLongClickListener import android.widget.EditText -import android.widget.ListView import android.widget.Toast import com.github.clans.fab.FloatingActionMenu import kotlinx.android.synthetic.main.activity_explorer_base.* @@ -22,6 +21,7 @@ import sushi.hardcore.droidfs.ConstValues.Companion.isImage import sushi.hardcore.droidfs.ConstValues.Companion.isText import sushi.hardcore.droidfs.ConstValues.Companion.isVideo import sushi.hardcore.droidfs.R +import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter import sushi.hardcore.droidfs.file_viewers.AudioPlayer @@ -32,7 +32,7 @@ import sushi.hardcore.droidfs.provider.RestrictedFileProvider import sushi.hardcore.droidfs.util.ExternalProvider import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.util.GocryptfsVolume -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.util.* open class BaseExplorerActivity : BaseActivity() { @@ -111,25 +111,21 @@ open class BaseExplorerActivity : BaseActivity() { startFileViewer(AudioPlayer::class.java, fullPath) } else -> { - val dialogListView = layoutInflater.inflate(R.layout.dialog_listview, null) - val listView = dialogListView.findViewById(R.id.listview) val adapter = OpenAsDialogAdapter(this) - listView.adapter = adapter - val dialog = ColoredAlertDialog(this) - .setView(dialogListView) + ColoredAlertDialogBuilder(this) + .setSingleChoiceItems(adapter, -1){ dialog, which -> + when (adapter.getItem(which)){ + "image" -> startFileViewer(ImageViewer::class.java, fullPath) + "video" -> startFileViewer(VideoPlayer::class.java, fullPath) + "audio" -> startFileViewer(AudioPlayer::class.java, fullPath) + "text" -> startFileViewer(TextEditor::class.java, fullPath) + } + dialog.dismiss() + } .setTitle(getString(R.string.open_as)) .setNegativeButton(R.string.cancel, null) .create() - listView.setOnItemClickListener{_, _, fileTypePosition, _ -> - when (adapter.getItem(fileTypePosition)){ - "image" -> startFileViewer(ImageViewer::class.java, fullPath) - "video" -> startFileViewer(VideoPlayer::class.java, fullPath) - "audio" -> startFileViewer(AudioPlayer::class.java, fullPath) - "text" -> startFileViewer(TextEditor::class.java, fullPath) - } - dialog.dismiss() - } - dialog.show() + .show() } } } @@ -177,7 +173,7 @@ open class BaseExplorerActivity : BaseActivity() { } private fun askCloseVolume() { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.warning) .setMessage(R.string.ask_close_volume) .setPositiveButton(R.string.ok) { _, _ -> closeVolumeOnUserExit() } @@ -213,7 +209,7 @@ open class BaseExplorerActivity : BaseActivity() { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() } else { if (!gocryptfsVolume.mkdir(PathUtils.path_join(currentDirectoryPath, folder_name))) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.error_mkdir) .setPositiveButton(R.string.ok, null) @@ -229,7 +225,7 @@ open class BaseExplorerActivity : BaseActivity() { findViewById(R.id.fam_explorer).close(true) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditText = dialogEditTextView.findViewById(R.id.dialog_edit_text) - val dialog = ColoredAlertDialog(this) + val dialog = ColoredAlertDialogBuilder(this) .setView(dialogEditTextView) .setTitle(R.string.enter_folder_name) .setPositiveButton(R.string.ok) { _, _ -> @@ -253,7 +249,7 @@ open class BaseExplorerActivity : BaseActivity() { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() } else { if (!gocryptfsVolume.rename(PathUtils.path_join(currentDirectoryPath, old_name), PathUtils.path_join(currentDirectoryPath, new_name))) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.rename_failed, old_name)) .setPositiveButton(R.string.ok, null) @@ -296,13 +292,15 @@ open class BaseExplorerActivity : BaseActivity() { true } R.id.explorer_menu_sort -> { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.sort_order) - .setSingleChoiceItems(sortModesEntries, currentSortModeIndex) { dialog, which -> + .setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which -> currentSortModeIndex = which setCurrentPath(currentDirectoryPath) dialog.dismiss() - }.show() + } + .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } + .show() true } R.id.explorer_menu_rename -> { @@ -311,7 +309,7 @@ open class BaseExplorerActivity : BaseActivity() { val dialogEditText = dialogEditTextView.findViewById(R.id.dialog_edit_text) dialogEditText.setText(oldName) dialogEditText.selectAll() - val dialog = ColoredAlertDialog(this) + val dialog = ColoredAlertDialogBuilder(this) .setView(dialogEditTextView) .setTitle(R.string.rename_title) .setPositiveButton(R.string.ok) { _, _ -> 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 2d098a2..e95ff03 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt @@ -17,7 +17,7 @@ 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.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.File import java.util.* @@ -39,7 +39,7 @@ class ExplorerActivity : BaseExplorerActivity() { } else { val handleID = gocryptfsVolume.open_write_mode(PathUtils.path_join(currentDirectoryPath, fileName)) if (handleID == -1) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.file_creation_failed) .setPositiveButton(R.string.ok, null) @@ -56,7 +56,7 @@ class ExplorerActivity : BaseExplorerActivity() { findViewById(R.id.fam_explorer).close(true) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditText = dialogEditTextView.findViewById(R.id.dialog_edit_text) - val dialog = ColoredAlertDialog(this) + val dialog = ColoredAlertDialogBuilder(this) .setView(dialogEditTextView) .setTitle(getString(R.string.enter_file_name)) .setPositiveButton(R.string.ok) { _, _ -> @@ -115,7 +115,7 @@ class ExplorerActivity : BaseExplorerActivity() { success = gocryptfsVolume.import_file(it, dstPath) } if (!success) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.import_failed, uri)) .setPositiveButton(R.string.ok, null) @@ -124,7 +124,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } if (success) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.success_import) .setMessage(""" ${getString(R.string.success_import_msg)} @@ -134,7 +134,7 @@ class ExplorerActivity : BaseExplorerActivity() { success = true for (uri in uris) { if (!Wiper.wipe(this, uri)) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.wipe_failed, uri)) .setPositiveButton(R.string.ok, null) @@ -144,7 +144,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } if (success) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.wipe_successful) .setMessage(R.string.wipe_success_msg) .setPositiveButton(R.string.ok, null) @@ -171,7 +171,7 @@ class ExplorerActivity : BaseExplorerActivity() { if (gocryptfsVolume.export_file(fullPath, PathUtils.path_join(outputDir, element.name))) null else fullPath } if (failedItem != null) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.export_failed, failedItem)) .setPositiveButton(R.string.ok, null) @@ -180,7 +180,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } if (failedItem == null) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.success_export) .setMessage(R.string.success_export_msg) .setPositiveButton(R.string.ok, null) @@ -214,13 +214,13 @@ class ExplorerActivity : BaseExplorerActivity() { failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path } if (failedItem == null) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.success_import) .setMessage(R.string.success_import_msg) .setPositiveButton(R.string.ok, null) .show() } else { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.import_failed, failedItem)) .setPositiveButton(R.string.ok, null) @@ -266,7 +266,7 @@ class ExplorerActivity : BaseExplorerActivity() { } R.id.explorer_menu_delete -> { val size = explorerAdapter.selectedItems.size - val dialog = ColoredAlertDialog(this) + val dialog = ColoredAlertDialogBuilder(this) dialog.setTitle(R.string.warning) dialog.setPositiveButton(R.string.ok) { _, _ -> removeSelectedItems() } dialog.setNegativeButton(R.string.cancel, null) @@ -399,7 +399,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } if (failedItem != null) { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.remove_failed, failedItem)) .setPositiveButton(R.string.ok, null) 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 5a0d73e..efe46ec 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt @@ -6,7 +6,7 @@ import android.view.Menu import android.view.MenuItem import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.util.PathUtils -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder class ExplorerActivityDrop : BaseExplorerActivity() { override fun init() { @@ -23,7 +23,7 @@ class ExplorerActivityDrop : BaseExplorerActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.explorer_menu_validate -> { - val alertDialog = ColoredAlertDialog(this) + val alertDialog = ColoredAlertDialogBuilder(this) alertDialog.setCancelable(false) alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } var error_msg: String? = null diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt index 80f0b68..cda60b9 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt @@ -5,7 +5,7 @@ import android.view.View import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.util.GocryptfsVolume -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder abstract class FileViewerActivity: BaseActivity() { lateinit var gocryptfsVolume: GocryptfsVolume @@ -49,7 +49,7 @@ abstract class FileViewerActivity: BaseActivity() { if (success){ return fileBuff } else { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.read_file_failed) .setCancelable(false) @@ -57,7 +57,7 @@ abstract class FileViewerActivity: BaseActivity() { .show() } } catch (e: OutOfMemoryError){ - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.outofmemoryerror_msg) .setCancelable(false) @@ -66,7 +66,7 @@ abstract class FileViewerActivity: BaseActivity() { } } else { - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.get_size_failed) .setCancelable(false) diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt index 54a6047..3c01103 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt @@ -4,24 +4,32 @@ import androidx.appcompat.app.AlertDialog import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer +import com.google.android.exoplayer2.extractor.ExtractorsFactory +import com.google.android.exoplayer2.extractor.flv.FlvExtractor +import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor +import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor +import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor +import com.google.android.exoplayer2.extractor.ogg.OggExtractor +import com.google.android.exoplayer2.extractor.wav.WavExtractor import com.google.android.exoplayer2.source.LoopingMediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.R -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder abstract class MediaPlayer: FileViewerActivity() { private lateinit var player: SimpleExoPlayer private var currentWindow = 0 private var playbackPosition: Long = 0 - private lateinit var errorDialog: AlertDialog.Builder + private lateinit var errorDialog: AlertDialog override fun viewFile() { - errorDialog = ColoredAlertDialog(this) + errorDialog = ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.playing_failed) .setCancelable(false) .setPositiveButton(R.string.ok) { _, _ -> finish() } + .create() } abstract fun bindPlayer(player: SimpleExoPlayer) @@ -30,7 +38,16 @@ abstract class MediaPlayer: FileViewerActivity() { player = SimpleExoPlayer.Builder(this).build() bindPlayer(player) val dataSourceFactory = GocryptfsDataSource.Factory(gocryptfsVolume, filePath) - val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(ConstValues.fakeUri) + val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory, ExtractorsFactory { + arrayOf( + MatroskaExtractor(), + Mp4Extractor(), + Mp3Extractor(), + FlvExtractor(), + OggExtractor(), + WavExtractor() + ) + }).createMediaSource(ConstValues.fakeUri) player.seekTo(currentWindow, playbackPosition) player.playWhenReady = true player.addListener(object : Player.EventListener{ diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt index 191617a..7f4a32b 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt @@ -10,7 +10,7 @@ import android.widget.Toast import androidx.appcompat.widget.Toolbar import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.util.GocryptfsVolume -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.ByteArrayInputStream import java.io.File @@ -30,7 +30,7 @@ class TextEditor: FileViewerActivity() { try { loadLayout(String(it)) } catch (e: OutOfMemoryError){ - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.outofmemoryerror_msg) .setCancelable(false) @@ -98,7 +98,7 @@ class TextEditor: FileViewerActivity() { private fun checkSaveAndExit(){ if (changedSinceLastSave){ - ColoredAlertDialog(this) + ColoredAlertDialogBuilder(this) .setTitle(R.string.warning) .setMessage(R.string.ask_save) .setPositiveButton(getString(R.string.save)) { _, _ -> diff --git a/app/src/main/java/sushi/hardcore/droidfs/fingerprint_stuff/FingerprintPasswordHashSaver.kt b/app/src/main/java/sushi/hardcore/droidfs/fingerprint_stuff/FingerprintPasswordHashSaver.kt index 26f4b77..6becbde 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/fingerprint_stuff/FingerprintPasswordHashSaver.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/fingerprint_stuff/FingerprintPasswordHashSaver.kt @@ -14,15 +14,13 @@ import android.os.Handler import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import android.util.Base64 -import android.util.Log import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.R -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.security.KeyStore import javax.crypto.* import javax.crypto.spec.GCMParameterSpec @@ -31,11 +29,11 @@ import javax.crypto.spec.GCMParameterSpec class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivity, private val shared_prefs: SharedPreferences) { private var isPrepared = false var isListening = false - var authenticationFailed = false - private val shared_prefs_editor: SharedPreferences.Editor = shared_prefs.edit() + private var authenticationFailed = false + private val sharedPrefsEditor: SharedPreferences.Editor = shared_prefs.edit() private val fingerprintManager = activityContext.getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager - private lateinit var root_cipher_dir: String - private lateinit var action_description: String + private lateinit var rootCipherDir: String + private lateinit var actionDescription: String private lateinit var onAuthenticationResult: (success: Boolean) -> Unit private lateinit var onPasswordDecrypted: (password: ByteArray) -> Unit private lateinit var keyStore: KeyStore @@ -55,16 +53,16 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit private const val SUCCESS_DISMISS_DIALOG_DELAY: Long = 400 private const val FAILED_DISMISS_DIALOG_DELAY: Long = 800 } - private fun reset_hash_storage() { + private fun resetHashStorage() { keyStore.deleteEntry(KEY_ALIAS) - val saved_volume_paths = shared_prefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set - for (path in saved_volume_paths){ - val saved_hash = shared_prefs.getString(path, null) - if (saved_hash != null){ - shared_prefs_editor.remove(path) + val savedVolumePaths = shared_prefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set + for (path in savedVolumePaths){ + val savedHash = shared_prefs.getString(path, null) + if (savedHash != null){ + sharedPrefsEditor.remove(path) } } - shared_prefs_editor.apply() + sharedPrefsEditor.apply() Toast.makeText(activityContext, activityContext.getString(R.string.hash_storage_reset), Toast.LENGTH_SHORT).show() } @@ -99,13 +97,13 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit keyGenerator.generateKey() } cipher = Cipher.getInstance(CIPHER_TYPE) - fingerprintFragment = FingerprintFragment(root_cipher_dir, action_description, ::stopListening) + fingerprintFragment = FingerprintFragment(rootCipherDir, actionDescription, ::stopListening) isPrepared = true } fun encryptAndSave(plainText: ByteArray, root_cipher_dir: String, onAuthenticationResult: (success: Boolean) -> Unit){ if (shared_prefs.getString(root_cipher_dir, null) == null){ - this.root_cipher_dir = root_cipher_dir - this.action_description = activityContext.getString(R.string.encrypt_action_description) + this.rootCipherDir = root_cipher_dir + this.actionDescription = activityContext.getString(R.string.encrypt_action_description) this.onAuthenticationResult = onAuthenticationResult if (!isPrepared){ prepare() @@ -117,8 +115,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit } } fun decrypt(cipherText: String, root_cipher_dir: String, onPasswordDecrypted: (password: ByteArray) -> Unit){ - this.root_cipher_dir = root_cipher_dir - this.action_description = activityContext.getString(R.string.decrypt_action_description) + this.rootCipherDir = root_cipher_dir + this.actionDescription = activityContext.getString(R.string.decrypt_action_description) this.onPasswordDecrypted = onPasswordDecrypted if (!isPrepared){ prepare() @@ -136,8 +134,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit cancellationSignal = CancellationSignal() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val biometricPrompt = BiometricPrompt.Builder(activityContext) - .setTitle(root_cipher_dir) - .setSubtitle(action_description) + .setTitle(rootCipherDir) + .setSubtitle(actionDescription) .setDescription(activityContext.getString(R.string.fingerprint_instruction)) .setNegativeButton(activityContext.getString(R.string.cancel), activityContext.mainExecutor, DialogInterface.OnClickListener{_, _ -> cancellationSignal.cancel() @@ -180,7 +178,7 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit if (!authenticationFailed){ if (fingerprintFragment.isAdded){ fingerprintFragment.image_fingerprint.setColorFilter(ContextCompat.getColor(activityContext, R.color.fingerprint_failed)) - fingerprintFragment.text_instruction.setText(activityContext.getString(R.string.authentication_error)) + fingerprintFragment.text_instruction.text = activityContext.getString(R.string.authentication_error) handler.postDelayed({ fingerprintFragment.dismiss() }, 1000) } if (actionMode == Cipher.ENCRYPT_MODE){ @@ -215,8 +213,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit val cipherText = cipher.doFinal(dataToProcess) val encodedCipherText = Base64.encodeToString(cipherText, 0) val encodedIv = Base64.encodeToString(cipher.iv, 0) - shared_prefs_editor.putString(root_cipher_dir, "$encodedIv:$encodedCipherText") - shared_prefs_editor.apply() + sharedPrefsEditor.putString(rootCipherDir, "$encodedIv:$encodedCipherText") + sharedPrefsEditor.apply() handler.postDelayed({ if (fingerprintFragment.isAdded){ fingerprintFragment.dismiss() @@ -234,11 +232,11 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit onPasswordDecrypted(plainText) }, SUCCESS_DISMISS_DIALOG_DELAY) } catch (e: AEADBadTagException){ - ColoredAlertDialog(activityContext) + ColoredAlertDialogBuilder(activityContext) .setTitle(R.string.error) .setMessage(activityContext.getString(R.string.MAC_verification_failed)) .setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ -> - reset_hash_storage() + resetHashStorage() } .setNegativeButton(R.string.cancel, null) .show() @@ -247,11 +245,11 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit } } catch (e: IllegalBlockSizeException){ stopListening() - ColoredAlertDialog(activityContext) + ColoredAlertDialogBuilder(activityContext) .setTitle(R.string.authentication_error) .setMessage(activityContext.getString(R.string.authentication_error_msg)) .setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ -> - reset_hash_storage() + resetHashStorage() } .setNegativeButton(R.string.cancel, null) .show() 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 fea972b..17478f4 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/ExternalProvider.kt @@ -5,7 +5,7 @@ import android.content.Intent import android.net.Uri import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.provider.RestrictedFileProvider -import sushi.hardcore.droidfs.widgets.ColoredAlertDialog +import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.File import java.net.URLConnection import java.util.* @@ -37,7 +37,7 @@ object ExternalProvider { return Pair(tmpFileUri, getContentType(fileName, previous_content_type)) } } - ColoredAlertDialog(context) + ColoredAlertDialogBuilder(context) .setTitle(R.string.error) .setMessage(context.getString(R.string.export_failed, file_path)) .setPositiveButton(R.string.ok, null) diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt b/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt index a6d0a39..7892592 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt @@ -79,7 +79,7 @@ object Wiper { return false } } - fun randomString(minSize: Int, maxSize: Int): String { + private fun randomString(minSize: Int, maxSize: Int): String { val r = Random() val sb = StringBuilder() val length = r.nextInt(maxSize-minSize)+minSize diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialog.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt similarity index 80% rename from app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialog.kt rename to app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt index 2fb7faa..2f8a548 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialog.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt @@ -1,10 +1,11 @@ package sushi.hardcore.droidfs.widgets -//import android.app.AlertDialog import androidx.appcompat.app.AlertDialog import android.content.Context -class ColoredAlertDialog(context: Context): AlertDialog.Builder(context) { +class ColoredAlertDialogBuilder: AlertDialog.Builder { + constructor(context: Context): super(context) + constructor(context: Context, themeResId: Int): super(context, themeResId) private fun applyColor(dialog: AlertDialog){ dialog.setOnShowListener{ val themeColor = ThemeColor.getThemeColor(context) diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt index 9c8c8b2..2f09a23 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt @@ -11,7 +11,7 @@ import android.widget.ListView import androidx.core.content.ContextCompat import sushi.hardcore.droidfs.R -class ColoredBorderListView: ListView { +open class ColoredBorderListView: ListView { constructor(context: Context) : super(context) { applyColor() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredListPreference.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredListPreference.kt new file mode 100644 index 0000000..9a51609 --- /dev/null +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredListPreference.kt @@ -0,0 +1,30 @@ +package sushi.hardcore.droidfs.widgets + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.ListPreference +import sushi.hardcore.droidfs.R +import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter + +class ColoredListPreference: ListPreference { + constructor(context: Context): super(context) + constructor(context: Context, attrs: AttributeSet): super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) + + override fun onAttached() { + super.onAttached() + summary = entries[entryValues.indexOf(getPersistedString(value))] + } + + override fun onClick() { + ColoredAlertDialogBuilder(context) + .setTitle(title) + .setSingleChoiceItems(DialogSingleChoiceAdapter(context, entries.map { s -> s.toString() }.toTypedArray()), entryValues.indexOf(getPersistedString(value))) { dialog, which -> + dialog.dismiss() + summary = entries[which].toString() + persistString(entryValues[which].toString()) + } + .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } + .show() + } +} \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/NonScrollableColoredBorderListView.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/NonScrollableColoredBorderListView.kt new file mode 100644 index 0000000..c488e7c --- /dev/null +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/NonScrollableColoredBorderListView.kt @@ -0,0 +1,23 @@ +package sushi.hardcore.droidfs.widgets + +import android.content.Context +import android.util.AttributeSet +import android.widget.ListAdapter + +class NonScrollableColoredBorderListView: ColoredBorderListView { + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet): super(context, attrs) + + override fun setAdapter(adapter: ListAdapter?) { + super.setAdapter(adapter) + adapter?.let { + var totalHeight = 0 + for (i in 0 until adapter.count){ + val item = adapter.getView(i, null, this) + item.measure(0, 0) + totalHeight += item.measuredHeight + } + layoutParams.height = totalHeight + (dividerHeight * (adapter.count-1)) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_pause.xml b/app/src/main/res/drawable/icon_pause.xml deleted file mode 100644 index f701d6f..0000000 --- a/app/src/main/res/drawable/icon_pause.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_play.xml b/app/src/main/res/drawable/icon_play.xml deleted file mode 100644 index 0870be8..0000000 --- a/app/src/main/res/drawable/icon_play.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_stop.xml b/app/src/main/res/drawable/icon_stop.xml deleted file mode 100644 index 513de7c..0000000 --- a/app/src/main/res/drawable/icon_stop.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/layout/activity_change_password.xml b/app/src/main/res/layout/activity_change_password.xml index 1766a1c..9ecd7ee 100644 --- a/app/src/main/res/layout/activity_change_password.xml +++ b/app/src/main/res/layout/activity_change_password.xml @@ -7,7 +7,6 @@ @@ -94,7 +93,7 @@ android:layout_width="@dimen/change_password_activity_label_width" android:layout_height="match_parent" android:gravity="center_vertical" - android:text="New Password (confirmation):" + android:text="@string/new_password_confirmation" android:textSize="@dimen/edit_text_label_size" /> - @@ -83,7 +82,7 @@ android:text="@string/fingerprint_save_checkbox_text" android:onClick="onClickSavePasswordHash"/> - + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_dialog_listview.xml b/app/src/main/res/layout/adapter_dialog_icon_text.xml similarity index 90% rename from app/src/main/res/layout/adapter_dialog_listview.xml rename to app/src/main/res/layout/adapter_dialog_icon_text.xml index 4c0b029..da4ed82 100644 --- a/app/src/main/res/layout/adapter_dialog_listview.xml +++ b/app/src/main/res/layout/adapter_dialog_icon_text.xml @@ -14,7 +14,7 @@ android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="18sp" + android:textSize="@dimen/adapter_text_size" android:layout_gravity="center"/> \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_explorer_element.xml b/app/src/main/res/layout/adapter_explorer_element.xml index 39fc227..0a99c0a 100644 --- a/app/src/main/res/layout/adapter_explorer_element.xml +++ b/app/src/main/res/layout/adapter_explorer_element.xml @@ -19,7 +19,7 @@ android:id="@+id/text_element_name" android:layout_width="match_parent" android:layout_height="wrap_content" - android:textSize="18sp"/> + android:textSize="@dimen/adapter_text_size"/> - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000..a149d49 --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 8de322e..d62c47b 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -9,4 +9,5 @@ 100dp 60dp 60dp + 18sp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c1990f6..68a4d61 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,7 @@ The volume has been successfully created. The volume creation has failed. Open failed - Failed to open the volume. Check the selected volume path and your password. + Failed to open the volume. Check the selected volume path and the entered password. Share File Storage unavailable DroidFS can\'t work without storage access. @@ -64,7 +64,7 @@ New password: Password successfully changed ! The volume\'s password has been successfully changed. - Failed to change the volume\'s password. Check the selected volume path and your old password. + Failed to change the volume\'s password. Check the selected volume path and the entered old password. Encrypt with DroidFS Failed to handle the share request. Unable to access this directory @@ -121,4 +121,9 @@ New File File name: Failed to create the file. + Loading... + Creating volume... + Changing password... + New Password (confirmation): + Opening volume... diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index b477530..e802898 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -13,14 +13,13 @@ - + android:icon="@drawable/icon_sort"/>