Volume on SD cards warning

This commit is contained in:
Hardcore Sushi 2020-08-25 14:10:46 +02:00
parent 29b12898a4
commit f44a702647
9 changed files with 285 additions and 151 deletions

View File

@ -20,6 +20,7 @@ import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
import sushi.hardcore.droidfs.util.* import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
import java.util.* import java.util.*
class ChangePasswordActivity : BaseActivity() { class ChangePasswordActivity : BaseActivity() {
@ -80,9 +81,25 @@ class ChangePasswordActivity : BaseActivity() {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (data != null) { if (data?.data != null) {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){
edit_volume_path.setText(path) val path = PathUtils.getFullPathFromTreeUri(data.data, this)
if (path != null){
edit_volume_path.setText(path)
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.change_pwd_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} }
} }
@ -93,37 +110,52 @@ class ChangePasswordActivity : BaseActivity() {
if (rootCipherDir.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) if (!File(rootCipherDir).canWrite()){
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.change_pwd_cant_write_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
} else {
changePassword(null)
}
} }
} }
private fun changePassword(givenHash: ByteArray?){ private fun changePassword(givenHash: ByteArray?){
object : LoadingTask(this, R.string.loading_msg_change_password){ val newPassword = edit_new_password.text.toString().toCharArray()
override fun doTask(activity: AppCompatActivity) { val newPasswordConfirm = edit_new_password_confirm.text.toString().toCharArray()
val newPassword = edit_new_password.text.toString().toCharArray() if (!newPassword.contentEquals(newPasswordConfirm)) {
val newPasswordConfirm = edit_new_password_confirm.text.toString().toCharArray() Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
if (!newPassword.contentEquals(newPasswordConfirm)) { } else {
stopTaskWithToast(R.string.passwords_mismatch) object : LoadingTask(this, R.string.loading_msg_change_password) {
} else { override fun doTask(activity: AppCompatActivity) {
val oldPassword = edit_old_password.text.toString().toCharArray() val oldPassword = edit_old_password.text.toString().toCharArray()
var returnedHash: ByteArray? = null var returnedHash: ByteArray? = null
if (usf_fingerprint && checkbox_save_password.isChecked){ if (usf_fingerprint && checkbox_save_password.isChecked) {
returnedHash = ByteArray(GocryptfsVolume.KeyLen) returnedHash = ByteArray(GocryptfsVolume.KeyLen)
} }
var changePasswordImmediately = true var changePasswordImmediately = true
if (givenHash == null){ if (givenHash == null) {
val cipherText = sharedPrefs.getString(rootCipherDir, null) val cipherText = sharedPrefs.getString(rootCipherDir, null)
if (cipherText != null){ //password hash saved if (cipherText != null) { //password hash saved
stopTask { stopTask {
fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword) fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword)
} }
changePasswordImmediately = false changePasswordImmediately = false
} }
} }
if (changePasswordImmediately){ if (changePasswordImmediately) {
if (GocryptfsVolume.changePassword(rootCipherDir, oldPassword, givenHash, newPassword, returnedHash)) { if (GocryptfsVolume.changePassword(
rootCipherDir,
oldPassword,
givenHash,
newPassword,
returnedHash
)
) {
val editor = sharedPrefs.edit() val editor = sharedPrefs.edit()
if (sharedPrefs.getString(rootCipherDir, null) != null){ if (sharedPrefs.getString(rootCipherDir, null) != null) {
editor.remove(rootCipherDir) editor.remove(rootCipherDir)
editor.apply() editor.apply()
} }
@ -133,17 +165,20 @@ class ChangePasswordActivity : BaseActivity() {
val newSavedVolumesPaths = oldSavedVolumesPaths.toMutableList() val newSavedVolumesPaths = oldSavedVolumesPaths.toMutableList()
if (!oldSavedVolumesPaths.contains(rootCipherDir)) { if (!oldSavedVolumesPaths.contains(rootCipherDir)) {
newSavedVolumesPaths.add(rootCipherDir) newSavedVolumesPaths.add(rootCipherDir)
editor.putStringSet(ConstValues.saved_volumes_key, newSavedVolumesPaths.toSet()) editor.putStringSet(
ConstValues.saved_volumes_key,
newSavedVolumesPaths.toSet()
)
editor.apply() editor.apply()
} }
if (checkbox_save_password.isChecked && returnedHash != null){ if (checkbox_save_password.isChecked && returnedHash != null) {
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ -> fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir) { _ ->
stopTask { onPasswordChanged() } stopTask { onPasswordChanged() }
} }
continueImmediately = false continueImmediately = false
} }
} }
if (continueImmediately){ if (continueImmediately) {
stopTask { onPasswordChanged() } stopTask { onPasswordChanged() }
} }
} else { } else {
@ -158,8 +193,10 @@ class ChangePasswordActivity : BaseActivity() {
} }
Arrays.fill(oldPassword, 0.toChar()) Arrays.fill(oldPassword, 0.toChar())
} }
Arrays.fill(newPassword, 0.toChar()) override fun doFinally(activity: AppCompatActivity) {
Arrays.fill(newPasswordConfirm, 0.toChar()) Arrays.fill(newPassword, 0.toChar())
Arrays.fill(newPasswordConfirm, 0.toChar())
}
} }
} }
} }

