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 { buildTypes {
release { release {
minifyEnabled false minifyEnabled true
shrinkResources true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 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.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.jaredrummler.cyanea.app.CyaneaAppCompatActivity 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.text.TextWatcher
import android.view.View import android.view.View
import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemClickListener
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import kotlinx.android.synthetic.main.activity_change_password.* import kotlinx.android.synthetic.main.activity_change_password.*
import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path
@ -21,7 +22,7 @@ import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.util.* import java.util.*
class ChangePasswordActivity : BaseActivity() { class ChangePasswordActivity : BaseActivity() {
@ -29,7 +30,7 @@ class ChangePasswordActivity : BaseActivity() {
private const val PICK_DIRECTORY_REQUEST_CODE = 1 private const val PICK_DIRECTORY_REQUEST_CODE = 1
} }
private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver
private lateinit var root_cipher_dir: String private lateinit var rootCipherDir: String
private var usf_fingerprint = false private var usf_fingerprint = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -87,75 +88,93 @@ class ChangePasswordActivity : BaseActivity() {
} }
fun onClickChangePassword(view: View?) { fun onClickChangePassword(view: View?) {
root_cipher_dir = edit_volume_path.text.toString() rootCipherDir = edit_volume_path.text.toString()
if (root_cipher_dir.isEmpty()) { if (rootCipherDir.isEmpty()) {
Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show()
} else { } else {
changePassword(null) changePassword(null)
} }
} }
fun changePassword(givenHash: ByteArray?){ private fun changePassword(givenHash: ByteArray?){
val new_password = edit_new_password.text.toString().toCharArray() val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
val new_password_confirm = edit_new_password_confirm.text.toString().toCharArray() val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
if (!new_password.contentEquals(new_password_confirm)) { dialogTextMessage.text = getString(R.string.loading_msg_change_password)
Toast.makeText(applicationContext, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() val dialogLoading = ColoredAlertDialogBuilder(this)
} else { .setView(dialogLoadingView)
val old_password = edit_old_password.text.toString().toCharArray() .setTitle(R.string.loading)
var returnedHash: ByteArray? = null .setCancelable(false)
if (usf_fingerprint && checkbox_save_password.isChecked){ .create()
returnedHash = ByteArray(GocryptfsVolume.KeyLen) dialogLoading.show()
} Thread {
var changePasswordImmediately = true val newPassword = edit_new_password.text.toString().toCharArray()
if (givenHash == null){ val newPasswordConfirm = edit_new_password_confirm.text.toString().toCharArray()
val cipherText = sharedPrefs.getString(root_cipher_dir, null) if (!newPassword.contentEquals(newPasswordConfirm)) {
if (cipherText != null){ //password hash saved dialogLoading.dismiss()
fingerprintPasswordHashSaver.decrypt(cipherText, root_cipher_dir, ::changePassword) toastFromThread(R.string.passwords_mismatch)
changePasswordImmediately = false } else {
val oldPassword = 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 (changePasswordImmediately){ if (givenHash == null){
if (GocryptfsVolume.change_password(root_cipher_dir, old_password, givenHash, new_password, returnedHash)) { val cipherText = sharedPrefs.getString(rootCipherDir, null)
val editor = sharedPrefs.edit() if (cipherText != null){ //password hash saved
if (sharedPrefs.getString(root_cipher_dir, null) != null){ dialogLoading.dismiss()
editor.remove(root_cipher_dir) fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword)
editor.apply() changePasswordImmediately = false
} }
var continueImmediately = true }
if (checkbox_remember_path.isChecked) { if (changePasswordImmediately){
val old_saved_volumes_paths = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set<String> if (GocryptfsVolume.change_password(rootCipherDir, oldPassword, givenHash, newPassword, returnedHash)) {
val new_saved_volumes_paths = old_saved_volumes_paths.toMutableList() val editor = sharedPrefs.edit()
if (!old_saved_volumes_paths.contains(root_cipher_dir)) { if (sharedPrefs.getString(rootCipherDir, null) != null){
new_saved_volumes_paths.add(root_cipher_dir) editor.remove(rootCipherDir)
editor.putStringSet(ConstValues.saved_volumes_key, new_saved_volumes_paths.toSet())
editor.apply() editor.apply()
} }
if (checkbox_save_password.isChecked && returnedHash != null){ var continueImmediately = true
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, root_cipher_dir){ _ -> if (checkbox_remember_path.isChecked) {
onPasswordChanged() 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(newPassword, 0.toChar())
} Arrays.fill(newPasswordConfirm, 0.toChar())
Arrays.fill(new_password, 0.toChar()) }.start()
Arrays.fill(new_password_confirm, 0.toChar())
} }
fun onPasswordChanged(){ private fun onPasswordChanged(){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_change_password) .setTitle(R.string.success_change_password)
.setMessage(R.string.success_change_password_msg) .setMessage(R.string.success_change_password_msg)
.setCancelable(false) .setCancelable(false)

View File

@ -8,9 +8,8 @@ class ConstValues {
const val creator = "DroidFS" const val creator = "DroidFS"
const val saved_volumes_key = "saved_volumes" const val saved_volumes_key = "saved_volumes"
const val sort_order_key = "sort_order" 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 wipe_passes = 2
const val seek_bar_inc = 200
private val fileExtensions = mapOf( private val fileExtensions = mapOf(
Pair("image", listOf("png", "jpg", "jpeg")), Pair("image", listOf("png", "jpg", "jpeg")),
Pair("video", listOf("mp4", "webm")), Pair("video", listOf("mp4", "webm")),

View File

@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View 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.*
import kotlinx.android.synthetic.main.activity_create.checkbox_remember_path import kotlinx.android.synthetic.main.activity_create.checkbox_remember_path
import kotlinx.android.synthetic.main.activity_create.checkbox_save_password 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.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -27,7 +27,7 @@ class CreateActivity : BaseActivity() {
private const val PICK_DIRECTORY_REQUEST_CODE = 1 private const val PICK_DIRECTORY_REQUEST_CODE = 1
} }
private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver
private lateinit var root_cipher_dir: String private lateinit var rootCipherDir: String
private var sessionID = -1 private var sessionID = -1
private var usf_fingerprint = false private var usf_fingerprint = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -64,89 +64,110 @@ class CreateActivity : BaseActivity() {
} }
fun onClickCreate(view: View?) { fun onClickCreate(view: View?) {
val password = edit_password.text.toString().toCharArray() val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
val password_confirm = edit_password_confirm.text.toString().toCharArray() val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
if (!password.contentEquals(password_confirm)) { dialogTextMessage.text = getString(R.string.loading_msg_create)
Toast.makeText(applicationContext, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() val dialogLoading = ColoredAlertDialogBuilder(this)
} else { .setView(dialogLoadingView)
root_cipher_dir = edit_volume_path.text.toString() .setTitle(R.string.loading)
val volume_path_file = File(root_cipher_dir) .setCancelable(false)
var good_directory = false .create()
if (!volume_path_file.isDirectory) { dialogLoading.show()
if (volume_path_file.mkdirs()) { Thread {
good_directory = true val password = edit_password.text.toString().toCharArray()
} else { val passwordConfirm = edit_password_confirm.text.toString().toCharArray()
Toast.makeText(applicationContext, R.string.error_mkdir, Toast.LENGTH_SHORT).show() if (!password.contentEquals(passwordConfirm)) {
} dialogLoading.dismiss()
toastFromThread(R.string.passwords_mismatch)
} else { } else {
val dir_content = volume_path_file.list() rootCipherDir = edit_volume_path.text.toString()
if (dir_content != null){ val volumePathFile = File(rootCipherDir)
if (dir_content.isEmpty()) { var goodDirectory = false
good_directory = true if (!volumePathFile.isDirectory) {
if (volumePathFile.mkdirs()) {
goodDirectory = true
} else { } else {
Toast.makeText(applicationContext, R.string.dir_not_empty, Toast.LENGTH_SHORT).show() dialogLoading.dismiss()
toastFromThread(R.string.error_mkdir)
} }
} else { } else {
Toast.makeText(applicationContext, getString(R.string.listdir_null_error_msg), Toast.LENGTH_SHORT).show() val dirContent = volumePathFile.list()
} if (dirContent != null){
} if (dirContent.isEmpty()) {
if (good_directory) { goodDirectory = true
if (GocryptfsVolume.create_volume(root_cipher_dir, password, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator)) { } else {
var returnedHash: ByteArray? = null dialogLoading.dismiss()
if (usf_fingerprint && checkbox_save_password.isChecked){ toastFromThread(R.string.dir_not_empty)
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()
} }
} else { } 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, 0.toChar()) Arrays.fill(passwordConfirm, 0.toChar())
Arrays.fill(password_confirm, 0.toChar()) }.start()
} }
fun startExplorer(){ private fun startExplorer(){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_volume_create) .setTitle(R.string.success_volume_create)
.setMessage(R.string.success_volume_create_msg) .setMessage(R.string.success_volume_create_msg)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
val intent = Intent(applicationContext, ExplorerActivity::class.java) val intent = Intent(applicationContext, ExplorerActivity::class.java)
intent.putExtra("sessionID", sessionID) intent.putExtra("sessionID", sessionID)
intent.putExtra("volume_name", File(root_cipher_dir).name) intent.putExtra("volume_name", File(rootCipherDir).name)
startActivity(intent) startActivity(intent)
finish() finish()
} }

View File

@ -9,11 +9,10 @@ import android.os.Environment
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.* import android.view.*
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.toolbar.* import kotlinx.android.synthetic.main.toolbar.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
companion object { companion object {
@ -33,14 +32,14 @@ class MainActivity : BaseActivity() {
val state = Environment.getExternalStorageState() val state = Environment.getExternalStorageState()
val storageAvailable = Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state val storageAvailable = Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state
if (!storageAvailable) { if (!storageAvailable) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.storage_unavailable) .setTitle(R.string.storage_unavailable)
.setMessage(R.string.storage_unavailable_msg) .setMessage(R.string.storage_unavailable_msg)
.setPositiveButton(R.string.ok .setPositiveButton(R.string.ok
) { _, _ -> finish() }.show() ) { _, _ -> finish() }.show()
} }
if (!sharedPrefs.getBoolean("alreadyLaunched", false)){ if (!sharedPrefs.getBoolean("alreadyLaunched", false)){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setMessage(R.string.usf_home_warning_msg) .setMessage(R.string.usf_home_warning_msg)
.setCancelable(false) .setCancelable(false)
@ -63,7 +62,7 @@ class MainActivity : BaseActivity() {
when (requestCode) { when (requestCode) {
STORAGE_PERMISSIONS_REQUEST -> if (grantResults.size == 2) { STORAGE_PERMISSIONS_REQUEST -> if (grantResults.size == 2) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.storage_perm_denied) .setTitle(R.string.storage_perm_denied)
.setMessage(R.string.storage_perm_denied_msg) .setMessage(R.string.storage_perm_denied_msg)
.setPositiveButton(R.string.ok .setPositiveButton(R.string.ok

View File

@ -6,7 +6,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.AdapterView.OnItemClickListener 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_remember_path
import kotlinx.android.synthetic.main.activity_open.checkbox_save_password import kotlinx.android.synthetic.main.activity_open.checkbox_save_password
import kotlinx.android.synthetic.main.activity_open.edit_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.GocryptfsVolume
import sushi.hardcore.droidfs.util.WidgetUtil import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -32,7 +32,7 @@ class OpenActivity : BaseActivity() {
} }
private lateinit var savedVolumesAdapter: SavedVolumesAdapter private lateinit var savedVolumesAdapter: SavedVolumesAdapter
private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver private lateinit var fingerprintPasswordHashSaver: FingerprintPasswordHashSaver
private lateinit var root_cipher_dir: String private lateinit var rootCipherDir: String
private var sessionID = -1 private var sessionID = -1
private var usf_fingerprint = false private var usf_fingerprint = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -49,11 +49,11 @@ class OpenActivity : BaseActivity() {
if (savedVolumesAdapter.count > 0){ if (savedVolumesAdapter.count > 0){
saved_path_listview.adapter = savedVolumesAdapter saved_path_listview.adapter = savedVolumesAdapter
saved_path_listview.onItemClickListener = OnItemClickListener { _, _, position, _ -> saved_path_listview.onItemClickListener = OnItemClickListener { _, _, position, _ ->
root_cipher_dir = savedVolumesAdapter.getItem(position) rootCipherDir = savedVolumesAdapter.getItem(position)
edit_volume_path.setText(root_cipher_dir) edit_volume_path.setText(rootCipherDir)
val cipherText = sharedPrefs.getString(root_cipher_dir, null) val cipherText = sharedPrefs.getString(rootCipherDir, null)
if (cipherText != null){ //password hash saved if (cipherText != null){ //password hash saved
fingerprintPasswordHashSaver.decrypt(cipherText, root_cipher_dir, ::openUsingPasswordHash) fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::openUsingPasswordHash)
} }
} }
} else { } else {
@ -83,49 +83,66 @@ class OpenActivity : BaseActivity() {
} }
fun onClickOpen(view: View?) { fun onClickOpen(view: View?) {
root_cipher_dir = edit_volume_path.text.toString() //fresh get in case of manual rewrite val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
if (root_cipher_dir.isEmpty()) { val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show() dialogTextMessage.text = getString(R.string.loading_msg_open)
} else { val dialogLoading = ColoredAlertDialogBuilder(this)
val password = edit_password.text.toString().toCharArray() .setView(dialogLoadingView)
var returnedHash: ByteArray? = null .setTitle(R.string.loading)
if (usf_fingerprint && checkbox_save_password.isChecked){ .setCancelable(false)
returnedHash = ByteArray(GocryptfsVolume.KeyLen) .create()
} dialogLoading.show()
sessionID = GocryptfsVolume.init(root_cipher_dir, password, null, returnedHash) Thread {
if (sessionID != -1) { rootCipherDir = edit_volume_path.text.toString() //fresh get in case of manual rewrite
var startExplorerImmediately = true if (rootCipherDir.isEmpty()) {
if (checkbox_remember_path.isChecked) { dialogLoading.dismiss()
savedVolumesAdapter.addVolumePath(root_cipher_dir) toastFromThread(R.string.enter_volume_path)
if (checkbox_save_password.isChecked && returnedHash != null){ } else {
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, root_cipher_dir) {success -> val password = edit_password.text.toString().toCharArray()
if (success){ var returnedHash: ByteArray? = null
startExplorer() 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){ Arrays.fill(password, 0.toChar())
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()) }.start()
}
} }
private fun openUsingPasswordHash(passwordHash: ByteArray){ private fun openUsingPasswordHash(passwordHash: ByteArray){
sessionID = GocryptfsVolume.init(root_cipher_dir, null, passwordHash, null) sessionID = GocryptfsVolume.init(rootCipherDir, null, passwordHash, null)
if (sessionID != -1){ if (sessionID != -1){
startExplorer() startExplorer()
} else { } else {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.open_volume_failed) .setTitle(R.string.open_volume_failed)
.setMessage(R.string.open_failed_hash_msg) .setMessage(R.string.open_failed_hash_msg)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -135,24 +152,24 @@ class OpenActivity : BaseActivity() {
} }
private fun startExplorer() { private fun startExplorer() {
var explorer_intent: Intent? = null var explorerIntent: Intent? = null
val current_intent_action = intent.action val currentIntentAction = intent.action
if (current_intent_action != null) { if (currentIntentAction != null) {
if ((current_intent_action == Intent.ACTION_SEND || current_intent_action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null) { //import via android share menu if ((currentIntentAction == Intent.ACTION_SEND || currentIntentAction == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null) { //import via android share menu
explorer_intent = Intent(this, ExplorerActivityDrop::class.java) explorerIntent = Intent(this, ExplorerActivityDrop::class.java)
explorer_intent.action = current_intent_action //forward action explorerIntent.action = currentIntentAction //forward action
explorer_intent.putExtras(intent.extras!!) //forward extras explorerIntent.putExtras(intent.extras!!) //forward extras
} else if (current_intent_action == "pick") { //pick items to import } else if (currentIntentAction == "pick") { //pick items to import
explorer_intent = Intent(this, ExplorerActivityPick::class.java) explorerIntent = Intent(this, ExplorerActivityPick::class.java)
explorer_intent.flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT explorerIntent.flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT
} }
} }
if (explorer_intent == null) { if (explorerIntent == null) {
explorer_intent = Intent(this, ExplorerActivity::class.java) //default opening explorerIntent = Intent(this, ExplorerActivity::class.java) //default opening
} }
explorer_intent.putExtra("sessionID", sessionID) explorerIntent.putExtra("sessionID", sessionID)
explorer_intent.putExtra("volume_name", File(root_cipher_dir).name) explorerIntent.putExtra("volume_name", File(rootCipherDir).name)
startActivity(explorer_intent) startActivity(explorerIntent)
finish() finish()
} }

View File

@ -26,6 +26,7 @@ class SettingsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceSt
.beginTransaction() .beginTransaction()
.replace(R.id.settings, fragment) .replace(R.id.settings, fragment)
.commit() .commit()
setTheme(R.style.AppTheme)
} }
class SettingsFragment : PreferenceFragmentCompat() { 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) listOf("text", context.getString(R.string.text), R.drawable.icon_file_text)
) )
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { 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) val text = view.findViewById<TextView>(R.id.text)
text.text = items[position][1] as String text.text = items[position][1] as String
val icon = view.findViewById<ColoredImageView>(R.id.icon) val icon = view.findViewById<ColoredImageView>(R.id.icon)

View File

@ -1,10 +1,8 @@
package sushi.hardcore.droidfs.adapters package sushi.hardcore.droidfs.adapters
import androidx.appcompat.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SharedPreferences.Editor import android.content.SharedPreferences.Editor
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -14,7 +12,7 @@ import android.widget.TextView
import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.WidgetUtil import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.util.* import java.util.*
class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferences) : BaseAdapter() { 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) val delete_imageview = view.findViewById<ImageView>(R.id.delete_imageview)
delete_imageview.setOnClickListener { delete_imageview.setOnClickListener {
val volume_path = saved_volumes_paths[position] val volume_path = saved_volumes_paths[position]
val dialog = ColoredAlertDialog(context) val dialog = ColoredAlertDialogBuilder(context)
dialog.setTitle(R.string.warning) dialog.setTitle(R.string.warning)
if (shared_prefs.getString(volume_path, null) != null){ if (shared_prefs.getString(volume_path, null) != null){
dialog.setMessage(context.getString(R.string.delete_hash_or_all)) 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.OnItemClickListener
import android.widget.AdapterView.OnItemLongClickListener import android.widget.AdapterView.OnItemLongClickListener
import android.widget.EditText import android.widget.EditText
import android.widget.ListView
import android.widget.Toast import android.widget.Toast
import com.github.clans.fab.FloatingActionMenu import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.activity_explorer_base.* 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.isText
import sushi.hardcore.droidfs.ConstValues.Companion.isVideo import sushi.hardcore.droidfs.ConstValues.Companion.isVideo
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
import sushi.hardcore.droidfs.file_viewers.AudioPlayer 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.ExternalProvider
import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.util.* import java.util.*
open class BaseExplorerActivity : BaseActivity() { open class BaseExplorerActivity : BaseActivity() {
@ -111,25 +111,21 @@ open class BaseExplorerActivity : BaseActivity() {
startFileViewer(AudioPlayer::class.java, fullPath) startFileViewer(AudioPlayer::class.java, fullPath)
} }
else -> { else -> {
val dialogListView = layoutInflater.inflate(R.layout.dialog_listview, null)
val listView = dialogListView.findViewById<ListView>(R.id.listview)
val adapter = OpenAsDialogAdapter(this) val adapter = OpenAsDialogAdapter(this)
listView.adapter = adapter ColoredAlertDialogBuilder(this)
val dialog = ColoredAlertDialog(this) .setSingleChoiceItems(adapter, -1){ dialog, which ->
.setView(dialogListView) 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)) .setTitle(getString(R.string.open_as))
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.create() .create()
listView.setOnItemClickListener{_, _, fileTypePosition, _ -> .show()
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()
} }
} }
} }
@ -177,7 +173,7 @@ open class BaseExplorerActivity : BaseActivity() {
} }
private fun askCloseVolume() { private fun askCloseVolume() {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setMessage(R.string.ask_close_volume) .setMessage(R.string.ask_close_volume)
.setPositiveButton(R.string.ok) { _, _ -> closeVolumeOnUserExit() } .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() Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else { } else {
if (!gocryptfsVolume.mkdir(PathUtils.path_join(currentDirectoryPath, folder_name))) { if (!gocryptfsVolume.mkdir(PathUtils.path_join(currentDirectoryPath, folder_name))) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.error_mkdir) .setMessage(R.string.error_mkdir)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -229,7 +225,7 @@ open class BaseExplorerActivity : BaseActivity() {
findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true) findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true)
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text) val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialog(this) val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView) .setView(dialogEditTextView)
.setTitle(R.string.enter_folder_name) .setTitle(R.string.enter_folder_name)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
@ -253,7 +249,7 @@ open class BaseExplorerActivity : BaseActivity() {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else { } else {
if (!gocryptfsVolume.rename(PathUtils.path_join(currentDirectoryPath, old_name), PathUtils.path_join(currentDirectoryPath, new_name))) { if (!gocryptfsVolume.rename(PathUtils.path_join(currentDirectoryPath, old_name), PathUtils.path_join(currentDirectoryPath, new_name))) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.rename_failed, old_name)) .setMessage(getString(R.string.rename_failed, old_name))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -296,13 +292,15 @@ open class BaseExplorerActivity : BaseActivity() {
true true
} }
R.id.explorer_menu_sort -> { R.id.explorer_menu_sort -> {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.sort_order) .setTitle(R.string.sort_order)
.setSingleChoiceItems(sortModesEntries, currentSortModeIndex) { dialog, which -> .setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which ->
currentSortModeIndex = which currentSortModeIndex = which
setCurrentPath(currentDirectoryPath) setCurrentPath(currentDirectoryPath)
dialog.dismiss() dialog.dismiss()
}.show() }
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.show()
true true
} }
R.id.explorer_menu_rename -> { R.id.explorer_menu_rename -> {
@ -311,7 +309,7 @@ open class BaseExplorerActivity : BaseActivity() {
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text) val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
dialogEditText.setText(oldName) dialogEditText.setText(oldName)
dialogEditText.selectAll() dialogEditText.selectAll()
val dialog = ColoredAlertDialog(this) val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView) .setView(dialogEditTextView)
.setTitle(R.string.rename_title) .setTitle(R.string.rename_title)
.setPositiveButton(R.string.ok) { _, _ -> .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.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.Wiper import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -39,7 +39,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} else { } else {
val handleID = gocryptfsVolume.open_write_mode(PathUtils.path_join(currentDirectoryPath, fileName)) val handleID = gocryptfsVolume.open_write_mode(PathUtils.path_join(currentDirectoryPath, fileName))
if (handleID == -1) { if (handleID == -1) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.file_creation_failed) .setMessage(R.string.file_creation_failed)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -56,7 +56,7 @@ class ExplorerActivity : BaseExplorerActivity() {
findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true) findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true)
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text) val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialog(this) val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView) .setView(dialogEditTextView)
.setTitle(getString(R.string.enter_file_name)) .setTitle(getString(R.string.enter_file_name))
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
@ -115,7 +115,7 @@ class ExplorerActivity : BaseExplorerActivity() {
success = gocryptfsVolume.import_file(it, dstPath) success = gocryptfsVolume.import_file(it, dstPath)
} }
if (!success) { if (!success) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, uri)) .setMessage(getString(R.string.import_failed, uri))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -124,7 +124,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
if (success) { if (success) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_import) .setTitle(R.string.success_import)
.setMessage(""" .setMessage("""
${getString(R.string.success_import_msg)} ${getString(R.string.success_import_msg)}
@ -134,7 +134,7 @@ class ExplorerActivity : BaseExplorerActivity() {
success = true success = true
for (uri in uris) { for (uri in uris) {
if (!Wiper.wipe(this, uri)) { if (!Wiper.wipe(this, uri)) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.wipe_failed, uri)) .setMessage(getString(R.string.wipe_failed, uri))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -144,7 +144,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
if (success) { if (success) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.wipe_successful) .setTitle(R.string.wipe_successful)
.setMessage(R.string.wipe_success_msg) .setMessage(R.string.wipe_success_msg)
.setPositiveButton(R.string.ok, null) .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 (gocryptfsVolume.export_file(fullPath, PathUtils.path_join(outputDir, element.name))) null else fullPath
} }
if (failedItem != null) { if (failedItem != null) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.export_failed, failedItem)) .setMessage(getString(R.string.export_failed, failedItem))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -180,7 +180,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
if (failedItem == null) { if (failedItem == null) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_export) .setTitle(R.string.success_export)
.setMessage(R.string.success_export_msg) .setMessage(R.string.success_export_msg)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -214,13 +214,13 @@ class ExplorerActivity : BaseExplorerActivity() {
failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path
} }
if (failedItem == null) { if (failedItem == null) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_import) .setTitle(R.string.success_import)
.setMessage(R.string.success_import_msg) .setMessage(R.string.success_import_msg)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} else { } else {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem)) .setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
@ -266,7 +266,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
R.id.explorer_menu_delete -> { R.id.explorer_menu_delete -> {
val size = explorerAdapter.selectedItems.size val size = explorerAdapter.selectedItems.size
val dialog = ColoredAlertDialog(this) val dialog = ColoredAlertDialogBuilder(this)
dialog.setTitle(R.string.warning) dialog.setTitle(R.string.warning)
dialog.setPositiveButton(R.string.ok) { _, _ -> removeSelectedItems() } dialog.setPositiveButton(R.string.ok) { _, _ -> removeSelectedItems() }
dialog.setNegativeButton(R.string.cancel, null) dialog.setNegativeButton(R.string.cancel, null)
@ -399,7 +399,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
if (failedItem != null) { if (failedItem != null) {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(getString(R.string.remove_failed, failedItem)) .setMessage(getString(R.string.remove_failed, failedItem))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)

View File

@ -6,7 +6,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
class ExplorerActivityDrop : BaseExplorerActivity() { class ExplorerActivityDrop : BaseExplorerActivity() {
override fun init() { override fun init() {
@ -23,7 +23,7 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.explorer_menu_validate -> { R.id.explorer_menu_validate -> {
val alertDialog = ColoredAlertDialog(this) val alertDialog = ColoredAlertDialogBuilder(this)
alertDialog.setCancelable(false) alertDialog.setCancelable(false)
alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() }
var error_msg: String? = null var error_msg: String? = null

View File

@ -5,7 +5,7 @@ import android.view.View
import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.BaseActivity
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.GocryptfsVolume import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
abstract class FileViewerActivity: BaseActivity() { abstract class FileViewerActivity: BaseActivity() {
lateinit var gocryptfsVolume: GocryptfsVolume lateinit var gocryptfsVolume: GocryptfsVolume
@ -49,7 +49,7 @@ abstract class FileViewerActivity: BaseActivity() {
if (success){ if (success){
return fileBuff return fileBuff
} else { } else {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.read_file_failed) .setMessage(R.string.read_file_failed)
.setCancelable(false) .setCancelable(false)
@ -57,7 +57,7 @@ abstract class FileViewerActivity: BaseActivity() {
.show() .show()
} }
} catch (e: OutOfMemoryError){ } catch (e: OutOfMemoryError){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.outofmemoryerror_msg) .setMessage(R.string.outofmemoryerror_msg)
.setCancelable(false) .setCancelable(false)
@ -66,7 +66,7 @@ abstract class FileViewerActivity: BaseActivity() {
} }
} else { } else {
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.get_size_failed) .setMessage(R.string.get_size_failed)
.setCancelable(false) .setCancelable(false)

View File

@ -4,24 +4,32 @@ import androidx.appcompat.app.AlertDialog
import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer 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.LoopingMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource
import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
abstract class MediaPlayer: FileViewerActivity() { abstract class MediaPlayer: FileViewerActivity() {
private lateinit var player: SimpleExoPlayer private lateinit var player: SimpleExoPlayer
private var currentWindow = 0 private var currentWindow = 0
private var playbackPosition: Long = 0 private var playbackPosition: Long = 0
private lateinit var errorDialog: AlertDialog.Builder private lateinit var errorDialog: AlertDialog
override fun viewFile() { override fun viewFile() {
errorDialog = ColoredAlertDialog(this) errorDialog = ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.playing_failed) .setMessage(R.string.playing_failed)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ -> finish() } .setPositiveButton(R.string.ok) { _, _ -> finish() }
.create()
} }
abstract fun bindPlayer(player: SimpleExoPlayer) abstract fun bindPlayer(player: SimpleExoPlayer)
@ -30,7 +38,16 @@ abstract class MediaPlayer: FileViewerActivity() {
player = SimpleExoPlayer.Builder(this).build() player = SimpleExoPlayer.Builder(this).build()
bindPlayer(player) bindPlayer(player)
val dataSourceFactory = GocryptfsDataSource.Factory(gocryptfsVolume, filePath) 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.seekTo(currentWindow, playbackPosition)
player.playWhenReady = true player.playWhenReady = true
player.addListener(object : Player.EventListener{ player.addListener(object : Player.EventListener{

View File

@ -10,7 +10,7 @@ import android.widget.Toast
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.GocryptfsVolume 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.ByteArrayInputStream
import java.io.File import java.io.File
@ -30,7 +30,7 @@ class TextEditor: FileViewerActivity() {
try { try {
loadLayout(String(it)) loadLayout(String(it))
} catch (e: OutOfMemoryError){ } catch (e: OutOfMemoryError){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.outofmemoryerror_msg) .setMessage(R.string.outofmemoryerror_msg)
.setCancelable(false) .setCancelable(false)
@ -98,7 +98,7 @@ class TextEditor: FileViewerActivity() {
private fun checkSaveAndExit(){ private fun checkSaveAndExit(){
if (changedSinceLastSave){ if (changedSinceLastSave){
ColoredAlertDialog(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setMessage(R.string.ask_save) .setMessage(R.string.ask_save)
.setPositiveButton(getString(R.string.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.KeyGenParameterSpec
import android.security.keystore.KeyProperties import android.security.keystore.KeyProperties
import android.util.Base64 import android.util.Base64
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.ConstValues import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.security.KeyStore import java.security.KeyStore
import javax.crypto.* import javax.crypto.*
import javax.crypto.spec.GCMParameterSpec 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) { class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivity, private val shared_prefs: SharedPreferences) {
private var isPrepared = false private var isPrepared = false
var isListening = false var isListening = false
var authenticationFailed = false private var authenticationFailed = false
private val shared_prefs_editor: SharedPreferences.Editor = shared_prefs.edit() private val sharedPrefsEditor: SharedPreferences.Editor = shared_prefs.edit()
private val fingerprintManager = activityContext.getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager private val fingerprintManager = activityContext.getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager
private lateinit var root_cipher_dir: String private lateinit var rootCipherDir: String
private lateinit var action_description: String private lateinit var actionDescription: String
private lateinit var onAuthenticationResult: (success: Boolean) -> Unit private lateinit var onAuthenticationResult: (success: Boolean) -> Unit
private lateinit var onPasswordDecrypted: (password: ByteArray) -> Unit private lateinit var onPasswordDecrypted: (password: ByteArray) -> Unit
private lateinit var keyStore: KeyStore 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 SUCCESS_DISMISS_DIALOG_DELAY: Long = 400
private const val FAILED_DISMISS_DIALOG_DELAY: Long = 800 private const val FAILED_DISMISS_DIALOG_DELAY: Long = 800
} }
private fun reset_hash_storage() { private fun resetHashStorage() {
keyStore.deleteEntry(KEY_ALIAS) keyStore.deleteEntry(KEY_ALIAS)
val saved_volume_paths = shared_prefs.getStringSet(ConstValues.saved_volumes_key, HashSet<String>()) as Set<String> val savedVolumePaths = shared_prefs.getStringSet(ConstValues.saved_volumes_key, HashSet<String>()) as Set<String>
for (path in saved_volume_paths){ for (path in savedVolumePaths){
val saved_hash = shared_prefs.getString(path, null) val savedHash = shared_prefs.getString(path, null)
if (saved_hash != null){ if (savedHash != null){
shared_prefs_editor.remove(path) sharedPrefsEditor.remove(path)
} }
} }
shared_prefs_editor.apply() sharedPrefsEditor.apply()
Toast.makeText(activityContext, activityContext.getString(R.string.hash_storage_reset), Toast.LENGTH_SHORT).show() 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() keyGenerator.generateKey()
} }
cipher = Cipher.getInstance(CIPHER_TYPE) cipher = Cipher.getInstance(CIPHER_TYPE)
fingerprintFragment = FingerprintFragment(root_cipher_dir, action_description, ::stopListening) fingerprintFragment = FingerprintFragment(rootCipherDir, actionDescription, ::stopListening)
isPrepared = true isPrepared = true
} }
fun encryptAndSave(plainText: ByteArray, root_cipher_dir: String, onAuthenticationResult: (success: Boolean) -> Unit){ fun encryptAndSave(plainText: ByteArray, root_cipher_dir: String, onAuthenticationResult: (success: Boolean) -> Unit){
if (shared_prefs.getString(root_cipher_dir, null) == null){ if (shared_prefs.getString(root_cipher_dir, null) == null){
this.root_cipher_dir = root_cipher_dir this.rootCipherDir = root_cipher_dir
this.action_description = activityContext.getString(R.string.encrypt_action_description) this.actionDescription = activityContext.getString(R.string.encrypt_action_description)
this.onAuthenticationResult = onAuthenticationResult this.onAuthenticationResult = onAuthenticationResult
if (!isPrepared){ if (!isPrepared){
prepare() prepare()
@ -117,8 +115,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
} }
} }
fun decrypt(cipherText: String, root_cipher_dir: String, onPasswordDecrypted: (password: ByteArray) -> Unit){ fun decrypt(cipherText: String, root_cipher_dir: String, onPasswordDecrypted: (password: ByteArray) -> Unit){
this.root_cipher_dir = root_cipher_dir this.rootCipherDir = root_cipher_dir
this.action_description = activityContext.getString(R.string.decrypt_action_description) this.actionDescription = activityContext.getString(R.string.decrypt_action_description)
this.onPasswordDecrypted = onPasswordDecrypted this.onPasswordDecrypted = onPasswordDecrypted
if (!isPrepared){ if (!isPrepared){
prepare() prepare()
@ -136,8 +134,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
cancellationSignal = CancellationSignal() cancellationSignal = CancellationSignal()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val biometricPrompt = BiometricPrompt.Builder(activityContext) val biometricPrompt = BiometricPrompt.Builder(activityContext)
.setTitle(root_cipher_dir) .setTitle(rootCipherDir)
.setSubtitle(action_description) .setSubtitle(actionDescription)
.setDescription(activityContext.getString(R.string.fingerprint_instruction)) .setDescription(activityContext.getString(R.string.fingerprint_instruction))
.setNegativeButton(activityContext.getString(R.string.cancel), activityContext.mainExecutor, DialogInterface.OnClickListener{_, _ -> .setNegativeButton(activityContext.getString(R.string.cancel), activityContext.mainExecutor, DialogInterface.OnClickListener{_, _ ->
cancellationSignal.cancel() cancellationSignal.cancel()
@ -180,7 +178,7 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
if (!authenticationFailed){ if (!authenticationFailed){
if (fingerprintFragment.isAdded){ if (fingerprintFragment.isAdded){
fingerprintFragment.image_fingerprint.setColorFilter(ContextCompat.getColor(activityContext, R.color.fingerprint_failed)) 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) handler.postDelayed({ fingerprintFragment.dismiss() }, 1000)
} }
if (actionMode == Cipher.ENCRYPT_MODE){ if (actionMode == Cipher.ENCRYPT_MODE){
@ -215,8 +213,8 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
val cipherText = cipher.doFinal(dataToProcess) val cipherText = cipher.doFinal(dataToProcess)
val encodedCipherText = Base64.encodeToString(cipherText, 0) val encodedCipherText = Base64.encodeToString(cipherText, 0)
val encodedIv = Base64.encodeToString(cipher.iv, 0) val encodedIv = Base64.encodeToString(cipher.iv, 0)
shared_prefs_editor.putString(root_cipher_dir, "$encodedIv:$encodedCipherText") sharedPrefsEditor.putString(rootCipherDir, "$encodedIv:$encodedCipherText")
shared_prefs_editor.apply() sharedPrefsEditor.apply()
handler.postDelayed({ handler.postDelayed({
if (fingerprintFragment.isAdded){ if (fingerprintFragment.isAdded){
fingerprintFragment.dismiss() fingerprintFragment.dismiss()
@ -234,11 +232,11 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
onPasswordDecrypted(plainText) onPasswordDecrypted(plainText)
}, SUCCESS_DISMISS_DIALOG_DELAY) }, SUCCESS_DISMISS_DIALOG_DELAY)
} catch (e: AEADBadTagException){ } catch (e: AEADBadTagException){
ColoredAlertDialog(activityContext) ColoredAlertDialogBuilder(activityContext)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(activityContext.getString(R.string.MAC_verification_failed)) .setMessage(activityContext.getString(R.string.MAC_verification_failed))
.setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ -> .setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ ->
reset_hash_storage() resetHashStorage()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
@ -247,11 +245,11 @@ class FingerprintPasswordHashSaver(private val activityContext: AppCompatActivit
} }
} catch (e: IllegalBlockSizeException){ } catch (e: IllegalBlockSizeException){
stopListening() stopListening()
ColoredAlertDialog(activityContext) ColoredAlertDialogBuilder(activityContext)
.setTitle(R.string.authentication_error) .setTitle(R.string.authentication_error)
.setMessage(activityContext.getString(R.string.authentication_error_msg)) .setMessage(activityContext.getString(R.string.authentication_error_msg))
.setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ -> .setPositiveButton(activityContext.getString(R.string.reset_hash_storage)) { _, _ ->
reset_hash_storage() resetHashStorage()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()

View File

@ -5,7 +5,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.provider.RestrictedFileProvider import sushi.hardcore.droidfs.provider.RestrictedFileProvider
import sushi.hardcore.droidfs.widgets.ColoredAlertDialog import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.net.URLConnection import java.net.URLConnection
import java.util.* import java.util.*
@ -37,7 +37,7 @@ object ExternalProvider {
return Pair(tmpFileUri, getContentType(fileName, previous_content_type)) return Pair(tmpFileUri, getContentType(fileName, previous_content_type))
} }
} }
ColoredAlertDialog(context) ColoredAlertDialogBuilder(context)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(context.getString(R.string.export_failed, file_path)) .setMessage(context.getString(R.string.export_failed, file_path))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)

View File

@ -79,7 +79,7 @@ object Wiper {
return false return false
} }
} }
fun randomString(minSize: Int, maxSize: Int): String { private fun randomString(minSize: Int, maxSize: Int): String {
val r = Random() val r = Random()
val sb = StringBuilder() val sb = StringBuilder()
val length = r.nextInt(maxSize-minSize)+minSize val length = r.nextInt(maxSize-minSize)+minSize

View File

@ -1,10 +1,11 @@
package sushi.hardcore.droidfs.widgets package sushi.hardcore.droidfs.widgets
//import android.app.AlertDialog
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.content.Context 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){ private fun applyColor(dialog: AlertDialog){
dialog.setOnShowListener{ dialog.setOnShowListener{
val themeColor = ThemeColor.getThemeColor(context) val themeColor = ThemeColor.getThemeColor(context)

View File

@ -11,7 +11,7 @@ import android.widget.ListView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
class ColoredBorderListView: ListView { open class ColoredBorderListView: ListView {
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) {
applyColor() 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"/> <include layout="@layout/toolbar"/>
<ScrollView <ScrollView
android:fillViewport="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -94,7 +93,7 @@
android:layout_width="@dimen/change_password_activity_label_width" android:layout_width="@dimen/change_password_activity_label_width"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="New Password (confirmation):" android:text="@string/new_password_confirmation"
android:textSize="@dimen/edit_text_label_size" /> android:textSize="@dimen/edit_text_label_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText <sushi.hardcore.droidfs.widgets.ColoredEditText
@ -123,7 +122,7 @@
android:text="@string/fingerprint_save_checkbox_text" android:text="@string/fingerprint_save_checkbox_text"
android:onClick="onClickSavePasswordHash"/> android:onClick="onClickSavePasswordHash"/>
<sushi.hardcore.droidfs.widgets.ColoredBorderListView <sushi.hardcore.droidfs.widgets.NonScrollableColoredBorderListView
android:id="@+id/saved_path_listview" android:id="@+id/saved_path_listview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -7,7 +7,6 @@
<include layout="@layout/toolbar"/> <include layout="@layout/toolbar"/>
<ScrollView <ScrollView
android:fillViewport="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -83,7 +82,7 @@
android:text="@string/fingerprint_save_checkbox_text" android:text="@string/fingerprint_save_checkbox_text"
android:onClick="onClickSavePasswordHash"/> android:onClick="onClickSavePasswordHash"/>
<sushi.hardcore.droidfs.widgets.ColoredBorderListView <sushi.hardcore.droidfs.widgets.NonScrollableColoredBorderListView
android:id="@+id/saved_path_listview" android:id="@+id/saved_path_listview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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:id="@+id/text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18sp" android:textSize="@dimen/adapter_text_size"
android:layout_gravity="center"/> android:layout_gravity="center"/>
</LinearLayout> </LinearLayout>

View File

@ -19,7 +19,7 @@
android:id="@+id/text_element_name" android:id="@+id/text_element_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18sp"/> android:textSize="@dimen/adapter_text_size"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" 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="change_password_activity_label_width">100dp</dimen>
<dimen name="action_activity_button_hor_margin">60dp</dimen> <dimen name="action_activity_button_hor_margin">60dp</dimen>
<dimen name="action_activity_button_height">60dp</dimen> <dimen name="action_activity_button_height">60dp</dimen>
<dimen name="adapter_text_size">18sp</dimen>
</resources> </resources>

View File

@ -30,7 +30,7 @@
<string name="success_volume_create_msg">The volume has been successfully created.</string> <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="create_volume_failed">The volume creation has failed.</string>
<string name="open_volume_failed">Open 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="share_chooser">Share File</string>
<string name="storage_unavailable">Storage unavailable</string> <string name="storage_unavailable">Storage unavailable</string>
<string name="storage_unavailable_msg">DroidFS can\'t work without storage access.</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="new_password">New password:</string>
<string name="success_change_password">Password successfully changed !</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="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_menu_label">Encrypt with DroidFS</string>
<string name="share_intent_parsing_failed">Failed to handle the share request.</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> <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="new_file">New File</string>
<string name="enter_file_name">File name:</string> <string name="enter_file_name">File name:</string>
<string name="file_creation_failed">Failed to create the file.</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> </resources>

View File

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