Multi volume openings
This commit is contained in:
parent
2d165c4a20
commit
1a1d3ea570
@ -88,9 +88,11 @@ dependencies {
|
|||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.9.0'
|
implementation 'androidx.core:core-ktx:1.9.0'
|
||||||
implementation "androidx.appcompat:appcompat:1.6.0"
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
|
def lifecycle_version = "2.5.1"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
|
||||||
|
|
||||||
implementation "androidx.sqlite:sqlite-ktx:2.3.0"
|
implementation "androidx.sqlite:sqlite-ktx:2.3.0"
|
||||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/BaseTheme">
|
android:theme="@style/BaseTheme"
|
||||||
|
android:name=".VolumeManagerApp">
|
||||||
<activity android:name=".MainActivity" android:exported="true">
|
<activity android:name=".MainActivity" android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
@ -4,7 +4,6 @@ import android.content.SharedPreferences
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
|
|
||||||
open class BaseActivity: AppCompatActivity() {
|
open class BaseActivity: AppCompatActivity() {
|
||||||
protected lateinit var sharedPrefs: SharedPreferences
|
protected lateinit var sharedPrefs: SharedPreferences
|
||||||
@ -13,7 +12,7 @@ open class BaseActivity: AppCompatActivity() {
|
|||||||
private var shouldCheckTheme = true
|
private var shouldCheckTheme = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
sharedPrefs = (application as VolumeManagerApp).sharedPreferences
|
||||||
themeValue = sharedPrefs.getString(Constants.THEME_VALUE_KEY, Constants.DEFAULT_THEME_VALUE)!!
|
themeValue = sharedPrefs.getString(Constants.THEME_VALUE_KEY, Constants.DEFAULT_THEME_VALUE)!!
|
||||||
if (shouldCheckTheme && applyCustomTheme) {
|
if (shouldCheckTheme && applyCustomTheme) {
|
||||||
when (themeValue) {
|
when (themeValue) {
|
||||||
|
@ -17,7 +17,6 @@ import android.view.animation.RotateAnimation
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.addCallback
|
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.camera.camera2.interop.Camera2CameraInfo
|
import androidx.camera.camera2.interop.Camera2CameraInfo
|
||||||
import androidx.camera.core.*
|
import androidx.camera.core.*
|
||||||
@ -29,7 +28,6 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
||||||
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
|
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
import sushi.hardcore.droidfs.util.IntentUtils
|
||||||
@ -63,14 +61,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
binding.imageTimer.setImageResource(R.drawable.icon_timer_off)
|
binding.imageTimer.setImageResource(R.drawable.icon_timer_off)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private var usf_keep_open = false
|
|
||||||
private lateinit var sensorOrientationListener: SensorOrientationListener
|
private lateinit var sensorOrientationListener: SensorOrientationListener
|
||||||
private var previousOrientation: Float = 0f
|
private var previousOrientation: Float = 0f
|
||||||
private lateinit var orientedIcons: List<ImageView>
|
private lateinit var orientedIcons: List<ImageView>
|
||||||
private lateinit var encryptedVolume: EncryptedVolume
|
private lateinit var encryptedVolume: EncryptedVolume
|
||||||
private lateinit var outputDirectory: String
|
private lateinit var outputDirectory: String
|
||||||
private var isFinishingIntentionally = false
|
|
||||||
private var isAskingPermissions = false
|
|
||||||
private var permissionsGranted = false
|
private var permissionsGranted = false
|
||||||
private lateinit var executor: Executor
|
private lateinit var executor: Executor
|
||||||
private lateinit var cameraProvider: ProcessCameraProvider
|
private lateinit var cameraProvider: ProcessCameraProvider
|
||||||
@ -92,7 +87,6 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
|
|
||||||
binding = ActivityCameraBinding.inflate(layoutInflater)
|
binding = ActivityCameraBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
supportActionBar?.hide()
|
supportActionBar?.hide()
|
||||||
@ -103,7 +97,6 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
|
||||||
permissionsGranted = true
|
permissionsGranted = true
|
||||||
} else {
|
} else {
|
||||||
isAskingPermissions = true
|
|
||||||
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
|
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -220,7 +213,6 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
binding.takePhotoButton.visibility = View.GONE
|
binding.takePhotoButton.visibility = View.GONE
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
||||||
isAskingPermissions = true
|
|
||||||
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION_REQUEST_CODE)
|
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION_REQUEST_CODE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,17 +272,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
|
||||||
isFinishingIntentionally = true
|
|
||||||
isEnabled = false
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
isAskingPermissions = false
|
|
||||||
if (grantResults.size == 1) {
|
if (grantResults.size == 1) {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
@ -301,10 +287,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
.setTitle(R.string.error)
|
.setTitle(R.string.error)
|
||||||
.setMessage(R.string.camera_perm_needed)
|
.setMessage(R.string.camera_perm_needed)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ -> finish() }.show()
|
||||||
isFinishingIntentionally = true
|
|
||||||
finish()
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
AUDIO_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
AUDIO_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
if (videoCapture != null) {
|
if (videoCapture != null) {
|
||||||
@ -431,10 +414,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
.setTitle(R.string.error)
|
.setTitle(R.string.error)
|
||||||
.setMessage(R.string.picture_save_failed)
|
.setMessage(R.string.picture_save_failed)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ -> finish() }
|
||||||
isFinishingIntentionally = true
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,32 +465,18 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
if (!isFinishingIntentionally) {
|
|
||||||
encryptedVolume.close()
|
|
||||||
RestrictedFileProvider.wipeAll(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
super.onStop()
|
|
||||||
if (!isFinishing && !usf_keep_open){
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
sensorOrientationListener.remove(this)
|
sensorOrientationListener.remove(this)
|
||||||
if (!isAskingPermissions && !usf_keep_open) {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
sensorOrientationListener.addListener(this)
|
if (encryptedVolume.isClosed()) {
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
sensorOrientationListener.addListener(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOrientationChange(newOrientation: Int) {
|
override fun onOrientationChange(newOrientation: Int) {
|
||||||
|
@ -20,12 +20,10 @@ import kotlinx.coroutines.launch
|
|||||||
import sushi.hardcore.droidfs.Constants.DEFAULT_VOLUME_KEY
|
import sushi.hardcore.droidfs.Constants.DEFAULT_VOLUME_KEY
|
||||||
import sushi.hardcore.droidfs.adapters.VolumeAdapter
|
import sushi.hardcore.droidfs.adapters.VolumeAdapter
|
||||||
import sushi.hardcore.droidfs.add_volume.AddVolumeActivity
|
import sushi.hardcore.droidfs.add_volume.AddVolumeActivity
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
||||||
import sushi.hardcore.droidfs.databinding.ActivityMainBinding
|
import sushi.hardcore.droidfs.databinding.ActivityMainBinding
|
||||||
import sushi.hardcore.droidfs.databinding.DialogDeleteVolumeBinding
|
import sushi.hardcore.droidfs.databinding.DialogDeleteVolumeBinding
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerRouter
|
import sushi.hardcore.droidfs.explorers.ExplorerRouter
|
||||||
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
import sushi.hardcore.droidfs.util.IntentUtils
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
@ -33,11 +31,15 @@ import sushi.hardcore.droidfs.widgets.EditTextDialog
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
||||||
|
companion object {
|
||||||
|
private const val OPEN_DEFAULT_VOLUME = "openDefault"
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var volumeDatabase: VolumeDatabase
|
private lateinit var volumeDatabase: VolumeDatabase
|
||||||
|
private lateinit var volumeManager: VolumeManager
|
||||||
private lateinit var volumeAdapter: VolumeAdapter
|
private lateinit var volumeAdapter: VolumeAdapter
|
||||||
private lateinit var volumeOpener: VolumeOpener
|
private lateinit var volumeOpener: VolumeOpener
|
||||||
private var usfKeepOpen: Boolean = false
|
|
||||||
private var addVolume = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private var addVolume = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if ((explorerRouter.pickMode || explorerRouter.dropMode) && result.resultCode != AddVolumeActivity.RESULT_USER_BACK) {
|
if ((explorerRouter.pickMode || explorerRouter.dropMode) && result.resultCode != AddVolumeActivity.RESULT_USER_BACK) {
|
||||||
setResult(result.resultCode, result.data) // forward result
|
setResult(result.resultCode, result.data) // forward result
|
||||||
@ -54,7 +56,6 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
}
|
}
|
||||||
private lateinit var fileOperationService: FileOperationService
|
private lateinit var fileOperationService: FileOperationService
|
||||||
private lateinit var explorerRouter: ExplorerRouter
|
private lateinit var explorerRouter: ExplorerRouter
|
||||||
private var shouldCloseVolume = true // used when launched to pick file from another volume
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -80,10 +81,12 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
explorerRouter = ExplorerRouter(this, intent)
|
explorerRouter = ExplorerRouter(this, intent)
|
||||||
|
volumeManager = (application as VolumeManagerApp).volumeManager
|
||||||
volumeDatabase = VolumeDatabase(this)
|
volumeDatabase = VolumeDatabase(this)
|
||||||
volumeAdapter = VolumeAdapter(
|
volumeAdapter = VolumeAdapter(
|
||||||
this,
|
this,
|
||||||
volumeDatabase,
|
volumeDatabase,
|
||||||
|
(application as VolumeManagerApp).volumeManager,
|
||||||
!explorerRouter.pickMode && !explorerRouter.dropMode,
|
!explorerRouter.pickMode && !explorerRouter.dropMode,
|
||||||
!explorerRouter.dropMode,
|
!explorerRouter.dropMode,
|
||||||
this,
|
this,
|
||||||
@ -100,30 +103,31 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
addVolume.launch(Intent(this, AddVolumeActivity::class.java).also {
|
addVolume.launch(Intent(this, AddVolumeActivity::class.java).also {
|
||||||
if (explorerRouter.dropMode || explorerRouter.pickMode) {
|
if (explorerRouter.dropMode || explorerRouter.pickMode) {
|
||||||
IntentUtils.forwardIntent(intent, it)
|
IntentUtils.forwardIntent(intent, it)
|
||||||
shouldCloseVolume = false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
usfKeepOpen = sharedPrefs.getBoolean("usf_keep_open", false)
|
|
||||||
volumeOpener = VolumeOpener(this)
|
volumeOpener = VolumeOpener(this)
|
||||||
volumeOpener.defaultVolumeName?.let { name ->
|
|
||||||
try {
|
|
||||||
openVolume(volumeAdapter.volumes.first { it.name == name })
|
|
||||||
} catch (e: NoSuchElementException) {
|
|
||||||
unsetDefaultVolume()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
if (volumeAdapter.selectedItems.isNotEmpty()) {
|
if (volumeAdapter.selectedItems.isNotEmpty()) {
|
||||||
unselectAll()
|
unselectAll()
|
||||||
} else {
|
} else {
|
||||||
if (explorerRouter.pickMode) {
|
|
||||||
shouldCloseVolume = false
|
|
||||||
}
|
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
volumeOpener.defaultVolumeName?.let { name ->
|
||||||
|
val state = savedInstanceState?.getBoolean(OPEN_DEFAULT_VOLUME)
|
||||||
|
if (state == true || state == null) {
|
||||||
|
val volumeData = volumeAdapter.volumes.first { it.name == name }
|
||||||
|
if (!volumeManager.isOpen(volumeData)) {
|
||||||
|
try {
|
||||||
|
openVolume(volumeData)
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
unsetDefaultVolume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Intent(this, FileOperationService::class.java).also {
|
Intent(this, FileOperationService::class.java).also {
|
||||||
bindService(it, object : ServiceConnection {
|
bindService(it, object : ServiceConnection {
|
||||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||||
@ -148,6 +152,11 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
volumeOpener.defaultVolumeName = sharedPrefs.getString(DEFAULT_VOLUME_KEY, null)
|
volumeOpener.defaultVolumeName = sharedPrefs.getString(DEFAULT_VOLUME_KEY, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putBoolean(OPEN_DEFAULT_VOLUME, false)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSelectionChanged(size: Int) {
|
override fun onSelectionChanged(size: Int) {
|
||||||
title = if (size == 0) {
|
title = if (size == 0) {
|
||||||
getString(R.string.app_name)
|
getString(R.string.app_name)
|
||||||
@ -179,6 +188,11 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun removeVolume(volume: VolumeData) {
|
||||||
|
volumeManager.getVolumeId(volume)?.let { volumeManager.closeVolume(it) }
|
||||||
|
volumeDatabase.removeVolume(volume.name)
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeVolumes(volumes: List<VolumeData>, i: Int = 0, doDeleteVolumeContent: Boolean? = null) {
|
private fun removeVolumes(volumes: List<VolumeData>, i: Int = 0, doDeleteVolumeContent: Boolean? = null) {
|
||||||
if (i < volumes.size) {
|
if (i < volumes.size) {
|
||||||
if (volumes[i].isHidden) {
|
if (volumes[i].isHidden) {
|
||||||
@ -196,12 +210,12 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
.setTitle(R.string.warning)
|
.setTitle(R.string.warning)
|
||||||
.setView(dialogBinding.root)
|
.setView(dialogBinding.root)
|
||||||
.setPositiveButton(R.string.forget_only) { _, _ ->
|
.setPositiveButton(R.string.forget_only) { _, _ ->
|
||||||
volumeDatabase.removeVolume(volumes[i].name)
|
removeVolume(volumes[i])
|
||||||
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) false else null)
|
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) false else null)
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.delete_volume) { _, _ ->
|
.setNegativeButton(R.string.delete_volume) { _, _ ->
|
||||||
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
||||||
volumeDatabase.removeVolume(volumes[i].name)
|
removeVolume(volumes[i])
|
||||||
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) true else null)
|
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) true else null)
|
||||||
}
|
}
|
||||||
.setOnCancelListener {
|
.setOnCancelListener {
|
||||||
@ -213,11 +227,11 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
if (doDeleteVolumeContent) {
|
if (doDeleteVolumeContent) {
|
||||||
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
||||||
}
|
}
|
||||||
volumeDatabase.removeVolume(volumes[i].name)
|
removeVolume(volumes[i])
|
||||||
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
volumeDatabase.removeVolume(volumes[i].name)
|
removeVolume(volumes[i])
|
||||||
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -241,9 +255,6 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
if (explorerRouter.pickMode || explorerRouter.dropMode) {
|
if (explorerRouter.pickMode || explorerRouter.dropMode) {
|
||||||
if (explorerRouter.pickMode) {
|
|
||||||
shouldCloseVolume = false
|
|
||||||
}
|
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
unselectAll()
|
unselectAll()
|
||||||
@ -255,6 +266,15 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.lock -> {
|
||||||
|
volumeAdapter.selectedItems.forEach {
|
||||||
|
volumeManager.getVolumeId(volumeAdapter.volumes[it])?.let { id ->
|
||||||
|
volumeManager.closeVolume(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unselectAll()
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.remove -> {
|
R.id.remove -> {
|
||||||
val selectedVolumes = volumeAdapter.selectedItems.map { i -> volumeAdapter.volumes[i] }
|
val selectedVolumes = volumeAdapter.selectedItems.map { i -> volumeAdapter.volumes[i] }
|
||||||
removeVolumes(selectedVolumes)
|
removeVolumes(selectedVolumes)
|
||||||
@ -325,6 +345,9 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
menu.findItem(R.id.settings).isVisible = !explorerRouter.pickMode && !explorerRouter.dropMode
|
menu.findItem(R.id.settings).isVisible = !explorerRouter.pickMode && !explorerRouter.dropMode
|
||||||
val isSelecting = volumeAdapter.selectedItems.isNotEmpty()
|
val isSelecting = volumeAdapter.selectedItems.isNotEmpty()
|
||||||
menu.findItem(R.id.select_all).isVisible = isSelecting
|
menu.findItem(R.id.select_all).isVisible = isSelecting
|
||||||
|
menu.findItem(R.id.lock).isVisible = isSelecting && volumeAdapter.selectedItems.any {
|
||||||
|
i -> volumeManager.isOpen(volumeAdapter.volumes[i])
|
||||||
|
}
|
||||||
menu.findItem(R.id.remove).isVisible = isSelecting
|
menu.findItem(R.id.remove).isVisible = isSelecting
|
||||||
menu.findItem(R.id.delete_password_hash).isVisible =
|
menu.findItem(R.id.delete_password_hash).isVisible =
|
||||||
isSelecting &&
|
isSelecting &&
|
||||||
@ -456,11 +479,8 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
volumeAdapter.refresh()
|
volumeAdapter.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVolumeOpened(encryptedVolume: EncryptedVolume, volumeShortName: String) {
|
override fun onVolumeOpened(id: Int) {
|
||||||
startActivity(explorerRouter.getExplorerIntent(encryptedVolume, volumeShortName))
|
startActivity(explorerRouter.getExplorerIntent(id, volume.shortName))
|
||||||
if (explorerRouter.pickMode) {
|
|
||||||
shouldCloseVolume = false
|
|
||||||
}
|
|
||||||
if (explorerRouter.dropMode || explorerRouter.pickMode) {
|
if (explorerRouter.dropMode || explorerRouter.pickMode) {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@ -468,18 +488,8 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
shouldCloseVolume = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
volumeOpener.wipeSensitive()
|
volumeOpener.wipeSensitive()
|
||||||
if (explorerRouter.pickMode && !usfKeepOpen && shouldCloseVolume) {
|
|
||||||
IntentUtils.getParcelableExtra<EncryptedVolume>(intent, "volume")?.close()
|
|
||||||
RestrictedFileProvider.wipeAll(this)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,17 @@ class VolumeData(val name: String, val isHidden: Boolean = false, val type: Byte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is VolumeData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return other.name == name && other.isHidden == isHidden
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return name.hashCode()+isHidden.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VOLUMES_DIRECTORY = "volumes"
|
const val VOLUMES_DIRECTORY = "volumes"
|
||||||
|
|
||||||
|
42
app/src/main/java/sushi/hardcore/droidfs/VolumeManager.kt
Normal file
42
app/src/main/java/sushi/hardcore/droidfs/VolumeManager.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package sushi.hardcore.droidfs
|
||||||
|
|
||||||
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
|
|
||||||
|
class VolumeManager {
|
||||||
|
private var id = 0
|
||||||
|
private val volumes = HashMap<Int, EncryptedVolume>()
|
||||||
|
private val volumesData = HashMap<VolumeData, Int>()
|
||||||
|
|
||||||
|
fun insert(volume: EncryptedVolume, data: VolumeData): Int {
|
||||||
|
volumes[id] = volume
|
||||||
|
volumesData[data] = id
|
||||||
|
return id++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isOpen(volume: VolumeData): Boolean {
|
||||||
|
return volumesData.containsKey(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVolumeId(volume: VolumeData): Int? {
|
||||||
|
return volumesData[volume]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVolume(id: Int): EncryptedVolume? {
|
||||||
|
return volumes[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeVolume(id: Int) {
|
||||||
|
volumes.remove(id)?.let { volume ->
|
||||||
|
volume.close()
|
||||||
|
volumesData.filter { it.value == id }.forEach {
|
||||||
|
volumesData.remove(it.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeAll() {
|
||||||
|
volumes.forEach { it.value.close() }
|
||||||
|
volumes.clear()
|
||||||
|
volumesData.clear()
|
||||||
|
}
|
||||||
|
}
|
49
app/src/main/java/sushi/hardcore/droidfs/VolumeManagerApp.kt
Normal file
49
app/src/main/java/sushi/hardcore/droidfs/VolumeManagerApp.kt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package sushi.hardcore.droidfs
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
||||||
|
|
||||||
|
class VolumeManagerApp : Application(), DefaultLifecycleObserver {
|
||||||
|
companion object {
|
||||||
|
private const val USF_KEEP_OPEN_KEY = "usf_keep_open"
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var sharedPreferences: SharedPreferences
|
||||||
|
private val sharedPreferencesListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||||
|
if (key == USF_KEEP_OPEN_KEY) {
|
||||||
|
reloadUsfKeepOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var usfKeepOpen = false
|
||||||
|
var isStartingExternalApp = false
|
||||||
|
val volumeManager = VolumeManager()
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super<Application>.onCreate()
|
||||||
|
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||||
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this).apply {
|
||||||
|
registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
||||||
|
}
|
||||||
|
reloadUsfKeepOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadUsfKeepOpen() {
|
||||||
|
usfKeepOpen = sharedPreferences.getBoolean(USF_KEEP_OPEN_KEY, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume(owner: LifecycleOwner) {
|
||||||
|
isStartingExternalApp = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop(owner: LifecycleOwner) {
|
||||||
|
if (!isStartingExternalApp && !usfKeepOpen) {
|
||||||
|
volumeManager.closeAll()
|
||||||
|
RestrictedFileProvider.wipeAll(applicationContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ class VolumeOpener(
|
|||||||
) {
|
) {
|
||||||
interface VolumeOpenerCallbacks {
|
interface VolumeOpenerCallbacks {
|
||||||
fun onHashStorageReset() {}
|
fun onHashStorageReset() {}
|
||||||
fun onVolumeOpened(encryptedVolume: EncryptedVolume, volumeShortName: String)
|
fun onVolumeOpened(id: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val volumeDatabase = VolumeDatabase(activity)
|
private val volumeDatabase = VolumeDatabase(activity)
|
||||||
@ -31,6 +31,7 @@ class VolumeOpener(
|
|||||||
var themeValue = sharedPrefs.getString(Constants.THEME_VALUE_KEY, Constants.DEFAULT_THEME_VALUE)!!
|
var themeValue = sharedPrefs.getString(Constants.THEME_VALUE_KEY, Constants.DEFAULT_THEME_VALUE)!!
|
||||||
var defaultVolumeName: String? = sharedPrefs.getString(DEFAULT_VOLUME_KEY, null)
|
var defaultVolumeName: String? = sharedPrefs.getString(DEFAULT_VOLUME_KEY, null)
|
||||||
private var dialogBinding: DialogOpenVolumeBinding? = null
|
private var dialogBinding: DialogOpenVolumeBinding? = null
|
||||||
|
private val volumeManager = (activity.application as VolumeManagerApp).volumeManager
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@ -40,54 +41,59 @@ class VolumeOpener(
|
|||||||
|
|
||||||
@SuppressLint("NewApi") // fingerprintProtector is non-null only when SDK_INT >= 23
|
@SuppressLint("NewApi") // fingerprintProtector is non-null only when SDK_INT >= 23
|
||||||
fun openVolume(volume: VolumeData, isVolumeSaved: Boolean, callbacks: VolumeOpenerCallbacks) {
|
fun openVolume(volume: VolumeData, isVolumeSaved: Boolean, callbacks: VolumeOpenerCallbacks) {
|
||||||
if (volume.type == EncryptedVolume.GOCRYPTFS_VOLUME_TYPE && BuildConfig.GOCRYPTFS_DISABLED) {
|
val volumeId = volumeManager.getVolumeId(volume)
|
||||||
Toast.makeText(activity, R.string.gocryptfs_disabled, Toast.LENGTH_SHORT).show()
|
if (volumeId == null) {
|
||||||
return
|
if (volume.type == EncryptedVolume.GOCRYPTFS_VOLUME_TYPE && BuildConfig.GOCRYPTFS_DISABLED) {
|
||||||
} else if (volume.type == EncryptedVolume.CRYFS_VOLUME_TYPE && BuildConfig.CRYFS_DISABLED) {
|
Toast.makeText(activity, R.string.gocryptfs_disabled, Toast.LENGTH_SHORT).show()
|
||||||
Toast.makeText(activity, R.string.cryfs_disabled, Toast.LENGTH_SHORT).show()
|
return
|
||||||
return
|
} else if (volume.type == EncryptedVolume.CRYFS_VOLUME_TYPE && BuildConfig.CRYFS_DISABLED) {
|
||||||
}
|
Toast.makeText(activity, R.string.cryfs_disabled, Toast.LENGTH_SHORT).show()
|
||||||
var askForPassword = true
|
return
|
||||||
fingerprintProtector?.let { fingerprintProtector ->
|
}
|
||||||
volume.encryptedHash?.let { encryptedHash ->
|
var askForPassword = true
|
||||||
volume.iv?.let { iv ->
|
fingerprintProtector?.let { fingerprintProtector ->
|
||||||
askForPassword = false
|
volume.encryptedHash?.let { encryptedHash ->
|
||||||
fingerprintProtector.listener = object : FingerprintProtector.Listener {
|
volume.iv?.let { iv ->
|
||||||
override fun onHashStorageReset() {
|
askForPassword = false
|
||||||
callbacks.onHashStorageReset()
|
fingerprintProtector.listener = object : FingerprintProtector.Listener {
|
||||||
}
|
override fun onHashStorageReset() {
|
||||||
override fun onPasswordHashDecrypted(hash: ByteArray) {
|
callbacks.onHashStorageReset()
|
||||||
object : LoadingTask<EncryptedVolume?>(activity, themeValue, R.string.loading_msg_open) {
|
}
|
||||||
override suspend fun doTask(): EncryptedVolume? {
|
override fun onPasswordHashDecrypted(hash: ByteArray) {
|
||||||
val encryptedVolume = EncryptedVolume.init(volume, activity.filesDir.path, null, hash, null)
|
object : LoadingTask<EncryptedVolume?>(activity, themeValue, R.string.loading_msg_open) {
|
||||||
Arrays.fill(hash, 0)
|
override suspend fun doTask(): EncryptedVolume? {
|
||||||
return encryptedVolume
|
val encryptedVolume = EncryptedVolume.init(volume, activity.filesDir.path, null, hash, null)
|
||||||
|
Arrays.fill(hash, 0)
|
||||||
|
return encryptedVolume
|
||||||
|
}
|
||||||
|
}.startTask(activity.lifecycleScope) { encryptedVolume ->
|
||||||
|
if (encryptedVolume == null) {
|
||||||
|
CustomAlertDialogBuilder(activity, themeValue)
|
||||||
|
.setTitle(R.string.open_volume_failed)
|
||||||
|
.setMessage(R.string.open_failed_hash_msg)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
callbacks.onVolumeOpened(volumeManager.insert(encryptedVolume, volume))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.startTask(activity.lifecycleScope) { encryptedVolume ->
|
}
|
||||||
if (encryptedVolume == null) {
|
override fun onPasswordHashSaved() {}
|
||||||
CustomAlertDialogBuilder(activity, themeValue)
|
override fun onFailed(pending: Boolean) {
|
||||||
.setTitle(R.string.open_volume_failed)
|
if (!pending) {
|
||||||
.setMessage(R.string.open_failed_hash_msg)
|
askForPassword(volume, isVolumeSaved, callbacks)
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show()
|
|
||||||
} else {
|
|
||||||
callbacks.onVolumeOpened(encryptedVolume, volume.shortName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onPasswordHashSaved() {}
|
fingerprintProtector.loadPasswordHash(volume.shortName, encryptedHash, iv)
|
||||||
override fun onFailed(pending: Boolean) {
|
|
||||||
if (!pending) {
|
|
||||||
askForPassword(volume, isVolumeSaved, callbacks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fingerprintProtector.loadPasswordHash(volume.shortName, encryptedHash, iv)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (askForPassword) {
|
||||||
if (askForPassword) {
|
askForPassword(volume, isVolumeSaved, callbacks)
|
||||||
askForPassword(volume, isVolumeSaved, callbacks)
|
}
|
||||||
|
} else {
|
||||||
|
callbacks.onVolumeOpened(volumeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +194,7 @@ class VolumeOpener(
|
|||||||
override fun onPasswordHashDecrypted(hash: ByteArray) {}
|
override fun onPasswordHashDecrypted(hash: ByteArray) {}
|
||||||
override fun onPasswordHashSaved() {
|
override fun onPasswordHashSaved() {
|
||||||
Arrays.fill(returnedHash.value!!, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
callbacks.onVolumeOpened(encryptedVolume, volume.shortName)
|
callbacks.onVolumeOpened(volumeManager.insert(encryptedVolume, volume))
|
||||||
}
|
}
|
||||||
private var isClosed = false
|
private var isClosed = false
|
||||||
override fun onFailed(pending: Boolean) {
|
override fun onFailed(pending: Boolean) {
|
||||||
@ -201,7 +207,7 @@ class VolumeOpener(
|
|||||||
}
|
}
|
||||||
fingerprintProtector.savePasswordHash(volume, returnedHash.value!!)
|
fingerprintProtector.savePasswordHash(volume, returnedHash.value!!)
|
||||||
} else {
|
} else {
|
||||||
callbacks.onVolumeOpened(encryptedVolume, volume.shortName)
|
callbacks.onVolumeOpened(volumeManager.insert(encryptedVolume, volume))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,18 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.VolumeData
|
import sushi.hardcore.droidfs.VolumeData
|
||||||
import sushi.hardcore.droidfs.VolumeDatabase
|
import sushi.hardcore.droidfs.VolumeDatabase
|
||||||
|
import sushi.hardcore.droidfs.VolumeManager
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
|
|
||||||
class VolumeAdapter(
|
class VolumeAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val volumeDatabase: VolumeDatabase,
|
private val volumeDatabase: VolumeDatabase,
|
||||||
|
private val volumeManager: VolumeManager,
|
||||||
private val allowSelection: Boolean,
|
private val allowSelection: Boolean,
|
||||||
private val showReadOnly: Boolean,
|
private val showReadOnly: Boolean,
|
||||||
private val listener: Listener,
|
private val listener: Listener,
|
||||||
@ -80,16 +83,12 @@ class VolumeAdapter(
|
|||||||
fun bind(position: Int) {
|
fun bind(position: Int) {
|
||||||
val volume = volumes[position]
|
val volume = volumes[position]
|
||||||
itemView.findViewById<TextView>(R.id.text_volume_name).text = volume.shortName
|
itemView.findViewById<TextView>(R.id.text_volume_name).text = volume.shortName
|
||||||
itemView.findViewById<ImageView>(R.id.image_icon).setImageResource(R.drawable.icon_volume)
|
|
||||||
itemView.findViewById<TextView>(R.id.text_path).text = if (volume.isHidden)
|
itemView.findViewById<TextView>(R.id.text_path).text = if (volume.isHidden)
|
||||||
context.getString(R.string.hidden_volume)
|
context.getString(R.string.hidden_volume)
|
||||||
else
|
else
|
||||||
volume.name
|
volume.name
|
||||||
itemView.findViewById<ImageView>(R.id.icon_fingerprint).visibility = if (volume.encryptedHash == null) {
|
itemView.findViewById<ImageView>(R.id.icon_unlocked).isVisible = volumeManager.isOpen(volume)
|
||||||
View.GONE
|
itemView.findViewById<ImageView>(R.id.icon_fingerprint).isVisible = volume.encryptedHash != null
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
itemView.findViewById<TextView>(R.id.text_info).text = context.getString(
|
itemView.findViewById<TextView>(R.id.text_info).text = context.getString(
|
||||||
if (volume.canWrite(context.filesDir.path)) {
|
if (volume.canWrite(context.filesDir.path)) {
|
||||||
R.string.volume_type
|
R.string.volume_type
|
||||||
|
@ -4,11 +4,8 @@ import android.os.Bundle
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
import sushi.hardcore.droidfs.*
|
import sushi.hardcore.droidfs.*
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
||||||
import sushi.hardcore.droidfs.databinding.ActivityAddVolumeBinding
|
import sushi.hardcore.droidfs.databinding.ActivityAddVolumeBinding
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerRouter
|
import sushi.hardcore.droidfs.explorers.ExplorerRouter
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
|
||||||
|
|
||||||
class AddVolumeActivity: BaseActivity() {
|
class AddVolumeActivity: BaseActivity() {
|
||||||
|
|
||||||
@ -19,15 +16,12 @@ class AddVolumeActivity: BaseActivity() {
|
|||||||
private lateinit var binding: ActivityAddVolumeBinding
|
private lateinit var binding: ActivityAddVolumeBinding
|
||||||
private lateinit var explorerRouter: ExplorerRouter
|
private lateinit var explorerRouter: ExplorerRouter
|
||||||
private lateinit var volumeOpener: VolumeOpener
|
private lateinit var volumeOpener: VolumeOpener
|
||||||
private var usfKeepOpen = false
|
|
||||||
var shouldCloseVolume = true // used when launched to pick file from another volume
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityAddVolumeBinding.inflate(layoutInflater)
|
binding = ActivityAddVolumeBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
usfKeepOpen = sharedPrefs.getBoolean("usf_keep_open", false)
|
|
||||||
explorerRouter = ExplorerRouter(this, intent)
|
explorerRouter = ExplorerRouter(this, intent)
|
||||||
volumeOpener = VolumeOpener(this)
|
volumeOpener = VolumeOpener(this)
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@ -41,7 +35,6 @@ class AddVolumeActivity: BaseActivity() {
|
|||||||
}
|
}
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
setResult(RESULT_USER_BACK)
|
setResult(RESULT_USER_BACK)
|
||||||
shouldCloseVolume = false
|
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
@ -53,7 +46,6 @@ class AddVolumeActivity: BaseActivity() {
|
|||||||
supportFragmentManager.popBackStack()
|
supportFragmentManager.popBackStack()
|
||||||
else {
|
else {
|
||||||
setResult(RESULT_USER_BACK)
|
setResult(RESULT_USER_BACK)
|
||||||
shouldCloseVolume = false
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,21 +62,19 @@ class AddVolumeActivity: BaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startExplorer(encryptedVolume: EncryptedVolume, volumeShortName: String) {
|
fun startExplorer(volumeId: Int, volumeShortName: String) {
|
||||||
startActivity(explorerRouter.getExplorerIntent(encryptedVolume, volumeShortName))
|
startActivity(explorerRouter.getExplorerIntent(volumeId, volumeShortName))
|
||||||
shouldCloseVolume = false
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onVolumeSelected(volume: VolumeData, rememberVolume: Boolean) {
|
fun onVolumeSelected(volume: VolumeData, rememberVolume: Boolean) {
|
||||||
if (rememberVolume) {
|
if (rememberVolume) {
|
||||||
setResult(RESULT_USER_BACK)
|
setResult(RESULT_USER_BACK)
|
||||||
shouldCloseVolume = false
|
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
volumeOpener.openVolume(volume, false, object : VolumeOpener.VolumeOpenerCallbacks {
|
volumeOpener.openVolume(volume, false, object : VolumeOpener.VolumeOpenerCallbacks {
|
||||||
override fun onVolumeOpened(encryptedVolume: EncryptedVolume, volumeShortName: String) {
|
override fun onVolumeOpened(id: Int) {
|
||||||
startExplorer(encryptedVolume, volumeShortName)
|
startExplorer(id, volume.shortName)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -106,18 +96,4 @@ class AddVolumeActivity: BaseActivity() {
|
|||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
shouldCloseVolume = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
super.onStop()
|
|
||||||
if (explorerRouter.pickMode && !usfKeepOpen && shouldCloseVolume) {
|
|
||||||
IntentUtils.getParcelableExtra<EncryptedVolume>(intent, "volume")?.close()
|
|
||||||
RestrictedFileProvider.wipeAll(this)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -158,11 +158,7 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val encryptedVolume = if (rememberVolume) {
|
val encryptedVolume = ObjRef<EncryptedVolume?>(null)
|
||||||
null
|
|
||||||
} else {
|
|
||||||
ObjRef<EncryptedVolume?>(null)
|
|
||||||
}
|
|
||||||
object: LoadingTask<Byte>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
|
object: LoadingTask<Byte>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
|
||||||
private fun generateResult(success: Boolean, volumeType: Byte): Byte {
|
private fun generateResult(success: Boolean, volumeType: Byte): Byte {
|
||||||
return if (success) {
|
return if (success) {
|
||||||
@ -223,6 +219,9 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
isVolumeSaved = saveVolume(volume)
|
isVolumeSaved = saveVolume(volume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val volumeId = encryptedVolume.value?.let {
|
||||||
|
(activity?.application as VolumeManagerApp).volumeManager.insert(it, volume)
|
||||||
|
}
|
||||||
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
|
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
|
||||||
if (isVolumeSaved && binding.checkboxSavePassword.isChecked && returnedHash != null) {
|
if (isVolumeSaved && binding.checkboxSavePassword.isChecked && returnedHash != null) {
|
||||||
fingerprintProtector!!.let {
|
fingerprintProtector!!.let {
|
||||||
@ -235,31 +234,31 @@ class CreateVolumeFragment: Fragment() {
|
|||||||
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
||||||
override fun onPasswordHashSaved() {
|
override fun onPasswordHashSaved() {
|
||||||
Arrays.fill(returnedHash.value!!, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
onVolumeCreated(encryptedVolume?.value, volume.shortName)
|
onVolumeCreated(volumeId, volume.shortName)
|
||||||
}
|
}
|
||||||
override fun onFailed(pending: Boolean) {
|
override fun onFailed(pending: Boolean) {
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
Arrays.fill(returnedHash.value!!, 0)
|
Arrays.fill(returnedHash.value!!, 0)
|
||||||
onVolumeCreated(encryptedVolume?.value, volume.shortName)
|
onVolumeCreated(volumeId, volume.shortName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.savePasswordHash(volume, returnedHash.value!!)
|
it.savePasswordHash(volume, returnedHash.value!!)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onVolumeCreated(encryptedVolume?.value, volume.shortName)
|
onVolumeCreated(volumeId, volume.shortName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onVolumeCreated(encryptedVolume: EncryptedVolume?, volumeShortName: String) {
|
private fun onVolumeCreated(id: Int?, volumeShortName: String) {
|
||||||
(activity as AddVolumeActivity).apply {
|
(activity as AddVolumeActivity).apply {
|
||||||
if (rememberVolume || encryptedVolume == null) {
|
if (rememberVolume || id == null) {
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
startExplorer(encryptedVolume, volumeShortName)
|
startExplorer(id, volumeShortName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import sushi.hardcore.droidfs.Constants
|
|||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.VolumeData
|
import sushi.hardcore.droidfs.VolumeData
|
||||||
import sushi.hardcore.droidfs.VolumeDatabase
|
import sushi.hardcore.droidfs.VolumeDatabase
|
||||||
|
import sushi.hardcore.droidfs.VolumeManagerApp
|
||||||
import sushi.hardcore.droidfs.databinding.DialogSdcardErrorBinding
|
import sushi.hardcore.droidfs.databinding.DialogSdcardErrorBinding
|
||||||
import sushi.hardcore.droidfs.databinding.FragmentSelectPathBinding
|
import sushi.hardcore.droidfs.databinding.FragmentSelectPathBinding
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
@ -62,6 +63,7 @@ class SelectPathFragment: Fragment() {
|
|||||||
if (uri != null)
|
if (uri != null)
|
||||||
onDirectoryPicked(uri)
|
onDirectoryPicked(uri)
|
||||||
}
|
}
|
||||||
|
private lateinit var app: VolumeManagerApp
|
||||||
private var themeValue = Constants.DEFAULT_THEME_VALUE
|
private var themeValue = Constants.DEFAULT_THEME_VALUE
|
||||||
private lateinit var volumeDatabase: VolumeDatabase
|
private lateinit var volumeDatabase: VolumeDatabase
|
||||||
private lateinit var filesDir: String
|
private lateinit var filesDir: String
|
||||||
@ -81,6 +83,7 @@ class SelectPathFragment: Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
app = requireActivity().application as VolumeManagerApp
|
||||||
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
originalRememberVolume = sharedPrefs.getBoolean(Constants.REMEMBER_VOLUME_KEY, true)
|
originalRememberVolume = sharedPrefs.getBoolean(Constants.REMEMBER_VOLUME_KEY, true)
|
||||||
binding.switchRemember.isChecked = originalRememberVolume
|
binding.switchRemember.isChecked = originalRememberVolume
|
||||||
@ -105,6 +108,7 @@ class SelectPathFragment: Fragment() {
|
|||||||
if (Environment.isExternalStorageManager()) {
|
if (Environment.isExternalStorageManager()) {
|
||||||
launchPickDirectory()
|
launchPickDirectory()
|
||||||
} else {
|
} else {
|
||||||
|
app.isStartingExternalApp = true
|
||||||
startActivity(Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:"+requireContext().packageName)))
|
startActivity(Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:"+requireContext().packageName)))
|
||||||
}
|
}
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@ -119,6 +123,7 @@ class SelectPathFragment: Fragment() {
|
|||||||
) {
|
) {
|
||||||
launchPickDirectory()
|
launchPickDirectory()
|
||||||
} else {
|
} else {
|
||||||
|
app.isStartingExternalApp = true
|
||||||
askStoragePermissions.launch(
|
askStoragePermissions.launch(
|
||||||
arrayOf(
|
arrayOf(
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
@ -149,7 +154,7 @@ class SelectPathFragment: Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun launchPickDirectory() {
|
private fun launchPickDirectory() {
|
||||||
(activity as AddVolumeActivity).shouldCloseVolume = false
|
app.isStartingExternalApp = true
|
||||||
PathUtils.safePickDirectory(pickDirectory, requireContext(), themeValue)
|
PathUtils.safePickDirectory(pickDirectory, requireContext(), themeValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import sushi.hardcore.droidfs.util.SQLUtil.appendSelectionArgs
|
|||||||
import sushi.hardcore.droidfs.util.SQLUtil.concatenateWhere
|
import sushi.hardcore.droidfs.util.SQLUtil.concatenateWhere
|
||||||
import sushi.hardcore.droidfs.util.Wiper
|
import sushi.hardcore.droidfs.util.Wiper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class RestrictedFileProvider: ContentProvider() {
|
class RestrictedFileProvider: ContentProvider() {
|
||||||
|
@ -13,6 +13,7 @@ import android.view.View
|
|||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@ -22,20 +23,15 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import sushi.hardcore.droidfs.BaseActivity
|
import sushi.hardcore.droidfs.*
|
||||||
import sushi.hardcore.droidfs.Constants
|
|
||||||
import sushi.hardcore.droidfs.FileTypes
|
|
||||||
import sushi.hardcore.droidfs.R
|
|
||||||
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
|
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
|
||||||
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
|
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
|
||||||
import sushi.hardcore.droidfs.content_providers.ExternalProvider
|
import sushi.hardcore.droidfs.content_providers.ExternalProvider
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
||||||
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
||||||
import sushi.hardcore.droidfs.file_operations.OperationFile
|
import sushi.hardcore.droidfs.file_operations.OperationFile
|
||||||
import sushi.hardcore.droidfs.file_viewers.*
|
import sushi.hardcore.droidfs.file_viewers.*
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
import sushi.hardcore.droidfs.filesystems.Stat
|
import sushi.hardcore.droidfs.filesystems.Stat
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
import sushi.hardcore.droidfs.widgets.EditTextDialog
|
import sushi.hardcore.droidfs.widgets.EditTextDialog
|
||||||
@ -46,6 +42,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
private var foldersFirst = true
|
private var foldersFirst = true
|
||||||
private var mapFolders = true
|
private var mapFolders = true
|
||||||
private var currentSortOrderIndex = 0
|
private var currentSortOrderIndex = 0
|
||||||
|
private var volumeId = -1
|
||||||
protected lateinit var encryptedVolume: EncryptedVolume
|
protected lateinit var encryptedVolume: EncryptedVolume
|
||||||
private lateinit var volumeName: String
|
private lateinit var volumeName: String
|
||||||
private lateinit var explorerViewModel: ExplorerViewModel
|
private lateinit var explorerViewModel: ExplorerViewModel
|
||||||
@ -58,10 +55,8 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
protected val taskScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
protected val taskScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||||
protected lateinit var explorerElements: MutableList<ExplorerElement>
|
protected lateinit var explorerElements: MutableList<ExplorerElement>
|
||||||
protected lateinit var explorerAdapter: ExplorerElementAdapter
|
protected lateinit var explorerAdapter: ExplorerElementAdapter
|
||||||
private var isCreating = true
|
protected lateinit var app: VolumeManagerApp
|
||||||
protected var isStartingActivity = false
|
|
||||||
private var usf_open = false
|
private var usf_open = false
|
||||||
protected var usf_keep_open = false
|
|
||||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||||
private var isUsingListLayout = true
|
private var isUsingListLayout = true
|
||||||
private lateinit var layoutIcon: ImageButton
|
private lateinit var layoutIcon: ImageButton
|
||||||
@ -76,10 +71,11 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
app = application as VolumeManagerApp
|
||||||
usf_open = sharedPrefs.getBoolean("usf_open", false)
|
usf_open = sharedPrefs.getBoolean("usf_open", false)
|
||||||
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
|
volumeName = intent.getStringExtra("volumeName") ?: ""
|
||||||
volumeName = intent.getStringExtra("volume_name") ?: ""
|
volumeId = intent.getIntExtra("volumeId", -1)
|
||||||
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
|
encryptedVolume = app.volumeManager.getVolume(volumeId)!!
|
||||||
sortOrderEntries = resources.getStringArray(R.array.sort_orders_entries)
|
sortOrderEntries = resources.getStringArray(R.array.sort_orders_entries)
|
||||||
sortOrderValues = resources.getStringArray(R.array.sort_orders_values)
|
sortOrderValues = resources.getStringArray(R.array.sort_orders_values)
|
||||||
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
||||||
@ -118,6 +114,19 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
isUsingListLayout = sharedPrefs.getBoolean("useListLayout", true)
|
isUsingListLayout = sharedPrefs.getBoolean("useListLayout", true)
|
||||||
layoutIcon = findViewById(R.id.layout_icon)
|
layoutIcon = findViewById(R.id.layout_icon)
|
||||||
setRecyclerViewLayout()
|
setRecyclerViewLayout()
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
||||||
|
val parentPath = PathUtils.getParentPath(currentDirectoryPath)
|
||||||
|
if (parentPath == currentDirectoryPath) {
|
||||||
|
isEnabled = false
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
} else {
|
||||||
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unselectAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
layoutIcon.setOnClickListener {
|
layoutIcon.setOnClickListener {
|
||||||
isUsingListLayout = !isUsingListLayout
|
isUsingListLayout = !isUsingListLayout
|
||||||
setRecyclerViewLayout()
|
setRecyclerViewLayout()
|
||||||
@ -171,18 +180,17 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startFileViewer(cls: Class<*>, filePath: String){
|
private fun startFileViewer(cls: Class<*>, filePath: String) {
|
||||||
val intent = Intent(this, cls).apply {
|
val intent = Intent(this, cls).apply {
|
||||||
putExtra("path", filePath)
|
putExtra("path", filePath)
|
||||||
putExtra("volume", encryptedVolume)
|
putExtra("volume", encryptedVolume)
|
||||||
putExtra("sortOrder", sortOrderValues[currentSortOrderIndex])
|
putExtra("sortOrder", sortOrderValues[currentSortOrderIndex])
|
||||||
}
|
}
|
||||||
isStartingActivity = true
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openWithExternalApp(fullPath: String){
|
private fun openWithExternalApp(fullPath: String) {
|
||||||
isStartingActivity = true
|
app.isStartingExternalApp = true
|
||||||
ExternalProvider.open(this, themeValue, encryptedVolume, fullPath)
|
ExternalProvider.open(this, themeValue, encryptedVolume, fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,28 +340,18 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun askCloseVolume() {
|
private fun askLockVolume() {
|
||||||
CustomAlertDialogBuilder(this, themeValue)
|
CustomAlertDialogBuilder(this, themeValue)
|
||||||
.setTitle(R.string.warning)
|
.setTitle(R.string.warning)
|
||||||
.setMessage(R.string.ask_close_volume)
|
.setMessage(R.string.ask_lock_volume)
|
||||||
.setPositiveButton(R.string.ok) { _, _ -> closeVolumeOnUserExit() }
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
app.volumeManager.closeVolume(volumeId)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if (explorerAdapter.selectedItems.isEmpty()) {
|
|
||||||
val parentPath = PathUtils.getParentPath(currentDirectoryPath)
|
|
||||||
if (parentPath == currentDirectoryPath) {
|
|
||||||
askCloseVolume()
|
|
||||||
} else {
|
|
||||||
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unselectAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createFolder(folderName: String){
|
private fun createFolder(folderName: String){
|
||||||
if (folderName.isEmpty()) {
|
if (folderName.isEmpty()) {
|
||||||
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
|
||||||
@ -508,9 +506,9 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
val noItemSelected = explorerAdapter.selectedItems.isEmpty()
|
val noItemSelected = explorerAdapter.selectedItems.isEmpty()
|
||||||
val iconColor = ContextCompat.getColor(this, R.color.neutralIconTint)
|
val iconColor = ContextCompat.getColor(this, R.color.neutralIconTint)
|
||||||
setMenuIconTint(menu, iconColor, R.id.sort, R.drawable.icon_sort)
|
setMenuIconTint(menu, iconColor, R.id.sort, R.drawable.icon_sort)
|
||||||
setMenuIconTint(menu, iconColor, R.id.decrypt, R.drawable.icon_decrypt)
|
|
||||||
setMenuIconTint(menu, iconColor, R.id.share, R.drawable.icon_share)
|
setMenuIconTint(menu, iconColor, R.id.share, R.drawable.icon_share)
|
||||||
menu.findItem(R.id.sort).isVisible = noItemSelected
|
menu.findItem(R.id.sort).isVisible = noItemSelected
|
||||||
|
menu.findItem(R.id.lock).isVisible = noItemSelected
|
||||||
menu.findItem(R.id.close).isVisible = noItemSelected
|
menu.findItem(R.id.close).isVisible = noItemSelected
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(!noItemSelected)
|
supportActionBar?.setDisplayHomeAsUpEnabled(!noItemSelected)
|
||||||
if (!noItemSelected) {
|
if (!noItemSelected) {
|
||||||
@ -577,55 +575,33 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.close -> {
|
R.id.close -> {
|
||||||
askCloseVolume()
|
finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.lock -> {
|
||||||
|
askLockVolume()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun closeVolumeOnUserExit() {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun closeVolumeOnDestroy() {
|
|
||||||
taskScope.cancel()
|
|
||||||
if (!encryptedVolume.isClosed()) {
|
|
||||||
encryptedVolume.close()
|
|
||||||
}
|
|
||||||
RestrictedFileProvider.wipeAll(this) //additional security
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
if (!isChangingConfigurations) { //activity won't be recreated
|
if (!isChangingConfigurations) { //activity won't be recreated
|
||||||
closeVolumeOnDestroy()
|
taskScope.cancel()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
if (!isChangingConfigurations){
|
|
||||||
if (isStartingActivity){
|
|
||||||
isStartingActivity = false
|
|
||||||
} else if (!usf_keep_open){
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (isCreating){
|
if (app.isStartingExternalApp) {
|
||||||
isCreating = false
|
ExternalProvider.removeFilesAsync(this)
|
||||||
|
}
|
||||||
|
if (encryptedVolume.isClosed()) {
|
||||||
|
finish()
|
||||||
} else {
|
} else {
|
||||||
if (encryptedVolume.isClosed()) {
|
setCurrentPath(currentDirectoryPath)
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
isStartingActivity = false
|
|
||||||
ExternalProvider.removeFilesAsync(this)
|
|
||||||
setCurrentPath(currentDirectoryPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.net.Uri
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
@ -60,9 +61,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
}
|
}
|
||||||
if (operationFiles.size > 0){
|
if (operationFiles.size > 0){
|
||||||
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
|
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
|
||||||
if (items == null) {
|
if (items != null) {
|
||||||
remoteEncryptedVolume.close()
|
|
||||||
} else {
|
|
||||||
// stop loading thumbnails while writing files
|
// stop loading thumbnails while writing files
|
||||||
explorerAdapter.loadThumbnails = false
|
explorerAdapter.loadThumbnails = false
|
||||||
taskScope.launch {
|
taskScope.launch {
|
||||||
@ -78,12 +77,9 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
}
|
}
|
||||||
explorerAdapter.loadThumbnails = true
|
explorerAdapter.loadThumbnails = true
|
||||||
setCurrentPath(currentDirectoryPath)
|
setCurrentPath(currentDirectoryPath)
|
||||||
remoteEncryptedVolume.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
remoteEncryptedVolume.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,6 +165,16 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
if (currentItemAction != ItemsActions.NONE) {
|
||||||
|
cancelItemAction()
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
} else {
|
||||||
|
isEnabled = false
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener {
|
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener {
|
||||||
if (currentItemAction != ItemsActions.NONE){
|
if (currentItemAction != ItemsActions.NONE){
|
||||||
openDialogCreateFolder()
|
openDialogCreateFolder()
|
||||||
@ -189,15 +195,14 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
val intent = Intent(this, MainActivity::class.java)
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
intent.action = "pick"
|
intent.action = "pick"
|
||||||
intent.putExtra("volume", encryptedVolume)
|
intent.putExtra("volume", encryptedVolume)
|
||||||
isStartingActivity = true
|
|
||||||
pickFromOtherVolumes.launch(intent)
|
pickFromOtherVolumes.launch(intent)
|
||||||
}
|
}
|
||||||
"importFiles" -> {
|
"importFiles" -> {
|
||||||
isStartingActivity = true
|
app.isStartingExternalApp = true
|
||||||
pickFiles.launch(arrayOf("*/*"))
|
pickFiles.launch(arrayOf("*/*"))
|
||||||
}
|
}
|
||||||
"importFolder" -> {
|
"importFolder" -> {
|
||||||
isStartingActivity = true
|
app.isStartingExternalApp = true
|
||||||
pickImportDirectory.launch(null)
|
pickImportDirectory.launch(null)
|
||||||
}
|
}
|
||||||
"createFile" -> {
|
"createFile" -> {
|
||||||
@ -212,7 +217,6 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
val intent = Intent(this, CameraActivity::class.java)
|
val intent = Intent(this, CameraActivity::class.java)
|
||||||
intent.putExtra("path", currentDirectoryPath)
|
intent.putExtra("path", currentDirectoryPath)
|
||||||
intent.putExtra("volume", encryptedVolume)
|
intent.putExtra("volume", encryptedVolume)
|
||||||
isStartingActivity = true
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,6 +261,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
val result = super.onCreateOptionsMenu(menu)
|
val result = super.onCreateOptionsMenu(menu)
|
||||||
if (currentItemAction != ItemsActions.NONE) {
|
if (currentItemAction != ItemsActions.NONE) {
|
||||||
menu.findItem(R.id.validate).isVisible = true
|
menu.findItem(R.id.validate).isVisible = true
|
||||||
|
menu.findItem(R.id.lock).isVisible = false
|
||||||
menu.findItem(R.id.close).isVisible = false
|
menu.findItem(R.id.close).isVisible = false
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
} else {
|
} else {
|
||||||
@ -405,13 +410,13 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
for (i in explorerAdapter.selectedItems) {
|
for (i in explorerAdapter.selectedItems) {
|
||||||
paths.add(explorerElements[i].fullPath)
|
paths.add(explorerElements[i].fullPath)
|
||||||
}
|
}
|
||||||
isStartingActivity = true
|
app.isStartingExternalApp = true
|
||||||
ExternalProvider.share(this, themeValue, encryptedVolume, paths)
|
ExternalProvider.share(this, themeValue, encryptedVolume, paths)
|
||||||
unselectAll()
|
unselectAll()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.decrypt -> {
|
R.id.decrypt -> {
|
||||||
isStartingActivity = true
|
app.isStartingExternalApp = true
|
||||||
pickExportDirectory.launch(null)
|
pickExportDirectory.launch(null)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -506,13 +511,4 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
itemsToProcess.clear()
|
itemsToProcess.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if (currentItemAction != ItemsActions.NONE) {
|
|
||||||
cancelItemAction()
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,21 +22,18 @@ class ExplorerActivityPick : BaseExplorerActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onExplorerElementClick(position: Int) {
|
override fun onExplorerElementClick(position: Int) {
|
||||||
val wasSelecting = explorerAdapter.selectedItems.isNotEmpty()
|
|
||||||
if (explorerAdapter.selectedItems.isEmpty()) {
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
||||||
if (!wasSelecting) {
|
val fullPath = PathUtils.pathJoin(currentDirectoryPath, explorerElements[position].name)
|
||||||
val fullPath = PathUtils.pathJoin(currentDirectoryPath, explorerElements[position].name)
|
when {
|
||||||
when {
|
explorerElements[position].isDirectory -> {
|
||||||
explorerElements[position].isDirectory -> {
|
setCurrentPath(fullPath)
|
||||||
setCurrentPath(fullPath)
|
}
|
||||||
}
|
explorerElements[position].isParentFolder -> {
|
||||||
explorerElements[position].isParentFolder -> {
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
}
|
||||||
}
|
else -> {
|
||||||
else -> {
|
resultIntent.putExtra("path", fullPath)
|
||||||
resultIntent.putExtra("path", fullPath)
|
returnActivityResult()
|
||||||
returnActivityResult()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,17 +78,4 @@ class ExplorerActivityPick : BaseExplorerActivity() {
|
|||||||
isFinishingIntentionally = true
|
isFinishingIntentionally = true
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeVolumeOnDestroy() {
|
|
||||||
if (!isFinishingIntentionally && !usf_keep_open){
|
|
||||||
IntentUtils.getParcelableExtra<EncryptedVolume>(intent, "destinationVolume")?.close()
|
|
||||||
super.closeVolumeOnDestroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun closeVolumeOnUserExit() {
|
|
||||||
isFinishingIntentionally = true
|
|
||||||
super.closeVolumeOnUserExit()
|
|
||||||
super.closeVolumeOnDestroy()
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ class ExplorerRouter(private val context: Context, private val intent: Intent) {
|
|||||||
var pickMode = intent.action == "pick"
|
var pickMode = intent.action == "pick"
|
||||||
var dropMode = (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null
|
var dropMode = (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null
|
||||||
|
|
||||||
fun getExplorerIntent(encryptedVolume: EncryptedVolume, volumeShortName: String): Intent {
|
fun getExplorerIntent(volumeId: Int, volumeShortName: String): Intent {
|
||||||
var explorerIntent: Intent? = null
|
var explorerIntent: Intent? = null
|
||||||
if (dropMode) { //import via android share menu
|
if (dropMode) { //import via android share menu
|
||||||
explorerIntent = Intent(context, ExplorerActivityDrop::class.java)
|
explorerIntent = Intent(context, ExplorerActivityDrop::class.java)
|
||||||
@ -22,8 +22,8 @@ class ExplorerRouter(private val context: Context, private val intent: Intent) {
|
|||||||
if (explorerIntent == null) {
|
if (explorerIntent == null) {
|
||||||
explorerIntent = Intent(context, ExplorerActivity::class.java) //default opening
|
explorerIntent = Intent(context, ExplorerActivity::class.java) //default opening
|
||||||
}
|
}
|
||||||
explorerIntent.putExtra("volume", encryptedVolume)
|
explorerIntent.putExtra("volumeId", volumeId)
|
||||||
explorerIntent.putExtra("volume_name", volumeShortName)
|
explorerIntent.putExtra("volumeName", volumeShortName)
|
||||||
return explorerIntent
|
return explorerIntent
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ package sushi.hardcore.droidfs.file_viewers
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.addCallback
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
@ -14,7 +13,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import sushi.hardcore.droidfs.BaseActivity
|
import sushi.hardcore.droidfs.BaseActivity
|
||||||
import sushi.hardcore.droidfs.FileTypes
|
import sushi.hardcore.droidfs.FileTypes
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
import sushi.hardcore.droidfs.util.IntentUtils
|
||||||
@ -27,8 +25,6 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
private lateinit var originalParentPath: String
|
private lateinit var originalParentPath: String
|
||||||
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
||||||
private var windowTypeMask = 0
|
private var windowTypeMask = 0
|
||||||
private var isFinishingIntentionally = false
|
|
||||||
private var usf_keep_open = false
|
|
||||||
private var foldersFirst = true
|
private var foldersFirst = true
|
||||||
private var wasMapped = false
|
private var wasMapped = false
|
||||||
protected val mappedPlaylist = mutableListOf<ExplorerElement>()
|
protected val mappedPlaylist = mutableListOf<ExplorerElement>()
|
||||||
@ -43,17 +39,11 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
filePath = intent.getStringExtra("path")!!
|
filePath = intent.getStringExtra("path")!!
|
||||||
originalParentPath = PathUtils.getParentPath(filePath)
|
originalParentPath = PathUtils.getParentPath(filePath)
|
||||||
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
|
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
|
||||||
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
|
|
||||||
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
||||||
windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
|
windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
|
||||||
windowInsetsController.addOnControllableInsetsChangedListener { _, typeMask ->
|
windowInsetsController.addOnControllableInsetsChangedListener { _, typeMask ->
|
||||||
windowTypeMask = typeMask
|
windowTypeMask = typeMask
|
||||||
}
|
}
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
|
||||||
isFinishingIntentionally = true
|
|
||||||
isEnabled = false
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
}
|
|
||||||
if (fullscreenMode) {
|
if (fullscreenMode) {
|
||||||
fixNavBarColor()
|
fixNavBarColor()
|
||||||
hideSystemUi()
|
hideSystemUi()
|
||||||
@ -156,21 +146,12 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun goBackToExplorer() {
|
protected fun goBackToExplorer() {
|
||||||
isFinishingIntentionally = true
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onResume() {
|
||||||
super.onDestroy()
|
super.onResume()
|
||||||
if (!isFinishingIntentionally) {
|
if (encryptedVolume.isClosed()) {
|
||||||
encryptedVolume.close()
|
|
||||||
RestrictedFileProvider.wipeAll(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
if (!usf_keep_open) {
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.addCallback
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -29,6 +30,9 @@ class TextEditor: FileViewerActivity() {
|
|||||||
loadWholeFile(filePath) {
|
loadWholeFile(filePath) {
|
||||||
try {
|
try {
|
||||||
loadLayout(String(it))
|
loadLayout(String(it))
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
checkSaveAndExit()
|
||||||
|
}
|
||||||
} catch (e: OutOfMemoryError){
|
} catch (e: OutOfMemoryError){
|
||||||
CustomAlertDialogBuilder(this, themeValue)
|
CustomAlertDialogBuilder(this, themeValue)
|
||||||
.setTitle(R.string.error)
|
.setTitle(R.string.error)
|
||||||
@ -124,8 +128,4 @@ class TextEditor: FileViewerActivity() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
checkSaveAndExit()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -67,13 +67,9 @@ class CryfsVolume(private val fusePtr: Long): EncryptedVolume() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef<ByteArray?>?, cipher: String?, volume: ObjRef<EncryptedVolume?>?): Boolean {
|
fun create(baseDir: String, localStateDir: String, password: ByteArray, returnedHash: ObjRef<ByteArray?>?, cipher: String?, volume: ObjRef<EncryptedVolume?>): Boolean {
|
||||||
return init(baseDir, localStateDir, password, null, returnedHash, true, cipher)?.also {
|
return init(baseDir, localStateDir, password, null, returnedHash, true, cipher)?.also {
|
||||||
if (volume == null) {
|
volume.value = it
|
||||||
it.close()
|
|
||||||
} else {
|
|
||||||
volume.value = it
|
|
||||||
}
|
|
||||||
} != null
|
} != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() {
|
|||||||
logN: Int,
|
logN: Int,
|
||||||
creator: String,
|
creator: String,
|
||||||
returnedHash: ByteArray?,
|
returnedHash: ByteArray?,
|
||||||
openAfterCreation: Boolean,
|
|
||||||
): Int
|
): Int
|
||||||
private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int
|
private external fun nativeInit(root_cipher_dir: String, password: ByteArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int
|
||||||
external fun changePassword(
|
external fun changePassword(
|
||||||
@ -53,20 +52,26 @@ class GocryptfsVolume(private val sessionID: Int): EncryptedVolume() {
|
|||||||
plainTextNames: Boolean,
|
plainTextNames: Boolean,
|
||||||
xchacha: Int,
|
xchacha: Int,
|
||||||
returnedHash: ByteArray?,
|
returnedHash: ByteArray?,
|
||||||
volume: ObjRef<EncryptedVolume?>?
|
volume: ObjRef<EncryptedVolume?>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val openAfterCreation = volume != null
|
return when (val result = nativeCreateVolume(
|
||||||
val result = nativeCreateVolume(root_cipher_dir, password, plainTextNames, xchacha, ScryptDefaultLogN, VOLUME_CREATOR, returnedHash, openAfterCreation)
|
root_cipher_dir,
|
||||||
return if (!openAfterCreation) {
|
password,
|
||||||
result == 1
|
plainTextNames,
|
||||||
} else if (result == -1) {
|
xchacha,
|
||||||
Log.e("gocryptfs", "Failed to open volume after creation")
|
ScryptDefaultLogN,
|
||||||
true
|
VOLUME_CREATOR,
|
||||||
} else if (result == -2) {
|
returnedHash,
|
||||||
false
|
)) {
|
||||||
} else {
|
-1 -> {
|
||||||
volume!!.value = GocryptfsVolume(result)
|
Log.e("gocryptfs", "Failed to open volume after creation")
|
||||||
true
|
true
|
||||||
|
}
|
||||||
|
-2 -> false
|
||||||
|
else -> {
|
||||||
|
volume.value = GocryptfsVolume(result)
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include "libgocryptfs.h"
|
#include "libgocryptfs.h"
|
||||||
|
|
||||||
const int KeyLen = 32;
|
const int KeyLen = 32;
|
||||||
@ -15,38 +13,34 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_nativeCre
|
|||||||
jint xchacha,
|
jint xchacha,
|
||||||
jint logN,
|
jint logN,
|
||||||
jstring jcreator,
|
jstring jcreator,
|
||||||
jbyteArray jreturned_hash,
|
jbyteArray jreturned_hash) {
|
||||||
jboolean open_after_creation) {
|
|
||||||
const char* root_cipher_dir = (*env)->GetStringUTFChars(env, jroot_cipher_dir, NULL);
|
const char* root_cipher_dir = (*env)->GetStringUTFChars(env, jroot_cipher_dir, NULL);
|
||||||
const char* creator = (*env)->GetStringUTFChars(env, jcreator, NULL);
|
const char* creator = (*env)->GetStringUTFChars(env, jcreator, NULL);
|
||||||
GoString gofilename = {root_cipher_dir, strlen(root_cipher_dir)}, gocreator = {creator, strlen(creator)};
|
GoString gofilename = {root_cipher_dir, strlen(root_cipher_dir)}, gocreator = {creator, strlen(creator)};
|
||||||
|
|
||||||
const size_t password_len = (*env)->GetArrayLength(env, jpassword);
|
const size_t password_len = (const size_t) (*env)->GetArrayLength(env, jpassword);
|
||||||
jbyte* password = (*env)->GetByteArrayElements(env, jpassword, NULL);
|
jbyte* password = (*env)->GetByteArrayElements(env, jpassword, NULL);
|
||||||
GoSlice go_password = {password, password_len, password_len};
|
GoSlice go_password = {password, password_len, password_len};
|
||||||
|
|
||||||
size_t returned_hash_len;
|
size_t returned_hash_len;
|
||||||
GoSlice go_returned_hash;
|
GoSlice go_returned_hash;
|
||||||
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
||||||
returned_hash_len = (*env)->GetArrayLength(env, jreturned_hash);
|
returned_hash_len = (size_t) (*env)->GetArrayLength(env, jreturned_hash);
|
||||||
go_returned_hash.data = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
go_returned_hash.data = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
||||||
} else if (open_after_creation) {
|
} else {
|
||||||
returned_hash_len = KeyLen;
|
returned_hash_len = KeyLen;
|
||||||
go_returned_hash.data = malloc(KeyLen);
|
go_returned_hash.data = malloc(KeyLen);
|
||||||
} else {
|
|
||||||
returned_hash_len = 0;
|
|
||||||
go_returned_hash.data = NULL;
|
|
||||||
}
|
}
|
||||||
go_returned_hash.len = returned_hash_len;
|
go_returned_hash.len = returned_hash_len;
|
||||||
go_returned_hash.cap = returned_hash_len;
|
go_returned_hash.cap = returned_hash_len;
|
||||||
|
|
||||||
GoUint8 result = gcf_create_volume(gofilename, go_password, plainTextNames, xchacha, logN, gocreator, go_returned_hash);
|
GoUint8 result = gcf_create_volume(gofilename, go_password, plainTextNames, (GoInt8) xchacha, logN, gocreator, go_returned_hash);
|
||||||
|
|
||||||
(*env)->ReleaseByteArrayElements(env, jpassword, password, 0);
|
(*env)->ReleaseByteArrayElements(env, jpassword, password, 0);
|
||||||
(*env)->ReleaseStringUTFChars(env, jcreator, creator);
|
(*env)->ReleaseStringUTFChars(env, jcreator, creator);
|
||||||
|
|
||||||
GoInt sessionID = -2;
|
GoInt sessionID = -2;
|
||||||
if (result && open_after_creation) {
|
if (result) {
|
||||||
GoSlice null_slice = {NULL, 0, 0};
|
GoSlice null_slice = {NULL, 0, 0};
|
||||||
sessionID = gcf_init(gofilename, null_slice, go_returned_hash, null_slice);
|
sessionID = gcf_init(gofilename, null_slice, go_returned_hash, null_slice);
|
||||||
}
|
}
|
||||||
@ -55,14 +49,14 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_nativeCre
|
|||||||
|
|
||||||
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
||||||
(*env)->ReleaseByteArrayElements(env, jreturned_hash, go_returned_hash.data, 0);
|
(*env)->ReleaseByteArrayElements(env, jreturned_hash, go_returned_hash.data, 0);
|
||||||
} else if (open_after_creation) {
|
} else {
|
||||||
for (unsigned int i=0; i<returned_hash_len; ++i) {
|
for (unsigned int i=0; i<returned_hash_len; ++i) {
|
||||||
((unsigned char*) go_returned_hash.data)[i] = 0;
|
((unsigned char*) go_returned_hash.data)[i] = 0;
|
||||||
}
|
}
|
||||||
free(go_returned_hash.data);
|
free(go_returned_hash.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionID*open_after_creation+result*!open_after_creation;
|
return sessionID;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
@ -81,13 +75,13 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_nativeIni
|
|||||||
jbyte* given_hash;
|
jbyte* given_hash;
|
||||||
GoSlice go_given_hash = {NULL, 0, 0};
|
GoSlice go_given_hash = {NULL, 0, 0};
|
||||||
if ((*env)->IsSameObject(env, jgiven_hash, NULL)){
|
if ((*env)->IsSameObject(env, jgiven_hash, NULL)){
|
||||||
password_len = (*env)->GetArrayLength(env, jpassword);
|
password_len = (size_t) (*env)->GetArrayLength(env, jpassword);
|
||||||
password = (*env)->GetByteArrayElements(env, jpassword, NULL);
|
password = (*env)->GetByteArrayElements(env, jpassword, NULL);
|
||||||
go_password.data = password;
|
go_password.data = password;
|
||||||
go_password.len = password_len;
|
go_password.len = password_len;
|
||||||
go_password.cap = password_len;
|
go_password.cap = password_len;
|
||||||
} else {
|
} else {
|
||||||
given_hash_len = (*env)->GetArrayLength(env, jgiven_hash);
|
given_hash_len = (size_t) (*env)->GetArrayLength(env, jgiven_hash);
|
||||||
given_hash = (*env)->GetByteArrayElements(env, jgiven_hash, NULL);
|
given_hash = (*env)->GetByteArrayElements(env, jgiven_hash, NULL);
|
||||||
go_given_hash.data = given_hash;
|
go_given_hash.data = given_hash;
|
||||||
go_given_hash.len = given_hash_len;
|
go_given_hash.len = given_hash_len;
|
||||||
@ -98,7 +92,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_nativeIni
|
|||||||
jbyte* returned_hash;
|
jbyte* returned_hash;
|
||||||
GoSlice go_returned_hash = {NULL, 0, 0};
|
GoSlice go_returned_hash = {NULL, 0, 0};
|
||||||
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)){
|
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)){
|
||||||
returned_hash_len = (*env)->GetArrayLength(env, jreturned_hash);
|
returned_hash_len = (size_t) (*env)->GetArrayLength(env, jreturned_hash);
|
||||||
returned_hash = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
returned_hash = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
||||||
go_returned_hash.data = returned_hash;
|
go_returned_hash.data = returned_hash;
|
||||||
go_returned_hash.len = returned_hash_len;
|
go_returned_hash.len = returned_hash_len;
|
||||||
@ -145,20 +139,20 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_changePas
|
|||||||
jbyte* given_hash;
|
jbyte* given_hash;
|
||||||
GoSlice go_given_hash = {NULL, 0, 0};
|
GoSlice go_given_hash = {NULL, 0, 0};
|
||||||
if ((*env)->IsSameObject(env, jgiven_hash, NULL)){
|
if ((*env)->IsSameObject(env, jgiven_hash, NULL)){
|
||||||
old_password_len = (*env)->GetArrayLength(env, jold_password);
|
old_password_len = (size_t) (*env)->GetArrayLength(env, jold_password);
|
||||||
old_password = (*env)->GetByteArrayElements(env, jold_password, NULL);
|
old_password = (*env)->GetByteArrayElements(env, jold_password, NULL);
|
||||||
go_old_password.data = old_password;
|
go_old_password.data = old_password;
|
||||||
go_old_password.len = old_password_len;
|
go_old_password.len = old_password_len;
|
||||||
go_old_password.cap = old_password_len;
|
go_old_password.cap = old_password_len;
|
||||||
} else {
|
} else {
|
||||||
given_hash_len = (*env)->GetArrayLength(env, jgiven_hash);
|
given_hash_len = (size_t) (*env)->GetArrayLength(env, jgiven_hash);
|
||||||
given_hash = (*env)->GetByteArrayElements(env, jgiven_hash, NULL);
|
given_hash = (*env)->GetByteArrayElements(env, jgiven_hash, NULL);
|
||||||
go_given_hash.data = given_hash;
|
go_given_hash.data = given_hash;
|
||||||
go_given_hash.len = given_hash_len;
|
go_given_hash.len = given_hash_len;
|
||||||
go_given_hash.cap = given_hash_len;
|
go_given_hash.cap = given_hash_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t new_password_len = (*env)->GetArrayLength(env, jnew_password);
|
size_t new_password_len = (size_t) (*env)->GetArrayLength(env, jnew_password);
|
||||||
jbyte* new_password = (*env)->GetByteArrayElements(env, jnew_password, NULL);
|
jbyte* new_password = (*env)->GetByteArrayElements(env, jnew_password, NULL);
|
||||||
GoSlice go_new_password = {new_password, new_password_len, new_password_len};
|
GoSlice go_new_password = {new_password, new_password_len, new_password_len};
|
||||||
|
|
||||||
@ -166,7 +160,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_00024Companion_changePas
|
|||||||
jbyte* returned_hash;
|
jbyte* returned_hash;
|
||||||
GoSlice go_returned_hash = {NULL, 0, 0};
|
GoSlice go_returned_hash = {NULL, 0, 0};
|
||||||
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
if (!(*env)->IsSameObject(env, jreturned_hash, NULL)) {
|
||||||
returned_hash_len = (*env)->GetArrayLength(env, jreturned_hash);
|
returned_hash_len = (size_t) (*env)->GetArrayLength(env, jreturned_hash);
|
||||||
returned_hash = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
returned_hash = (*env)->GetByteArrayElements(env, jreturned_hash, NULL);
|
||||||
go_returned_hash.data = returned_hash;
|
go_returned_hash.data = returned_hash;
|
||||||
go_returned_hash.len = returned_hash_len;
|
go_returned_hash.len = returned_hash_len;
|
||||||
@ -303,7 +297,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1open_1write_1mod
|
|||||||
const char* file_path = (*env)->GetStringUTFChars(env, jfile_path, NULL);
|
const char* file_path = (*env)->GetStringUTFChars(env, jfile_path, NULL);
|
||||||
GoString go_file_path = {file_path, strlen(file_path)};
|
GoString go_file_path = {file_path, strlen(file_path)};
|
||||||
|
|
||||||
GoInt handleID = gcf_open_write_mode(sessionID, go_file_path, mode);
|
GoInt handleID = gcf_open_write_mode(sessionID, go_file_path, (GoUint32) mode);
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(env, jfile_path, file_path);
|
(*env)->ReleaseStringUTFChars(env, jfile_path, file_path);
|
||||||
|
|
||||||
@ -317,7 +311,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1write_1file(JNIE
|
|||||||
jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL);
|
jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL);
|
||||||
GoSlice go_buff = {buff+src_offset, length, length};
|
GoSlice go_buff = {buff+src_offset, length, length};
|
||||||
|
|
||||||
int written = gcf_write_file(sessionID, handleID, file_offset, go_buff);
|
int written = gcf_write_file(sessionID, handleID, (GoUint64) file_offset, go_buff);
|
||||||
|
|
||||||
(*env)->ReleaseByteArrayElements(env, jbuff, buff, 0);
|
(*env)->ReleaseByteArrayElements(env, jbuff, buff, 0);
|
||||||
|
|
||||||
@ -331,7 +325,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1read_1file(JNIEn
|
|||||||
jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL);
|
jbyte* buff = (*env)->GetByteArrayElements(env, jbuff, NULL);
|
||||||
GoSlice go_buff = {buff+dst_offset, length, length};
|
GoSlice go_buff = {buff+dst_offset, length, length};
|
||||||
|
|
||||||
int read = gcf_read_file(sessionID, handleID, file_offset, go_buff);
|
int read = gcf_read_file(sessionID, handleID, (GoUint64) file_offset, go_buff);
|
||||||
|
|
||||||
(*env)->ReleaseByteArrayElements(env, jbuff, buff, 0);
|
(*env)->ReleaseByteArrayElements(env, jbuff, buff, 0);
|
||||||
return read;
|
return read;
|
||||||
@ -345,7 +339,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1truncate(JNIEnv
|
|||||||
const char* path = (*env)->GetStringUTFChars(env, jpath, NULL);
|
const char* path = (*env)->GetStringUTFChars(env, jpath, NULL);
|
||||||
GoString go_path = {path, strlen(path)};
|
GoString go_path = {path, strlen(path)};
|
||||||
|
|
||||||
GoUint8 result = gcf_truncate(sessionID, go_path, offset);
|
GoUint8 result = gcf_truncate(sessionID, go_path, (GoUint64) offset);
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(env, jpath, path);
|
(*env)->ReleaseStringUTFChars(env, jpath, path);
|
||||||
return result;
|
return result;
|
||||||
@ -377,7 +371,7 @@ Java_sushi_hardcore_droidfs_filesystems_GocryptfsVolume_native_1mkdir(JNIEnv *en
|
|||||||
const char* dir_path = (*env)->GetStringUTFChars(env, jdir_path, NULL);
|
const char* dir_path = (*env)->GetStringUTFChars(env, jdir_path, NULL);
|
||||||
GoString go_dir_path = {dir_path, strlen(dir_path)};
|
GoString go_dir_path = {dir_path, strlen(dir_path)};
|
||||||
|
|
||||||
GoUint8 result = gcf_mkdir(sessionID, go_dir_path, mode);
|
GoUint8 result = gcf_mkdir(sessionID, go_dir_path, (GoUint32) mode);
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(env, jdir_path, dir_path);
|
(*env)->ReleaseStringUTFChars(env, jdir_path, dir_path);
|
||||||
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
<vector android:height="24dp" android:viewportHeight="500"
|
|
||||||
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="?attr/colorAccent" android:pathData="M68.29,431.711c0,20.078 16.264,36.34 36.343,36.34h290.734c20.078,0 36.345,-16.262 36.345,-36.34V250c0,-20.079 -16.267,-36.342 -36.345,-36.342H177.317v-63.597c0,-40.157 32.525,-72.685 72.683,-72.685c40.158,0 72.685,32.528 72.685,72.685v4.541c0,12.538 10.176,22.715 22.711,22.715c12.537,0 22.717,-10.177 22.717,-22.715v-4.541c0,-65.232 -52.882,-118.111 -118.112,-118.111c-65.24,0 -118.111,52.879 -118.111,118.111v63.597h-27.256c-20.079,0 -36.343,16.263 -36.343,36.342V431.711zM213.658,313.599c0,-20.078 16.263,-36.341 36.342,-36.341s36.341,16.263 36.341,36.341c0,12.812 -6.634,24.079 -16.625,30.529c0,0 3.55,21.446 7.542,46.699c0,7.538 -6.087,13.625 -13.629,13.625h-27.258c-7.541,0 -13.627,-6.087 -13.627,-13.625l7.542,-46.699C220.294,337.678 213.658,326.41 213.658,313.599z"/>
|
|
||||||
</vector>
|
|
5
app/src/main/res/drawable/icon_lock.xml
Normal file
5
app/src/main/res/drawable/icon_lock.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<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="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/icon_lock_open.xml
Normal file
5
app/src/main/res/drawable/icon_lock_open.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?attr/colorAccent" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
|
||||||
|
</vector>
|
@ -8,21 +8,23 @@
|
|||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/selectable_container"
|
android:id="@+id/selectable_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent"
|
||||||
|
android:paddingEnd="4dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image_icon"
|
android:id="@+id/image_icon"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"/>
|
android:layout_height="50dp"
|
||||||
|
android:src="@drawable/icon_volume"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_toEndOf="@id/image_icon"
|
android:layout_toEndOf="@id/image_icon"
|
||||||
android:layout_toStartOf="@id/icon_fingerprint">
|
android:layout_toStartOf="@id/layout_icons">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_volume_name"
|
android:id="@+id/text_volume_name"
|
||||||
@ -63,16 +65,31 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/icon_fingerprint"
|
android:id="@+id/layout_icons"
|
||||||
android:layout_width="26dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="26dp"
|
android:layout_height="match_parent"
|
||||||
android:src="@drawable/icon_fingerprint"
|
android:gravity="center_vertical"
|
||||||
android:contentDescription="@string/password_hash_saved"
|
android:layout_alignParentEnd="true">
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_alignParentEnd="true"
|
<ImageView
|
||||||
android:layout_marginTop="11dp"
|
android:id="@+id/icon_unlocked"
|
||||||
android:layout_marginEnd="5dp"/>
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/icon_lock_open"
|
||||||
|
android:contentDescription="@string/volume_unlocked"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon_fingerprint"
|
||||||
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:src="@drawable/icon_fingerprint"
|
||||||
|
android:contentDescription="@string/password_hash_saved"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginStart="5dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -30,9 +30,8 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/decrypt"
|
android:id="@+id/decrypt"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_decrypt"
|
|
||||||
android:title="@string/decrypt_files"/>
|
android:title="@string/decrypt_files"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
@ -72,6 +71,12 @@
|
|||||||
android:title="@string/sort_by"
|
android:title="@string/sort_by"
|
||||||
android:icon="@drawable/icon_sort"/>
|
android:icon="@drawable/icon_sort"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/lock"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/lock_volume"
|
||||||
|
android:icon="@drawable/icon_lock"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/close"
|
android:id="@+id/close"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
|
@ -19,6 +19,12 @@
|
|||||||
android:title="@string/sort_by"
|
android:title="@string/sort_by"
|
||||||
android:icon="@drawable/icon_sort"/>
|
android:icon="@drawable/icon_sort"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/lock"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/lock_volume"
|
||||||
|
android:icon="@drawable/icon_lock"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/close"
|
android:id="@+id/close"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
|
@ -38,6 +38,12 @@
|
|||||||
android:title="@string/sort_by"
|
android:title="@string/sort_by"
|
||||||
android:icon="@drawable/icon_sort"/>
|
android:icon="@drawable/icon_sort"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/lock"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/lock_volume"
|
||||||
|
android:icon="@drawable/icon_lock"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/close"
|
android:id="@+id/close"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
|
@ -9,6 +9,13 @@
|
|||||||
android:title="@string/select_all"
|
android:title="@string/select_all"
|
||||||
android:icon="@drawable/icon_select_all"/>
|
android:icon="@drawable/icon_select_all"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/lock"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/lock"
|
||||||
|
android:icon="@drawable/icon_lock"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/remove"
|
android:id="@+id/remove"
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<string name="mkdir">إنشاء مجلد</string>
|
<string name="mkdir">إنشاء مجلد</string>
|
||||||
<string name="dir_empty">مجلد فارغ</string>
|
<string name="dir_empty">مجلد فارغ</string>
|
||||||
<string name="warning">تحذير !</string>
|
<string name="warning">تحذير !</string>
|
||||||
<string name="ask_close_volume">هل تريد إغلاق المجلد المشفر ?</string>
|
|
||||||
<string name="ok">حسنا</string>
|
<string name="ok">حسنا</string>
|
||||||
<string name="cancel">إلغاء</string>
|
<string name="cancel">إلغاء</string>
|
||||||
<string name="enter_folder_name">اسم المجلد:</string>
|
<string name="enter_folder_name">اسم المجلد:</string>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<string name="mkdir">Crear carpeta</string>
|
<string name="mkdir">Crear carpeta</string>
|
||||||
<string name="dir_empty">Directorio vacío</string>
|
<string name="dir_empty">Directorio vacío</string>
|
||||||
<string name="warning">¡Advertencia!</string>
|
<string name="warning">¡Advertencia!</string>
|
||||||
<string name="ask_close_volume">¿Estás seguro de que quieres cerrar este volumen?</string>
|
|
||||||
<string name="ok">Aceptar</string>
|
<string name="ok">Aceptar</string>
|
||||||
<string name="cancel">Cancelar</string>
|
<string name="cancel">Cancelar</string>
|
||||||
<string name="enter_folder_name">Nombre de la carpeta:</string>
|
<string name="enter_folder_name">Nombre de la carpeta:</string>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<string name="mkdir">Criar pasta</string>
|
<string name="mkdir">Criar pasta</string>
|
||||||
<string name="dir_empty">Pasta vazia</string>
|
<string name="dir_empty">Pasta vazia</string>
|
||||||
<string name="warning">Aviso!</string>
|
<string name="warning">Aviso!</string>
|
||||||
<string name="ask_close_volume">Tem certeza que quer fechar este volume?</string>
|
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="cancel">Cancelar</string>
|
<string name="cancel">Cancelar</string>
|
||||||
<string name="enter_folder_name">Nome da pasta:</string>
|
<string name="enter_folder_name">Nome da pasta:</string>
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
<string name="mkdir">Создать папку</string>
|
<string name="mkdir">Создать папку</string>
|
||||||
<string name="dir_empty">Пусто</string>
|
<string name="dir_empty">Пусто</string>
|
||||||
<string name="warning">Предупреждение!</string>
|
<string name="warning">Предупреждение!</string>
|
||||||
<string name="ask_close_volume">Закрыть том?</string>
|
|
||||||
<string name="cancel">Отмена</string>
|
<string name="cancel">Отмена</string>
|
||||||
<string name="enter_folder_name">Имя папки:</string>
|
<string name="enter_folder_name">Имя папки:</string>
|
||||||
<string name="error">Ошибка</string>
|
<string name="error">Ошибка</string>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<string name="mkdir">Create folder</string>
|
<string name="mkdir">Create folder</string>
|
||||||
<string name="dir_empty">Directory Empty</string>
|
<string name="dir_empty">Directory Empty</string>
|
||||||
<string name="warning">Warning !</string>
|
<string name="warning">Warning !</string>
|
||||||
<string name="ask_close_volume">Are you sure you want to close this volume ?</string>
|
<string name="ask_lock_volume">Are you sure you want to lock this volume ?</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="enter_folder_name">Folder Name:</string>
|
<string name="enter_folder_name">Folder Name:</string>
|
||||||
@ -253,4 +253,7 @@
|
|||||||
<string name="remember_volume">Remember volume</string>
|
<string name="remember_volume">Remember volume</string>
|
||||||
<string name="open_volume">Open volume</string>
|
<string name="open_volume">Open volume</string>
|
||||||
<string name="choose_existing_volume">Please choose an existing volume</string>
|
<string name="choose_existing_volume">Please choose an existing volume</string>
|
||||||
|
<string name="volume_unlocked">Volume unlocked</string>
|
||||||
|
<string name="lock_volume">Lock volume</string>
|
||||||
|
<string name="lock">Lock</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:icon="@drawable/icon_decrypt"
|
android:icon="@drawable/icon_lock_open"
|
||||||
android:key="usf_decrypt"
|
android:key="usf_decrypt"
|
||||||
android:title="@string/usf_decrypt" />
|
android:title="@string/usf_decrypt" />
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:icon="@drawable/icon_decrypt"
|
android:icon="@drawable/icon_lock_open"
|
||||||
android:key="usf_keep_open"
|
android:key="usf_keep_open"
|
||||||
android:title="@string/usf_keep_open" />
|
android:title="@string/usf_keep_open" />
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.4.1'
|
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user