View File

@ -5,12 +5,9 @@ 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 androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
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_save_password
import kotlinx.android.synthetic.main.activity_create.edit_password
import kotlinx.android.synthetic.main.activity_create.edit_volume_path
import kotlinx.android.synthetic.main.toolbar.* import kotlinx.android.synthetic.main.toolbar.*
import sushi.hardcore.droidfs.explorers.ExplorerActivity import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
@ -19,6 +16,7 @@ import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
class CreateActivity : BaseActivity() { class CreateActivity : BaseActivity() {
companion object { companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1 private const val PICK_DIRECTORY_REQUEST_CODE = 1
@ -53,91 +51,129 @@ class CreateActivity : BaseActivity() {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (data != null) { if (data?.data != null) {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){
edit_volume_path.setText(path) val path = PathUtils.getFullPathFromTreeUri(data.data, this)
if (path != null){
edit_volume_path.setText(path)
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.create_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} }
} }
} }
fun onClickCreate(view: View?) { fun onClickCreate(view: View?) {
object: LoadingTask(this, R.string.loading_msg_create){ rootCipherDir = edit_volume_path.text.toString()
override fun doTask(activity: AppCompatActivity) { if (rootCipherDir.isEmpty()) {
val password = edit_password.text.toString().toCharArray() Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show()
val passwordConfirm = edit_password_confirm.text.toString().toCharArray() } else {
if (!password.contentEquals(passwordConfirm)) { val password = edit_password.text.toString().toCharArray()
stopTaskWithToast(R.string.passwords_mismatch) val passwordConfirm = edit_password_confirm.text.toString().toCharArray()
} else { if (!password.contentEquals(passwordConfirm)) {
rootCipherDir = edit_volume_path.text.toString() Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
val volumePathFile = File(rootCipherDir) } else {
var goodDirectory = false object: LoadingTask(this, R.string.loading_msg_create){
if (!volumePathFile.isDirectory) { override fun doTask(activity: AppCompatActivity) {
if (volumePathFile.mkdirs()) { val volumePathFile = File(rootCipherDir)
goodDirectory = true var goodDirectory = false
} else { if (!volumePathFile.isDirectory) {
stopTaskWithToast(R.string.error_mkdir) if (volumePathFile.mkdirs()) {
}
} else {
val dirContent = volumePathFile.list()
if (dirContent != null){
if (dirContent.isEmpty()) {
goodDirectory = true goodDirectory = true
} else { } else {
stopTaskWithToast(R.string.dir_not_empty) stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.warning)
.setMessage(R.string.create_cant_write_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} else { } else {
stopTaskWithToast(R.string.listdir_null_error_msg) val dirContent = volumePathFile.list()
} if (dirContent != null){
} if (dirContent.isEmpty()) {
if (goodDirectory) { if (volumePathFile.canWrite()){
if (GocryptfsVolume.createVolume(rootCipherDir, password, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator)) { goodDirectory = true
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 { } else {
newSavedVolumesPaths.add(rootCipherDir) stopTask {
editor.putStringSet(ConstValues.saved_volumes_key, newSavedVolumesPaths.toSet()) ColoredAlertDialogBuilder(activity)
} .setTitle(R.string.warning)
editor.apply() .setMessage(R.string.create_cant_write_error_msg)
if (checkbox_save_password.isChecked && returnedHash != null){ .setPositiveButton(R.string.ok, null)
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ -> .show()
stopTask { startExplorer() }
} }
startExplorerImmediately = false
} }
} } else {
if (startExplorerImmediately){ stopTaskWithToast(R.string.dir_not_empty)
stopTask { startExplorer() }
} }
} else { } else {
stopTaskWithToast(R.string.open_volume_failed) stopTaskWithToast(R.string.listdir_null_error_msg)
} }
} else { }
stopTask { if (goodDirectory) {
ColoredAlertDialogBuilder(activity) if (GocryptfsVolume.createVolume(rootCipherDir, password, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator)) {
.setTitle(R.string.error) var returnedHash: ByteArray? = null
.setMessage(R.string.create_volume_failed) if (usf_fingerprint && checkbox_save_password.isChecked){
.setPositiveButton(R.string.ok, null) returnedHash = ByteArray(GocryptfsVolume.KeyLen)
.show() }
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){
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ ->
stopTask { startExplorer() }
}
startExplorerImmediately = false
}
}
if (startExplorerImmediately){
stopTask { startExplorer() }
}
} else {
stopTaskWithToast(R.string.open_volume_failed)
}
} else {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(R.string.create_volume_failed)
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} }
} }
override fun doFinally(activity: AppCompatActivity) {
Arrays.fill(password, 0.toChar())
Arrays.fill(passwordConfirm, 0.toChar())
}
} }
Arrays.fill(password, 0.toChar())
Arrays.fill(passwordConfirm, 0.toChar())
} }
} }
} }

