Loading dialogs & Graphical bug fixes

This commit is contained in:
Hardcore Sushi 2020-07-27 16:20:52 +02:00
parent 24cfc1093e
commit 2571849bc3
37 changed files with 486 additions and 315 deletions

View File

@ -24,7 +24,9 @@ android {
buildTypes {
release {
minifyEnabled false
minifyEnabled true
shrinkResources true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

View File

@ -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() }
}
}

View File

@ -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<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 {
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<String>
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<String>
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)

View File

@ -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")),

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.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<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 {
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<String>
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<String>
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()
}

View File

@ -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

View File

@ -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<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 {
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()
}

View File

@ -26,6 +26,7 @@ class SettingsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceSt
.beginTransaction()
.replace(R.id.settings, fragment)
.commit()
setTheme(R.style.AppTheme)
}
class SettingsFragment : PreferenceFragmentCompat() {

View File

@ -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<String>): 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<CheckedTextView>(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 }
}

View File

@ -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<TextView>(R.id.text)
text.text = items[position][1] as String
val icon = view.findViewById<ColoredImageView>(R.id.icon)

View File

@ -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<ImageView>(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))

View File

@ -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<ListView>(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<FloatingActionMenu>(R.id.fam_explorer).close(true)
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(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<EditText>(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) { _, _ ->

View File

@ -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<FloatingActionMenu>(R.id.fam_explorer).close(true)
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(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)

View File

@ -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

View File

@ -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)

View File

@ -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{

View File

@ -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)) { _, _ ->

View File

@ -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<String>()) as Set<String>
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<String>()) as Set<String>
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()

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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()
}

View File

@ -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()
}
}

View File

@ -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))
}
}
}

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M6,6h12v12H6z"/>
</vector>

View File

@ -7,7 +7,6 @@
<include layout="@layout/toolbar"/>
<ScrollView
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -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" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
@ -123,7 +122,7 @@
android:text="@string/fingerprint_save_checkbox_text"
android:onClick="onClickSavePasswordHash"/>
<sushi.hardcore.droidfs.widgets.ColoredBorderListView
<sushi.hardcore.droidfs.widgets.NonScrollableColoredBorderListView
android:id="@+id/saved_path_listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -7,7 +7,6 @@
<include layout="@layout/toolbar"/>
<ScrollView
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -83,7 +82,7 @@
android:text="@string/fingerprint_save_checkbox_text"
android:onClick="onClickSavePasswordHash"/>
<sushi.hardcore.droidfs.widgets.ColoredBorderListView
<sushi.hardcore.droidfs.widgets.NonScrollableColoredBorderListView
android:id="@+id/saved_path_listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingEnd="16dp"
android:paddingStart="24dp"
android:textSize="@dimen/adapter_text_size"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
android:drawablePadding="20dp"/>

View File

@ -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"/>
</LinearLayout>

View File

@ -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"/>
<LinearLayout
android:layout_width="wrap_content"

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="@style/listViewNoDivider"
android:layout_marginTop="10dp"/>
</LinearLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="20dp"
android:paddingTop="10dp"
android:paddingBottom="20dp">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="10dp"/>
</LinearLayout>

View File

@ -9,4 +9,5 @@
<dimen name="change_password_activity_label_width">100dp</dimen>
<dimen name="action_activity_button_hor_margin">60dp</dimen>
<dimen name="action_activity_button_height">60dp</dimen>
<dimen name="adapter_text_size">18sp</dimen>
</resources>

View File

@ -30,7 +30,7 @@
<string name="success_volume_create_msg">The volume has been successfully created.</string>
<string name="create_volume_failed">The volume creation has failed.</string>
<string name="open_volume_failed">Open failed</string>
<string name="open_volume_failed_msg">Failed to open the volume. Check the selected volume path and your password.</string>
<string name="open_volume_failed_msg">Failed to open the volume. Check the selected volume path and the entered password.</string>
<string name="share_chooser">Share File</string>
<string name="storage_unavailable">Storage unavailable</string>
<string name="storage_unavailable_msg">DroidFS can\'t work without storage access.</string>
@ -64,7 +64,7 @@
<string name="new_password">New password:</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 your old password.</string>
<string name="change_password_failed">Failed to change the volume\'s password. Check the selected volume path and the entered old password.</string>
<string name="share_menu_label">Encrypt with DroidFS</string>
<string name="share_intent_parsing_failed">Failed to handle the share request.</string>
<string name="listdir_null_error_msg">Unable to access this directory</string>
@ -121,4 +121,9 @@
<string name="new_file">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>
</resources>

View File

@ -13,14 +13,13 @@
<PreferenceCategory android:title="@string/explorer">
<ListPreference
<sushi.hardcore.droidfs.widgets.ColoredListPreference
app:defaultValue="name"
app:entries="@array/sort_orders_entries"
app:entryValues="@array/sort_orders_values"
app:key="sort_order"
android:title="@string/settings_title_sort_order"
android:icon="@drawable/icon_sort"
app:useSimpleSummaryProvider="true"/>
android:icon="@drawable/icon_sort"/>
</PreferenceCategory>