View File

@ -7,8 +7,16 @@ import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemClickListener
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_change_password.*
import kotlinx.android.synthetic.main.activity_create.*
import kotlinx.android.synthetic.main.activity_open.* import kotlinx.android.synthetic.main.activity_open.*
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
import kotlinx.android.synthetic.main.activity_open.edit_volume_path
import kotlinx.android.synthetic.main.activity_open.saved_path_listview
import kotlinx.android.synthetic.main.toolbar.* import kotlinx.android.synthetic.main.toolbar.*
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import sushi.hardcore.droidfs.explorers.ExplorerActivity import sushi.hardcore.droidfs.explorers.ExplorerActivity
@ -82,52 +90,81 @@ class OpenActivity : BaseActivity() {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (data != null) { if (data?.data != null) {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){
edit_volume_path.setText(path) val path = PathUtils.getFullPathFromTreeUri(data.data, this)
if (path != null){
edit_volume_path.setText(path)
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.open_on_sdcard_warning)
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} }
} }
} }
fun onClickOpen(view: View?) { fun onClickOpen(view: View?) {
rootCipherDir = edit_volume_path.text.toString()
if (rootCipherDir.isEmpty()) {
Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show()
} else {
if (!File(rootCipherDir).canWrite()){
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.open_cant_write_warning)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ -> openVolume() }
.show()
} else {
openVolume()
}
}
}
private fun openVolume(){
object : LoadingTask(this, R.string.loading_msg_open){ object : LoadingTask(this, R.string.loading_msg_open){
override fun doTask(activity: AppCompatActivity) { override fun doTask(activity: AppCompatActivity) {
rootCipherDir = edit_volume_path.text.toString() //fresh get in case of manual rewrite val password = edit_password.text.toString().toCharArray()
if (rootCipherDir.isEmpty()) { var returnedHash: ByteArray? = null
stopTaskWithToast(R.string.enter_volume_path) if (usf_fingerprint && checkbox_save_password.isChecked){
} else { returnedHash = ByteArray(GocryptfsVolume.KeyLen)
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) { _ ->
stopTask { startExplorer() }
}
startExplorerImmediately = false
}
}
if (startExplorerImmediately){
stopTask { startExplorer() }
}
} else {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.open_volume_failed)
.setMessage(R.string.open_volume_failed_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
Arrays.fill(password, 0.toChar())
} }
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) { _ ->
stopTask { startExplorer() }
}
startExplorerImmediately = false
}
}
if (startExplorerImmediately){
stopTask { startExplorer() }
}
} else {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.open_volume_failed)
.setMessage(R.string.open_volume_failed_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
Arrays.fill(password, 0.toChar())
} }
} }
} }

View File

@ -194,7 +194,9 @@ open class BaseExplorerActivity : BaseActivity() {
} }
} }
total_size_text.text = getString(R.string.total_size, PathUtils.formatSize(totalSize)) total_size_text.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
explorerAdapter.notifyDataSetChanged() runOnUiThread {
explorerAdapter.notifyDataSetChanged()
}
}.start() }.start()
} }

View File

@ -6,7 +6,9 @@ import android.net.Uri;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.io.File; import java.io.File;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -81,24 +83,35 @@ public class PathUtils {
return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))+" "+units[digitGroups]; return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))+" "+units[digitGroups];
} }
public static Boolean isTreeUriOnPrimaryStorage(Uri treeUri){
String volumeId = getVolumeIdFromTreeUri(treeUri);
if (volumeId != null) {
return volumeId.equals(PRIMARY_VOLUME_NAME) || volumeId.equals("home") || volumeId.equals("downloads");
} else {
return false;
}
}
private static final String PRIMARY_VOLUME_NAME = "primary"; private static final String PRIMARY_VOLUME_NAME = "primary";
@Nullable @Nullable
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) { public static String getFullPathFromTreeUri(@Nullable Uri treeUri, Context context) {
if (treeUri == null) return null; if (treeUri == null) return null;
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con); if ("content".equalsIgnoreCase(treeUri.getScheme())) {
if (volumePath == null) return File.separator; String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),context);
if (volumePath.endsWith(File.separator)) if (volumePath == null) return null;
volumePath = volumePath.substring(0, volumePath.length() - 1); if (volumePath.endsWith(File.separator))
String documentPath = getDocumentPathFromTreeUri(treeUri); volumePath = volumePath.substring(0, volumePath.length() - 1);
if (documentPath.endsWith(File.separator)) String documentPath = getDocumentPathFromTreeUri(treeUri);
documentPath = documentPath.substring(0, documentPath.length() - 1); if (documentPath.endsWith(File.separator))
if (documentPath.length() > 0) { documentPath = documentPath.substring(0, documentPath.length() - 1);
if (documentPath.startsWith(File.separator)) if (documentPath.length() > 0) {
return volumePath + documentPath; return path_join(volumePath, documentPath);
else }
return volumePath + File.separator + documentPath; else return volumePath;
} else if ("file".equalsIgnoreCase(treeUri.getScheme())) {
return treeUri.getPath();
} }
else return volumePath; return null;
} }
private static String getVolumePath(final String volumeId, Context context) { private static String getVolumePath(final String volumeId, Context context) {
@ -117,7 +130,6 @@ public class PathUtils {
Object storageVolumeElement = Array.get(result, i); Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement); String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement); Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId)) if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
return (String) getPath.invoke(storageVolumeElement); return (String) getPath.invoke(storageVolumeElement);
if (uuid != null && uuid.equals(volumeId)) if (uuid != null && uuid.equals(volumeId))

View File

@ -143,7 +143,7 @@
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin" android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom" android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
android:onClick="onClickChangePassword" android:onClick="onClickChangePassword"
android:text="@string/change_volume_password" android:text="@string/change_password"
style="@style/button"/> style="@style/button"/>
</LinearLayout> </LinearLayout>

View File

@ -114,7 +114,7 @@
android:layout_height="@dimen/action_activity_button_height" android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin" android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:onClick="onClickCreate" android:onClick="onClickCreate"
android:text="@string/create_volume" android:text="@string/create"
style="@style/button"/> style="@style/button"/>
</LinearLayout> </LinearLayout>

View File

@ -102,7 +102,7 @@
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin" android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom" android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
android:onClick="onClickOpen" android:onClick="onClickOpen"
android:text="@string/open_volume" android:text="@string/open"
style="@style/button"/> style="@style/button"/>
</LinearLayout> </LinearLayout>

View File

@ -1,8 +1,11 @@
<resources> <resources>
<string name="app_name">DroidFS</string> <string name="app_name">DroidFS</string>
<string name="open_volume">OPEN A VOLUME</string> <string name="open_volume">Open a volume</string>
<string name="create_volume">CREATE A VOLUME</string> <string name="create_volume">Create a volume</string>
<string name="change_volume_password">CHANGE VOLUME PASSWORD</string> <string name="change_volume_password">Change a volume\'s password</string>
<string name="open">Open</string>
<string name="create">Create</string>
<string name="change_password">Change password</string>
<string name="password">Password:</string> <string name="password">Password:</string>
<string name="password_confirm">Password (confirmation):</string> <string name="password_confirm">Password (confirmation):</string>
<string name="volume_path">Volume Path:</string> <string name="volume_path">Volume Path:</string>
@ -163,4 +166,11 @@
<string name="choose_filter">Choose filter</string> <string name="choose_filter">Choose filter</string>
<string name="filters_warning">Filters can only be applied to reduced quality images. If you want to take high definition photos, do not apply any filters.</string> <string name="filters_warning">Filters can only be applied to reduced quality images. If you want to take high definition photos, do not apply any filters.</string>
<string name="timer_empty_error_msg">Please enter a numeric value</string> <string name="timer_empty_error_msg">Please enter a numeric value</string>
<string name="path_from_uri_null_error_msg">Failed to retrieve the selected path.</string>
<string name="create_cant_write_error_msg">DroidFS doesn\'t have write access to this path. Please try another location.</string>
<string name="create_on_sdcard_error_msg">DroidFS can\'t write on removable SD cards, please select a path on internal storage.</string>
<string name="open_on_sdcard_warning">DroidFS can\'t write on removable SD cards. You will only have read-only access to the volumes therein.</string>
<string name="open_cant_write_warning">DroidFS doesn\'t have write access to this path. You will only have read-only access to this volume.</string>
<string name="change_pwd_cant_write_error_msg">DroidFS doesn\'t have write access to this path. You can try to move the volume to a writable location.</string>
<string name="change_pwd_on_sdcard_error_msg">DroidFS can\'t write on removable SD cards, please move the volume to internal storage.</string>
</resources> </resources>