"Copy" feature, FAB icons & GitHub link
This commit is contained in:
parent
34d7f19927
commit
5a444f2829
@ -26,7 +26,6 @@ android {
|
|||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
useProguard true
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,9 @@ func openBackingDir(sessionID int, relPath string) (dirfd int, cName string, err
|
|||||||
if i == len(parts)-1 {
|
if i == len(parts)-1 {
|
||||||
cache_dirfd, err := syscall.Dup(dirfd)
|
cache_dirfd, err := syscall.Dup(dirfd)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sessions[sessionID].dirCache[dirRelPath] = Directory{cache_dirfd, iv}
|
var dirRelPathCopy strings.Builder
|
||||||
|
dirRelPathCopy.WriteString(dirRelPath)
|
||||||
|
sessions[sessionID].dirCache[dirRelPathCopy.String()] = Directory{cache_dirfd, iv}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".ColoredApplication"
|
android:name=".ColoredApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/icon_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
@ -28,6 +28,7 @@
|
|||||||
<activity android:name=".explorers.ExplorerActivityDrop" />
|
<activity android:name=".explorers.ExplorerActivityDrop" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".OpenActivity"
|
android:name=".OpenActivity"
|
||||||
|
android:parentActivityName=".MainActivity"
|
||||||
android:screenOrientation="nosensor"
|
android:screenOrientation="nosensor"
|
||||||
android:windowSoftInputMode="adjustPan">
|
android:windowSoftInputMode="adjustPan">
|
||||||
<intent-filter android:label="@string/share_menu_label">
|
<intent-filter android:label="@string/share_menu_label">
|
||||||
@ -39,9 +40,11 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".CreateActivity"
|
android:name=".CreateActivity"
|
||||||
|
android:parentActivityName=".MainActivity"
|
||||||
android:screenOrientation="nosensor" />
|
android:screenOrientation="nosensor" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ChangePasswordActivity"
|
android:name=".ChangePasswordActivity"
|
||||||
|
android:parentActivityName=".MainActivity"
|
||||||
android:screenOrientation="nosensor"
|
android:screenOrientation="nosensor"
|
||||||
android:windowSoftInputMode="adjustPan" />
|
android:windowSoftInputMode="adjustPan" />
|
||||||
<activity
|
<activity
|
||||||
|
@ -33,6 +33,7 @@ class ChangePasswordActivity : BaseActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_change_password)
|
setContentView(R.layout.activity_change_password)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
||||||
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
||||||
|
@ -9,6 +9,7 @@ class ConstValues {
|
|||||||
const val saved_volumes_key = "saved_volumes"
|
const val saved_volumes_key = "saved_volumes"
|
||||||
const val sort_order_key = "sort_order"
|
const val sort_order_key = "sort_order"
|
||||||
val fakeUri: Uri = Uri.parse("fakeuri://droidfs")
|
val fakeUri: Uri = Uri.parse("fakeuri://droidfs")
|
||||||
|
const val MAX_KERNEL_WRITE = 128*1024
|
||||||
const val wipe_passes = 2
|
const val wipe_passes = 2
|
||||||
private val fileExtensions = mapOf(
|
private val fileExtensions = mapOf(
|
||||||
Pair("image", listOf("png", "jpg", "jpeg")),
|
Pair("image", listOf("png", "jpg", "jpeg")),
|
||||||
|
@ -31,6 +31,7 @@ class CreateActivity : BaseActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_create)
|
setContentView(R.layout.activity_create)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
||||||
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
||||||
|
@ -38,6 +38,7 @@ class OpenActivity : BaseActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_open)
|
setContentView(R.layout.activity_open)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
usf_fingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && usf_fingerprint) {
|
||||||
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
fingerprintPasswordHashSaver = FingerprintPasswordHashSaver(this, sharedPrefs)
|
||||||
|
@ -26,7 +26,6 @@ class SettingsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceSt
|
|||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, fragment)
|
.replace(R.id.settings, fragment)
|
||||||
.commit()
|
.commit()
|
||||||
setTheme(R.style.AppTheme)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
@ -63,11 +63,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
setCurrentPath(currentDirectoryPath)
|
setCurrentPath(currentDirectoryPath)
|
||||||
list_explorer.adapter = explorerAdapter
|
list_explorer.adapter = explorerAdapter
|
||||||
list_explorer.onItemClickListener = OnItemClickListener { _, _, position, _ -> onExplorerItemClick(position) }
|
list_explorer.onItemClickListener = OnItemClickListener { _, _, position, _ -> onExplorerItemClick(position) }
|
||||||
list_explorer.onItemLongClickListener = OnItemLongClickListener { _, _, position, _ ->
|
list_explorer.onItemLongClickListener = OnItemLongClickListener { _, _, position, _ -> onExplorerItemLongClick(position); true }
|
||||||
explorerAdapter.onItemLongClick(position)
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
refresher.setOnRefreshListener {
|
refresher.setOnRefreshListener {
|
||||||
setCurrentPath(currentDirectoryPath)
|
setCurrentPath(currentDirectoryPath)
|
||||||
refresher.isRefreshing = false
|
refresher.isRefreshing = false
|
||||||
@ -90,13 +86,13 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
explorerAdapter.onItemClick(position)
|
explorerAdapter.onItemClick(position)
|
||||||
if (explorerAdapter.selectedItems.isEmpty()) {
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
||||||
if (!wasSelecting) {
|
if (!wasSelecting) {
|
||||||
val fullPath = PathUtils.path_join(currentDirectoryPath, explorerElements[position].name)
|
val fullPath = explorerElements[position].getFullPath()
|
||||||
when {
|
when {
|
||||||
explorerElements[position].isDirectory -> {
|
explorerElements[position].isDirectory -> {
|
||||||
setCurrentPath(fullPath)
|
setCurrentPath(fullPath)
|
||||||
}
|
}
|
||||||
explorerElements[position].isParentFolder -> {
|
explorerElements[position].isParentFolder -> {
|
||||||
setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
}
|
}
|
||||||
isImage(fullPath) -> {
|
isImage(fullPath) -> {
|
||||||
startFileViewer(ImageViewer::class.java, fullPath)
|
startFileViewer(ImageViewer::class.java, fullPath)
|
||||||
@ -128,8 +124,14 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onExplorerItemLongClick(position: Int) {
|
||||||
|
explorerAdapter.onItemLongClick(position)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +171,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
text_dir_empty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.INVISIBLE
|
text_dir_empty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.INVISIBLE
|
||||||
sortExplorerElements()
|
sortExplorerElements()
|
||||||
if (path.isNotEmpty()) { //not root
|
if (path.isNotEmpty()) { //not root
|
||||||
explorerElements.add(0, ExplorerElement("..", (-1).toShort(), -1, -1))
|
explorerElements.add(0, ExplorerElement("..", (-1).toShort(), -1, -1, currentDirectoryPath))
|
||||||
}
|
}
|
||||||
explorerAdapter.setExplorerElements(explorerElements)
|
explorerAdapter.setExplorerElements(explorerElements)
|
||||||
currentDirectoryPath = path
|
currentDirectoryPath = path
|
||||||
@ -192,16 +194,16 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
|
|
||||||
protected open fun closeVolumeOnDestroy() {
|
protected open fun closeVolumeOnDestroy() {
|
||||||
gocryptfsVolume.close()
|
gocryptfsVolume.close()
|
||||||
RestrictedFileProvider.wipeAll() //additional security
|
RestrictedFileProvider.wipeAll(this) //additional security
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (explorerAdapter.selectedItems.isEmpty()) {
|
if (explorerAdapter.selectedItems.isEmpty()) {
|
||||||
val parentPath = PathUtils.get_parent_path(currentDirectoryPath)
|
val parentPath = PathUtils.getParentPath(currentDirectoryPath)
|
||||||
if (parentPath == currentDirectoryPath) {
|
if (parentPath == currentDirectoryPath) {
|
||||||
askCloseVolume()
|
askCloseVolume()
|
||||||
} else {
|
} else {
|
||||||
setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unselectAll()
|
unselectAll()
|
||||||
@ -266,23 +268,21 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun handleMenuItems(menu: Menu){
|
fun handleMenuItems(menu: Menu){
|
||||||
menu.findItem(R.id.explorer_menu_rename).isVisible = false
|
menu.findItem(R.id.rename).isVisible = false
|
||||||
if (usf_open){
|
if (usf_open){
|
||||||
menu.findItem(R.id.explorer_menu_external_open)?.isVisible = false
|
menu.findItem(R.id.external_open)?.isVisible = false
|
||||||
}
|
}
|
||||||
val selectedItems = explorerAdapter.selectedItems
|
val noItemSelected = explorerAdapter.selectedItems.isEmpty()
|
||||||
if (selectedItems.isEmpty()){
|
menu.findItem(R.id.sort).isVisible = noItemSelected
|
||||||
|
menu.findItem(R.id.close).isVisible = noItemSelected
|
||||||
|
if (noItemSelected){
|
||||||
toolbar.navigationIcon = null
|
toolbar.navigationIcon = null
|
||||||
menu.findItem(R.id.explorer_menu_close).isVisible = true
|
|
||||||
menu.findItem(R.id.explorer_menu_sort).isVisible = true
|
|
||||||
} else {
|
} else {
|
||||||
toolbar.setNavigationIcon(R.drawable.icon_arrow_back)
|
toolbar.setNavigationIcon(R.drawable.icon_arrow_back)
|
||||||
menu.findItem(R.id.explorer_menu_close).isVisible = false
|
if (explorerAdapter.selectedItems.size == 1) {
|
||||||
menu.findItem(R.id.explorer_menu_sort).isVisible = false
|
menu.findItem(R.id.rename).isVisible = true
|
||||||
if (selectedItems.size == 1) {
|
if (usf_open && explorerElements[explorerAdapter.selectedItems[0]].isRegularFile) {
|
||||||
menu.findItem(R.id.explorer_menu_rename).isVisible = true
|
menu.findItem(R.id.external_open)?.isVisible = true
|
||||||
if (usf_open && explorerElements[selectedItems[0]].isRegularFile) {
|
|
||||||
menu.findItem(R.id.explorer_menu_external_open)?.isVisible = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +294,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
unselectAll()
|
unselectAll()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_sort -> {
|
R.id.sort -> {
|
||||||
ColoredAlertDialogBuilder(this)
|
ColoredAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.sort_order)
|
.setTitle(R.string.sort_order)
|
||||||
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which ->
|
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortModesEntries), currentSortModeIndex) { dialog, which ->
|
||||||
@ -306,7 +306,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
.show()
|
.show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_rename -> {
|
R.id.rename -> {
|
||||||
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
|
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
|
||||||
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
|
val oldName = explorerElements[explorerAdapter.selectedItems[0]].name
|
||||||
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
|
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
|
||||||
@ -331,14 +331,14 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_external_open -> {
|
R.id.external_open -> {
|
||||||
if (usf_open){
|
if (usf_open){
|
||||||
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
|
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
|
||||||
unselectAll()
|
unselectAll()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_close -> {
|
R.id.close -> {
|
||||||
askCloseVolume()
|
askCloseVolume()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.github.clans.fab.FloatingActionButton
|
||||||
import com.github.clans.fab.FloatingActionMenu
|
import com.github.clans.fab.FloatingActionMenu
|
||||||
import kotlinx.android.synthetic.main.activity_explorer.*
|
import kotlinx.android.synthetic.main.activity_explorer.*
|
||||||
import sushi.hardcore.droidfs.OpenActivity
|
import sushi.hardcore.droidfs.OpenActivity
|
||||||
@ -18,7 +18,7 @@ import sushi.hardcore.droidfs.R
|
|||||||
import sushi.hardcore.droidfs.util.*
|
import sushi.hardcore.droidfs.util.*
|
||||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class ExplorerActivity : BaseExplorerActivity() {
|
class ExplorerActivity : BaseExplorerActivity() {
|
||||||
private val PICK_DIRECTORY_REQUEST_CODE = 1
|
private val PICK_DIRECTORY_REQUEST_CODE = 1
|
||||||
@ -26,12 +26,20 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
private val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
|
private val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
|
||||||
private var usf_decrypt = false
|
private var usf_decrypt = false
|
||||||
private var usf_share = false
|
private var usf_share = false
|
||||||
|
private var modeSelectLocation = false
|
||||||
|
private val filesToCopy = ArrayList<ExplorerElement>()
|
||||||
override fun init() {
|
override fun init() {
|
||||||
setContentView(R.layout.activity_explorer)
|
setContentView(R.layout.activity_explorer)
|
||||||
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
|
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
|
||||||
usf_share = sharedPrefs.getBoolean("usf_share", false)
|
usf_share = sharedPrefs.getBoolean("usf_share", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onExplorerItemLongClick(position: Int) {
|
||||||
|
cancelCopy()
|
||||||
|
explorerAdapter.onItemLongClick(position)
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
|
||||||
private fun createNewFile(fileName: String){
|
private fun createNewFile(fileName: String){
|
||||||
if (fileName.isEmpty()) {
|
if (fileName.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()
|
||||||
@ -230,7 +238,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
failedItem = if (types[i] == 0) { //directory
|
failedItem = if (types[i] == 0) { //directory
|
||||||
recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)
|
recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)
|
||||||
} else {
|
} else {
|
||||||
if (importFileFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)) null else paths[i]
|
if (importFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.path_join(currentDirectoryPath, File(paths[i]).name))) null else paths[i]
|
||||||
}
|
}
|
||||||
if (failedItem != null) {
|
if (failedItem != null) {
|
||||||
break
|
break
|
||||||
@ -238,7 +246,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path
|
failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name))) null else path
|
||||||
}
|
}
|
||||||
if (failedItem == null) {
|
if (failedItem == null) {
|
||||||
stopTask {
|
stopTask {
|
||||||
@ -269,24 +277,30 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.explorer, menu)
|
menuInflater.inflate(R.menu.explorer, menu)
|
||||||
handleMenuItems(menu)
|
if (modeSelectLocation) {
|
||||||
if (usf_share){
|
menu.findItem(R.id.validate).isVisible = true
|
||||||
menu.findItem(R.id.explorer_menu_share).isVisible = false
|
menu.findItem(R.id.close).isVisible = false
|
||||||
}
|
} else {
|
||||||
val anyItemSelected = explorerAdapter.selectedItems.isNotEmpty()
|
handleMenuItems(menu)
|
||||||
menu.findItem(R.id.explorer_menu_select_all).isVisible = anyItemSelected
|
if (usf_share){
|
||||||
menu.findItem(R.id.explorer_menu_delete).isVisible = anyItemSelected
|
menu.findItem(R.id.share).isVisible = false
|
||||||
menu.findItem(R.id.explorer_menu_decrypt).isVisible = anyItemSelected && usf_decrypt
|
|
||||||
if (anyItemSelected && usf_share){
|
|
||||||
var containsDir = false
|
|
||||||
for (i in explorerAdapter.selectedItems) {
|
|
||||||
if (explorerElements[i].isDirectory) {
|
|
||||||
containsDir = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!containsDir) {
|
val anyItemSelected = explorerAdapter.selectedItems.isNotEmpty()
|
||||||
menu.findItem(R.id.explorer_menu_share).isVisible = true
|
menu.findItem(R.id.select_all).isVisible = anyItemSelected
|
||||||
|
menu.findItem(R.id.delete).isVisible = anyItemSelected
|
||||||
|
menu.findItem(R.id.copy).isVisible = anyItemSelected
|
||||||
|
menu.findItem(R.id.decrypt).isVisible = anyItemSelected && usf_decrypt
|
||||||
|
if (anyItemSelected && usf_share){
|
||||||
|
var containsDir = false
|
||||||
|
for (i in explorerAdapter.selectedItems) {
|
||||||
|
if (explorerElements[i].isDirectory) {
|
||||||
|
containsDir = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!containsDir) {
|
||||||
|
menu.findItem(R.id.share).isVisible = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -294,12 +308,67 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.explorer_menu_select_all -> {
|
android.R.id.home -> {
|
||||||
|
cancelCopy()
|
||||||
|
super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
R.id.select_all -> {
|
||||||
explorerAdapter.selectAll()
|
explorerAdapter.selectAll()
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_delete -> {
|
R.id.copy -> {
|
||||||
|
for (i in explorerAdapter.selectedItems){
|
||||||
|
filesToCopy.add(explorerElements[i])
|
||||||
|
}
|
||||||
|
modeSelectLocation = true
|
||||||
|
unselectAll()
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_add_file).visibility = View.GONE
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_import_file).visibility = View.GONE
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_import_file_from_other_volume).visibility = View.GONE
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.validate -> {
|
||||||
|
object : LoadingTask(this, R.string.loading_msg_copy){
|
||||||
|
override fun doTask(activity: AppCompatActivity) {
|
||||||
|
var failedItem: String? = null
|
||||||
|
for (element in filesToCopy) {
|
||||||
|
val originalPath = element.getFullPath()
|
||||||
|
failedItem = if (element.isDirectory) {
|
||||||
|
recursiveCopyDirectory(originalPath, currentDirectoryPath)
|
||||||
|
} else {
|
||||||
|
if (copyFile(originalPath, PathUtils.path_join(currentDirectoryPath, element.name))) null else originalPath
|
||||||
|
}
|
||||||
|
if (failedItem != null) {
|
||||||
|
stopTask {
|
||||||
|
ColoredAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.error)
|
||||||
|
.setMessage(getString(R.string.copy_failed, failedItem))
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failedItem == null) {
|
||||||
|
stopTask {
|
||||||
|
ColoredAlertDialogBuilder(activity)
|
||||||
|
.setTitle(getString(R.string.copy_success))
|
||||||
|
.setMessage(getString(R.string.copy_success_msg))
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun doFinally(activity: AppCompatActivity) {
|
||||||
|
cancelCopy()
|
||||||
|
unselectAll()
|
||||||
|
setCurrentPath(currentDirectoryPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.delete -> {
|
||||||
val size = explorerAdapter.selectedItems.size
|
val size = explorerAdapter.selectedItems.size
|
||||||
val dialog = ColoredAlertDialogBuilder(this)
|
val dialog = ColoredAlertDialogBuilder(this)
|
||||||
dialog.setTitle(R.string.warning)
|
dialog.setTitle(R.string.warning)
|
||||||
@ -313,17 +382,16 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_share -> {
|
R.id.share -> {
|
||||||
val paths: MutableList<String> = ArrayList()
|
val paths: MutableList<String> = ArrayList()
|
||||||
for (i in explorerAdapter.selectedItems) {
|
for (i in explorerAdapter.selectedItems) {
|
||||||
val e = explorerElements[i]
|
paths.add(explorerElements[i].getFullPath())
|
||||||
paths.add(PathUtils.path_join(currentDirectoryPath, e.name))
|
|
||||||
}
|
}
|
||||||
ExternalProvider.share(this, gocryptfsVolume, paths)
|
ExternalProvider.share(this, gocryptfsVolume, paths)
|
||||||
unselectAll()
|
unselectAll()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_decrypt -> {
|
R.id.decrypt -> {
|
||||||
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
|
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
|
||||||
true
|
true
|
||||||
@ -332,12 +400,86 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importFileFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, full_path: String, output_dir: String): Boolean {
|
private fun recursiveMapFiles(rootPath: String): MutableList<ExplorerElement> {
|
||||||
val outputPath = PathUtils.path_join(output_dir, File(full_path).name)
|
val result = mutableListOf<ExplorerElement>()
|
||||||
|
val explorerElements = gocryptfsVolume.list_dir(rootPath)
|
||||||
|
result.addAll(explorerElements)
|
||||||
|
for (e in explorerElements){
|
||||||
|
if (e.isDirectory){
|
||||||
|
result.addAll(recursiveMapFiles(e.getFullPath()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelCopy() {
|
||||||
|
if (modeSelectLocation){
|
||||||
|
modeSelectLocation = false
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_add_file).visibility = View.VISIBLE
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_import_file).visibility = View.VISIBLE
|
||||||
|
findViewById<FloatingActionButton>(R.id.fab_import_file_from_other_volume).visibility = View.VISIBLE
|
||||||
|
filesToCopy.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyFile(srcPath: String, dstPath: String): Boolean {
|
||||||
var success = true
|
var success = true
|
||||||
val srcHandleID = remote_gocryptfsVolume.open_read_mode(full_path)
|
val originalHandleId = gocryptfsVolume.open_read_mode(srcPath)
|
||||||
|
if (originalHandleId != -1){
|
||||||
|
val newHandleId = gocryptfsVolume.open_write_mode(dstPath)
|
||||||
|
if (newHandleId != -1){
|
||||||
|
var offset: Long = 0
|
||||||
|
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
|
||||||
|
var length: Int
|
||||||
|
while (gocryptfsVolume.read_file(originalHandleId, offset, ioBuffer).also { length = it } > 0) {
|
||||||
|
val written = gocryptfsVolume.write_file(newHandleId, offset, ioBuffer, length).toLong()
|
||||||
|
if (written == length.toLong()) {
|
||||||
|
offset += written
|
||||||
|
} else {
|
||||||
|
success = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gocryptfsVolume.close_file(newHandleId)
|
||||||
|
} else {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
gocryptfsVolume.close_file(originalHandleId)
|
||||||
|
} else {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recursiveCopyDirectory(srcDirectoryPath: String, outputPath: String): String? {
|
||||||
|
val mappedElements = recursiveMapFiles(srcDirectoryPath)
|
||||||
|
val dstDirectoryPath = PathUtils.path_join(outputPath, File(srcDirectoryPath).name)
|
||||||
|
if (!gocryptfsVolume.path_exists(dstDirectoryPath)) {
|
||||||
|
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
|
||||||
|
return dstDirectoryPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (e in mappedElements) {
|
||||||
|
val srcPath = e.getFullPath()
|
||||||
|
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, srcPath))
|
||||||
|
if (e.isDirectory) {
|
||||||
|
if (!gocryptfsVolume.mkdir(dstPath)){
|
||||||
|
return srcPath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!copyFile(srcPath, dstPath)) {
|
||||||
|
return srcPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importFileFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean {
|
||||||
|
var success = true
|
||||||
|
val srcHandleID = remote_gocryptfsVolume.open_read_mode(srcPath)
|
||||||
if (srcHandleID != -1) {
|
if (srcHandleID != -1) {
|
||||||
val dstHandleID = gocryptfsVolume.open_write_mode(outputPath)
|
val dstHandleID = gocryptfsVolume.open_write_mode(dstPath)
|
||||||
if (dstHandleID != -1) {
|
if (dstHandleID != -1) {
|
||||||
var length: Int
|
var length: Int
|
||||||
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
|
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
|
||||||
@ -358,22 +500,24 @@ class ExplorerActivity : BaseExplorerActivity() {
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, output_dir: String): String? {
|
private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? {
|
||||||
val directoryPath = PathUtils.path_join(output_dir, File(remote_directory_path).name)
|
val mappedElements = recursiveMapFiles(remote_directory_path)
|
||||||
if (!gocryptfsVolume.path_exists(directoryPath)) {
|
val dstDirectoryPath = PathUtils.path_join(outputPath, File(remote_directory_path).name)
|
||||||
if (!gocryptfsVolume.mkdir(directoryPath)) {
|
if (!gocryptfsVolume.path_exists(dstDirectoryPath)) {
|
||||||
return directoryPath
|
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
|
||||||
|
return dstDirectoryPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val explorerElements = remote_gocryptfsVolume.list_dir(remote_directory_path)
|
for (e in mappedElements) {
|
||||||
for (e in explorerElements) {
|
val srcPath = e.getFullPath()
|
||||||
val fullPath = PathUtils.path_join(remote_directory_path, e.name)
|
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, srcPath))
|
||||||
if (e.isDirectory) {
|
if (e.isDirectory) {
|
||||||
val failedItem = recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume, fullPath, directoryPath)
|
if (!gocryptfsVolume.mkdir(dstPath)){
|
||||||
failedItem?.let { return it }
|
return srcPath
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!importFileFromOtherVolume(remote_gocryptfsVolume, fullPath, directoryPath)) {
|
if (!importFileFromOtherVolume(remote_gocryptfsVolume, srcPath, dstPath)) {
|
||||||
return fullPath
|
return srcPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.explorers
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
@ -16,48 +17,48 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
|
|||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.explorer_drop, menu)
|
menuInflater.inflate(R.menu.explorer_drop, menu)
|
||||||
handleMenuItems(menu)
|
handleMenuItems(menu)
|
||||||
menu.findItem(R.id.explorer_menu_validate).isVisible = explorerAdapter.selectedItems.isEmpty()
|
menu.findItem(R.id.validate).isVisible = explorerAdapter.selectedItems.isEmpty()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.explorer_menu_validate -> {
|
R.id.validate -> {
|
||||||
val alertDialog = ColoredAlertDialogBuilder(this)
|
val alertDialog = ColoredAlertDialogBuilder(this)
|
||||||
alertDialog.setCancelable(false)
|
alertDialog.setCancelable(false)
|
||||||
alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() }
|
alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() }
|
||||||
var error_msg: String? = null
|
var errorMsg: String? = null
|
||||||
val extras = intent.extras
|
val extras = intent.extras
|
||||||
if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){
|
if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){
|
||||||
if (intent.action == Intent.ACTION_SEND) {
|
if (intent.action == Intent.ACTION_SEND) {
|
||||||
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||||
val output_path = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
|
val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
|
||||||
error_msg = if (gocryptfsVolume.import_file(this, uri, output_path)) null else getString(R.string.import_failed, output_path)
|
errorMsg = if (gocryptfsVolume.import_file(this, uri, outputPath)) null else getString(R.string.import_failed, outputPath)
|
||||||
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
||||||
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||||
if (uris != null){
|
if (uris != null){
|
||||||
for (uri in uris) {
|
for (uri in uris) {
|
||||||
val output_path = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
|
val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
|
||||||
if (!gocryptfsVolume.import_file(this, uri, output_path)) {
|
if (!gocryptfsVolume.import_file(this, uri, outputPath)) {
|
||||||
error_msg = getString(R.string.import_failed, output_path)
|
errorMsg = getString(R.string.import_failed, outputPath)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_msg = getString(R.string.share_intent_parsing_failed)
|
errorMsg = getString(R.string.share_intent_parsing_failed)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_msg = getString(R.string.share_intent_parsing_failed)
|
errorMsg = getString(R.string.share_intent_parsing_failed)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_msg = getString(R.string.share_intent_parsing_failed)
|
errorMsg = getString(R.string.share_intent_parsing_failed)
|
||||||
}
|
}
|
||||||
if (error_msg == null) {
|
if (errorMsg == null) {
|
||||||
alertDialog.setTitle(R.string.success_import)
|
alertDialog.setTitle(R.string.success_import)
|
||||||
alertDialog.setMessage(R.string.success_import_msg)
|
alertDialog.setMessage(R.string.success_import_msg)
|
||||||
} else {
|
} else {
|
||||||
alertDialog.setTitle(R.string.error)
|
alertDialog.setTitle(R.string.error)
|
||||||
alertDialog.setMessage(error_msg)
|
alertDialog.setMessage(errorMsg)
|
||||||
}
|
}
|
||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
true
|
true
|
||||||
|
@ -27,7 +27,7 @@ class ExplorerActivityPick : BaseExplorerActivity() {
|
|||||||
setCurrentPath(full_path)
|
setCurrentPath(full_path)
|
||||||
}
|
}
|
||||||
explorerElements[position].isParentFolder -> {
|
explorerElements[position].isParentFolder -> {
|
||||||
setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
result_intent.putExtra("path", full_path)
|
result_intent.putExtra("path", full_path)
|
||||||
@ -43,19 +43,19 @@ class ExplorerActivityPick : BaseExplorerActivity() {
|
|||||||
menuInflater.inflate(R.menu.explorer_pick, menu)
|
menuInflater.inflate(R.menu.explorer_pick, menu)
|
||||||
handleMenuItems(menu)
|
handleMenuItems(menu)
|
||||||
val any_item_selected = explorerAdapter.selectedItems.isNotEmpty()
|
val any_item_selected = explorerAdapter.selectedItems.isNotEmpty()
|
||||||
menu.findItem(R.id.explorer_menu_select_all).isVisible = any_item_selected
|
menu.findItem(R.id.select_all).isVisible = any_item_selected
|
||||||
menu.findItem(R.id.explorer_menu_validate).isVisible = any_item_selected
|
menu.findItem(R.id.validate).isVisible = any_item_selected
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.explorer_menu_select_all -> {
|
R.id.select_all -> {
|
||||||
explorerAdapter.selectAll()
|
explorerAdapter.selectAll()
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.explorer_menu_validate -> {
|
R.id.validate -> {
|
||||||
val paths = ArrayList<String>()
|
val paths = ArrayList<String>()
|
||||||
val types = ArrayList<Int>()
|
val types = ArrayList<Int>()
|
||||||
for (i in explorerAdapter.selectedItems) {
|
for (i in explorerAdapter.selectedItems) {
|
||||||
@ -79,7 +79,7 @@ class ExplorerActivityPick : BaseExplorerActivity() {
|
|||||||
|
|
||||||
override fun closeVolumeOnDestroy() {
|
override fun closeVolumeOnDestroy() {
|
||||||
//don't close volume
|
//don't close volume
|
||||||
RestrictedFileProvider.wipeAll()
|
RestrictedFileProvider.wipeAll(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun closeVolumeOnUserExit() {
|
override fun closeVolumeOnUserExit() {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package sushi.hardcore.droidfs.explorers
|
package sushi.hardcore.droidfs.explorers
|
||||||
|
|
||||||
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ExplorerElement(val name: String, val elementType: Short, val size: Long, mtime: Long) {
|
class ExplorerElement(val name: String, val elementType: Short, val size: Long, mtime: Long, private val parentPath: String) {
|
||||||
val mTime = Date((mtime * 1000).toString().toLong())
|
val mTime = Date((mtime * 1000).toString().toLong())
|
||||||
|
|
||||||
val isDirectory: Boolean
|
val isDirectory: Boolean
|
||||||
@ -14,4 +15,7 @@ class ExplorerElement(val name: String, val elementType: Short, val size: Long,
|
|||||||
val isRegularFile: Boolean
|
val isRegularFile: Boolean
|
||||||
get() = elementType.toInt() == 1
|
get() = elementType.toInt() == 1
|
||||||
|
|
||||||
|
fun getFullPath(): String {
|
||||||
|
return PathUtils.path_join(parentPath, name)
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,6 +4,8 @@ import android.net.Uri
|
|||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.*
|
||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource {
|
class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource {
|
||||||
private var handleID = -1
|
private var handleID = -1
|
||||||
@ -34,15 +36,20 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
|
|||||||
if (fileOffset >= fileSize){
|
if (fileOffset >= fileSize){
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
val tmpBuff = if (fileOffset+readLength > fileSize){
|
var totalRead = 0
|
||||||
ByteArray((fileSize-fileOffset).toInt())
|
for (i in 0 until ceil(readLength.toDouble()/ConstValues.MAX_KERNEL_WRITE).toInt()){
|
||||||
} else {
|
val tmpReadLength = min(readLength-totalRead, ConstValues.MAX_KERNEL_WRITE)
|
||||||
ByteArray(readLength)
|
val tmpBuff = if (fileOffset+tmpReadLength > fileSize){
|
||||||
|
ByteArray((fileSize-fileOffset).toInt())
|
||||||
|
} else {
|
||||||
|
ByteArray(tmpReadLength)
|
||||||
|
}
|
||||||
|
val read = gocryptfsVolume.read_file(handleID, fileOffset, tmpBuff)
|
||||||
|
System.arraycopy(tmpBuff, 0, buffer, offset+totalRead, read)
|
||||||
|
fileOffset += read
|
||||||
|
totalRead += read
|
||||||
}
|
}
|
||||||
val read = gocryptfsVolume.read_file(handleID, fileOffset, tmpBuff)
|
return totalRead
|
||||||
fileOffset += read
|
|
||||||
System.arraycopy(tmpBuff, 0, buffer, offset, read)
|
|
||||||
return read
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource.Factory{
|
class Factory(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource.Factory{
|
||||||
|
@ -2,12 +2,17 @@ package sushi.hardcore.droidfs.provider
|
|||||||
|
|
||||||
import android.content.ContentProvider
|
import android.content.ContentProvider
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.database.MatrixCursor
|
import android.database.MatrixCursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import sushi.hardcore.droidfs.BuildConfig
|
import sushi.hardcore.droidfs.BuildConfig
|
||||||
|
import sushi.hardcore.droidfs.util.SQLUtil.appendSelectionArgs
|
||||||
|
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.*
|
||||||
@ -15,48 +20,105 @@ import java.util.regex.Pattern
|
|||||||
|
|
||||||
class RestrictedFileProvider: ContentProvider() {
|
class RestrictedFileProvider: ContentProvider() {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val DB_NAME = "temporary_files.db"
|
||||||
|
private const val TABLE_FILES = "files"
|
||||||
|
private const val DB_VERSION = 3
|
||||||
|
private var dbHelper: RestrictedDatabaseHelper? = null
|
||||||
private const val AUTHORITY = BuildConfig.APPLICATION_ID + ".temporary_provider"
|
private const val AUTHORITY = BuildConfig.APPLICATION_ID + ".temporary_provider"
|
||||||
private val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
|
private val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
|
||||||
const val TEMPORARY_FILES_DIR_NAME = "temp"
|
const val TEMPORARY_FILES_DIR_NAME = "temp"
|
||||||
private val UUID_PATTERN = Pattern.compile("[a-fA-F0-9-]+")
|
private val UUID_PATTERN = Pattern.compile("[a-fA-F0-9-]+")
|
||||||
|
|
||||||
private lateinit var tempFilesDir: File
|
private lateinit var tempFilesDir: File
|
||||||
private val tempFiles = mutableMapOf<String, TemporaryFile>()
|
|
||||||
|
|
||||||
class TemporaryFile(val fileName: String, val file: File)
|
internal class TemporaryFileColumns {
|
||||||
|
companion object {
|
||||||
|
const val COLUMN_UUID = "uuid"
|
||||||
|
const val COLUMN_NAME = "name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class RestrictedDatabaseHelper(context: Context?): SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||||
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
|
||||||
|
TemporaryFileColumns.COLUMN_UUID + " TEXT PRIMARY KEY, " +
|
||||||
|
TemporaryFileColumns.COLUMN_NAME + " TEXT" +
|
||||||
|
");"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
if (oldVersion == 1) {
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS files")
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
|
||||||
|
TemporaryFileColumns.COLUMN_UUID + " TEXT PRIMARY KEY, " +
|
||||||
|
TemporaryFileColumns.COLUMN_NAME + " TEXT" +
|
||||||
|
");"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun newFile(fileName: String): Uri? {
|
fun newFile(fileName: String): Uri? {
|
||||||
val uuid = UUID.randomUUID().toString()
|
val uuid = UUID.randomUUID().toString()
|
||||||
val file = File(tempFilesDir, uuid)
|
val file = File(tempFilesDir, uuid)
|
||||||
return if (file.createNewFile()){
|
return if (file.createNewFile()){
|
||||||
tempFiles[uuid] = TemporaryFile(fileName, file)
|
val contentValues = ContentValues()
|
||||||
Uri.withAppendedPath(CONTENT_URI, uuid)
|
contentValues.put(TemporaryFileColumns.COLUMN_UUID, uuid)
|
||||||
|
contentValues.put(TemporaryFileColumns.COLUMN_NAME, fileName)
|
||||||
|
if (dbHelper?.writableDatabase?.insert(TABLE_FILES, null, contentValues)?.toInt() != -1){
|
||||||
|
Uri.withAppendedPath(CONTENT_URI, uuid)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wipeAll() {
|
fun wipeAll(context: Context) {
|
||||||
tempFilesDir.listFiles()?.let{
|
tempFilesDir.listFiles()?.let{
|
||||||
for (file in it) {
|
for (file in it) {
|
||||||
Wiper.wipe(file)
|
Wiper.wipe(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
context.deleteDatabase(DB_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFileFromUri(uri: Uri): TemporaryFile? {
|
private fun isValidUUID(uuid: String): Boolean {
|
||||||
|
return UUID_PATTERN.matcher(uuid).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUuidFromUri(uri: Uri): String? {
|
||||||
val uuid = uri.lastPathSegment
|
val uuid = uri.lastPathSegment
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
if (UUID_PATTERN.matcher(uuid).matches()) {
|
if (isValidUUID(uuid)) {
|
||||||
return tempFiles[uuid]
|
return uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFileFromUUID(uuid: String): File? {
|
||||||
|
if (isValidUUID(uuid)){
|
||||||
|
return File(tempFilesDir, uuid)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileFromUri(uri: Uri): File? {
|
||||||
|
getUuidFromUri(uri)?.let {
|
||||||
|
return getFileFromUUID(it)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(): Boolean {
|
override fun onCreate(): Boolean {
|
||||||
context?.let {
|
context?.let {
|
||||||
|
dbHelper = RestrictedDatabaseHelper(it)
|
||||||
tempFilesDir = File(it.cacheDir, TEMPORARY_FILES_DIR_NAME)
|
tempFilesDir = File(it.cacheDir, TEMPORARY_FILES_DIR_NAME)
|
||||||
return tempFilesDir.mkdirs()
|
return tempFilesDir.mkdirs()
|
||||||
}
|
}
|
||||||
@ -73,28 +135,47 @@ class RestrictedFileProvider: ContentProvider() {
|
|||||||
|
|
||||||
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
||||||
val temporaryFile = getFileFromUri(uri)
|
val temporaryFile = getFileFromUri(uri)
|
||||||
if (temporaryFile != null) {
|
temporaryFile?.let{
|
||||||
val cursor = MatrixCursor(
|
val fileName =
|
||||||
arrayOf(
|
dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_NAME), TemporaryFileColumns.COLUMN_UUID + "=?", arrayOf(uri.lastPathSegment), null, null, null)
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
fileName?.let{
|
||||||
MediaStore.MediaColumns.SIZE
|
if (fileName.moveToNext()) {
|
||||||
)
|
val cursor = MatrixCursor(
|
||||||
)
|
arrayOf(
|
||||||
cursor.newRow()
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
.add(temporaryFile.fileName)
|
MediaStore.MediaColumns.SIZE
|
||||||
.add(temporaryFile.file.length())
|
)
|
||||||
return cursor
|
)
|
||||||
|
cursor.newRow()
|
||||||
|
.add(fileName.getString(0))
|
||||||
|
.add(temporaryFile.length())
|
||||||
|
fileName.close()
|
||||||
|
return cursor
|
||||||
|
}
|
||||||
|
fileName.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
|
override fun delete(uri: Uri, givenSelection: String?, givenSelectionArgs: Array<String>?): Int {
|
||||||
val temporaryFile = getFileFromUri(uri)
|
val uuid = getUuidFromUri(uri)
|
||||||
if (temporaryFile != null) {
|
uuid?.let{
|
||||||
Wiper.wipe(temporaryFile.file)
|
val selection = concatenateWhere(givenSelection ?: "" , TemporaryFileColumns.COLUMN_UUID + "=?")
|
||||||
tempFiles.remove(uri.lastPathSegment)
|
val selectionArgs = appendSelectionArgs(givenSelectionArgs, arrayOf(it))
|
||||||
|
|
||||||
|
val files = dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_UUID), selection, selectionArgs, null, null, null)
|
||||||
|
if (files != null) {
|
||||||
|
while (files.moveToNext()) {
|
||||||
|
getFileFromUUID(files.getString(0))?.let { file ->
|
||||||
|
Wiper.wipe(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files.close()
|
||||||
|
return dbHelper?.writableDatabase?.delete(TABLE_FILES, selection, selectionArgs) ?: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(uri: Uri): String {
|
override fun getType(uri: Uri): String {
|
||||||
@ -104,7 +185,7 @@ class RestrictedFileProvider: ContentProvider() {
|
|||||||
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
||||||
if (("w" in mode && callingPackage == BuildConfig.APPLICATION_ID) || "w" !in mode) {
|
if (("w" in mode && callingPackage == BuildConfig.APPLICATION_ID) || "w" !in mode) {
|
||||||
getFileFromUri(uri)?.let{
|
getFileFromUri(uri)?.let{
|
||||||
return ParcelFileDescriptor.open(it.file, ParcelFileDescriptor.parseMode(mode))
|
return ParcelFileDescriptor.open(it, ParcelFileDescriptor.parseMode(mode))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw SecurityException("Read-only access")
|
throw SecurityException("Read-only access")
|
||||||
|
@ -9,7 +9,6 @@ import sushi.hardcore.droidfs.provider.RestrictedFileProvider
|
|||||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLConnection
|
import java.net.URLConnection
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
object ExternalProvider {
|
object ExternalProvider {
|
||||||
@ -102,13 +101,13 @@ object ExternalProvider {
|
|||||||
|
|
||||||
fun removeFiles(context: Context) {
|
fun removeFiles(context: Context) {
|
||||||
Thread{
|
Thread{
|
||||||
val wiped = ArrayList<Uri>()
|
val success = ArrayList<Uri>()
|
||||||
for (uri in storedFiles) {
|
for (uri in storedFiles){
|
||||||
if (Wiper.wipe(context, uri) == null){
|
if (context.contentResolver.delete(uri, null, null) == 1){
|
||||||
wiped.add(uri)
|
success.add(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (uri in wiped){
|
for (uri in success){
|
||||||
storedFiles.remove(uri)
|
storedFiles.remove(uri)
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
|
@ -2,7 +2,6 @@ package sushi.hardcore.droidfs.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
@ -93,10 +92,10 @@ class GocryptfsVolume(var sessionID: Int) {
|
|||||||
|
|
||||||
fun export_file(handleID: Int, os: OutputStream): Boolean {
|
fun export_file(handleID: Int, os: OutputStream): Boolean {
|
||||||
var offset: Long = 0
|
var offset: Long = 0
|
||||||
val io_buffer = ByteArray(DefaultBS)
|
val ioBuffer = ByteArray(DefaultBS)
|
||||||
var length: Int
|
var length: Int
|
||||||
while (native_read_file(sessionID, handleID, offset, io_buffer).also { length = it } > 0){
|
while (native_read_file(sessionID, handleID, offset, ioBuffer).also { length = it } > 0){
|
||||||
os.write(io_buffer, 0, length)
|
os.write(ioBuffer, 0, length)
|
||||||
offset += length.toLong()
|
offset += length.toLong()
|
||||||
}
|
}
|
||||||
os.close()
|
os.close()
|
||||||
@ -105,10 +104,10 @@ class GocryptfsVolume(var sessionID: Int) {
|
|||||||
|
|
||||||
fun export_file(src_path: String, os: OutputStream): Boolean {
|
fun export_file(src_path: String, os: OutputStream): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
val src_handleID = open_read_mode(src_path)
|
val srcHandleId = open_read_mode(src_path)
|
||||||
if (src_handleID != -1) {
|
if (srcHandleId != -1) {
|
||||||
success = export_file(src_handleID, os)
|
success = export_file(srcHandleId, os)
|
||||||
close_file(src_handleID)
|
close_file(srcHandleId)
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
@ -127,10 +126,10 @@ class GocryptfsVolume(var sessionID: Int) {
|
|||||||
|
|
||||||
fun import_file(inputStream: InputStream, handleID: Int): Boolean {
|
fun import_file(inputStream: InputStream, handleID: Int): Boolean {
|
||||||
var offset: Long = 0
|
var offset: Long = 0
|
||||||
val io_buffer = ByteArray(DefaultBS)
|
val ioBuffer = ByteArray(DefaultBS)
|
||||||
var length: Int
|
var length: Int
|
||||||
while (inputStream.read(io_buffer).also { length = it } > 0) {
|
while (inputStream.read(ioBuffer).also { length = it } > 0) {
|
||||||
val written = native_write_file(sessionID, handleID, offset, io_buffer, length).toLong()
|
val written = native_write_file(sessionID, handleID, offset, ioBuffer, length).toLong()
|
||||||
if (written == length.toLong()) {
|
if (written == length.toLong()) {
|
||||||
offset += written
|
offset += written
|
||||||
} else {
|
} else {
|
||||||
@ -145,10 +144,10 @@ class GocryptfsVolume(var sessionID: Int) {
|
|||||||
|
|
||||||
fun import_file(inputStream: InputStream, dst_path: String): Boolean {
|
fun import_file(inputStream: InputStream, dst_path: String): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
val dst_handleID = open_write_mode(dst_path)
|
val dstHandleId = open_write_mode(dst_path)
|
||||||
if (dst_handleID != -1) {
|
if (dstHandleId != -1) {
|
||||||
success = import_file(inputStream, dst_handleID)
|
success = import_file(inputStream, dstHandleId)
|
||||||
close_file(dst_handleID)
|
close_file(dstHandleId)
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import java.text.DecimalFormat;
|
|||||||
|
|
||||||
public class PathUtils {
|
public class PathUtils {
|
||||||
|
|
||||||
public static String get_parent_path(String path){
|
public static String getParentPath(String path){
|
||||||
if (path.endsWith("/")){
|
if (path.endsWith("/")){
|
||||||
String a = path.substring(0, path.length()-2);
|
String a = path.substring(0, path.length()-2);
|
||||||
if (a.contains("/")){
|
if (a.contains("/")){
|
||||||
@ -44,16 +44,22 @@ public class PathUtils {
|
|||||||
return result.substring(0, result.length()-1);
|
return result.substring(0, result.length()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRelativePath(String parentPath, String childPath){
|
||||||
|
return childPath.substring(parentPath.length()+1);
|
||||||
|
}
|
||||||
|
|
||||||
public static String getFilenameFromURI(Context context, Uri uri){
|
public static String getFilenameFromURI(Context context, Uri uri){
|
||||||
String result = null;
|
String result = null;
|
||||||
if (uri.getScheme().equals("content")){
|
if (uri.getScheme().equals("content")){
|
||||||
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
try {
|
if (cursor != null){
|
||||||
if (cursor != null && cursor.moveToFirst()){
|
try {
|
||||||
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
if (cursor.moveToFirst()){
|
||||||
|
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == null){
|
if (result == null){
|
||||||
|
24
app/src/main/java/sushi/hardcore/droidfs/util/SQLUtil.kt
Normal file
24
app/src/main/java/sushi/hardcore/droidfs/util/SQLUtil.kt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package sushi.hardcore.droidfs.util
|
||||||
|
|
||||||
|
object SQLUtil {
|
||||||
|
@JvmStatic
|
||||||
|
fun concatenateWhere(a: String, b: String): String {
|
||||||
|
if (a.isEmpty()) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return if (b.isEmpty()) {
|
||||||
|
a
|
||||||
|
} else "($a) AND ($b)"
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun appendSelectionArgs(originalValues: Array<String>?, newValues: Array<String>): Array<String> {
|
||||||
|
if (originalValues == null || originalValues.isEmpty()) {
|
||||||
|
return newValues
|
||||||
|
}
|
||||||
|
val result = Array(originalValues.size + newValues.size){ "it = $it" }
|
||||||
|
System.arraycopy(originalValues, 0, result, 0, originalValues.size)
|
||||||
|
System.arraycopy(newValues, 0, result, originalValues.size, newValues.size)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package sushi.hardcore.droidfs.widgets
|
package sushi.hardcore.droidfs.widgets
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
@ -20,9 +18,8 @@ object ThemeColor {
|
|||||||
for (i in 0 until preference.preferenceCount) {
|
for (i in 0 until preference.preferenceCount) {
|
||||||
tintPreferenceIcons(preference.getPreference(i), color)
|
tintPreferenceIcons(preference.getPreference(i), color)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (preference.icon != null) {
|
||||||
val icon: Drawable = preference.icon
|
DrawableCompat.setTint(preference.icon, color)
|
||||||
DrawableCompat.setTint(icon, color)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,7 @@ Java_sushi_hardcore_droidfs_util_GocryptfsVolume_native_1list_1dir(JNIEnv *env,
|
|||||||
jmethodID java_util_ArrayList_add = (*env)->GetMethodID(env, java_util_ArrayList, "add", "(Ljava/lang/Object;)Z");
|
jmethodID java_util_ArrayList_add = (*env)->GetMethodID(env, java_util_ArrayList, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
|
||||||
jclass classExplorerElement = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "sushi/hardcore/droidfs/explorers/ExplorerElement"));
|
jclass classExplorerElement = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "sushi/hardcore/droidfs/explorers/ExplorerElement"));
|
||||||
jmethodID classExplorerElement_init = (*env)->GetMethodID(env, classExplorerElement, "<init>", "(Ljava/lang/String;SJJ)V");
|
jmethodID classExplorerElement_init = (*env)->GetMethodID(env, classExplorerElement, "<init>", "(Ljava/lang/String;SJJLjava/lang/String;)V");
|
||||||
|
|
||||||
jobject element_list = (*env)->NewObject(env, java_util_ArrayList, java_util_ArrayList_init, elements.r2);
|
jobject element_list = (*env)->NewObject(env, java_util_ArrayList, java_util_ArrayList_init, elements.r2);
|
||||||
unsigned int c = 0;
|
unsigned int c = 0;
|
||||||
@ -254,7 +254,7 @@ Java_sushi_hardcore_droidfs_util_GocryptfsVolume_native_1list_1dir(JNIEnv *env,
|
|||||||
type = 1; //regular file
|
type = 1; //regular file
|
||||||
}
|
}
|
||||||
jstring jname = (*env)->NewStringUTF(env, name);
|
jstring jname = (*env)->NewStringUTF(env, name);
|
||||||
jobject explorerElement = (*env)->NewObject(env, classExplorerElement, classExplorerElement_init, jname, type, (long long)attrs.r0, attrs.r1);
|
jobject explorerElement = (*env)->NewObject(env, classExplorerElement, classExplorerElement_init, jname, type, (long long)attrs.r0, attrs.r1, jplain_dir);
|
||||||
(*env)->CallBooleanMethod(env, element_list, java_util_ArrayList_add, explorerElement);
|
(*env)->CallBooleanMethod(env, element_list, java_util_ArrayList_add, explorerElement);
|
||||||
c += name_len+1;
|
c += name_len+1;
|
||||||
}
|
}
|
||||||
|
5
app/src/main/res/drawable/icon_copy.xml
Normal file
5
app/src/main/res/drawable/icon_copy.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="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5l6,6v10c0,1.1 -0.9,2 -2,2L7.99,23C6.89,23 6,22.1 6,21l0.01,-14c0,-1.1 0.89,-2 1.99,-2h7zM14,12h5.5L14,6.5L14,12z"/>
|
||||||
|
</vector>
|
@ -1,12 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<vector android:height="24dp" android:viewportHeight="500"
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
android:width="24dp"
|
<path android:fillColor="#ffffff" android:fillType="evenOdd" 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"/>
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="1024"
|
|
||||||
android:viewportHeight="1024"
|
|
||||||
android:tint="#FFFFFF">
|
|
||||||
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:pathData="M749.5,417.3H715V283.8c0-54.4-21.1-105.3-59.4-143.6c-38.4-38.4-89.3-59.5-143.5-59.5 c-54.3,0-105.3,21.3-143.6,59.6c-38.3,38.4-59.5,88.9-59.5,143.9h60c0-79,64.2-143.4,143.1-143.4c38.2,0,74.1,14.7,101.1,41.7 c27,27,41.8,62.9,41.8,101.2v133.5H274.5c-41.1,0-74.5,33.4-74.5,74.5v376.5c0,41.3,33.4,74.9,74.5,74.9h475 c41.1,0,74.5-33.6,74.5-74.9V491.8C824,450.7,790.6,417.3,749.5,417.3z M532,742.7v55.5h-40v-55.5c-23-8.2-39.5-30.2-39.5-56 c0-32.8,26.7-59.5,59.5-59.5s59.5,26.7,59.5,59.5C571.5,712.5,555,734.5,532,742.7z" />
|
|
||||||
</vector>
|
</vector>
|
11
app/src/main/res/drawable/icon_encrypt.xml
Normal file
11
app/src/main/res/drawable/icon_encrypt.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="500"
|
||||||
|
android:viewportHeight="500">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M131.889,150.061v63.597h-27.256 c-20.079,0-36.343,16.263-36.343,36.342v181.711c0,20.078,16.264,36.34,36.343,36.34h290.734c20.078,0,36.345-16.262,36.345-36.34 V250c0-20.079-16.267-36.342-36.345-36.342h-27.254v-63.597c0-65.232-52.882-118.111-118.112-118.111 S131.889,84.828,131.889,150.061z M177.317,213.658v-63.597c0-40.157,32.525-72.685,72.683-72.685 c40.158,0,72.685,32.528,72.685,72.685v63.597H177.317z M213.658,313.599c0-20.078,16.263-36.341,36.342-36.341 s36.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.699 c0,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.699 C220.294,337.678,213.658,326.41,213.658,313.599z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/icon_github.xml
Normal file
10
app/src/main/res/drawable/icon_github.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff" android:pathData="M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.6,5,2.5,9.3,6.9,10.7v-2.3c0,0-0.4,0.1-0.9,0.1c-1.4,0-2-1.2-2.1-1.9 c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1c0.4,0,0.7-0.1,0.9-0.2 c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6C7,7.2,7,6.6,7.3,6c0,0,1.4,0,2.8,1.3 C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3C15.3,6,16.8,6,16.8,6C17,6.6,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4c0.7,0.8,1.2,1.8,1.2,3 c0,2.2-1.7,3.5-4,4c0.6,0.5,1,1.4,1,2.3v3.3c4.1-1.3,7-5.1,7-9.5C22,6.1,16.9,1.4,10.9,2.1z" />
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/icon_transfert.xml
Normal file
5
app/src/main/res/drawable/icon_transfert.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="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/>
|
||||||
|
</vector>
|
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 49 KiB |
@ -47,37 +47,39 @@
|
|||||||
app:menu_labels_style="@style/fab_label">
|
app:menu_labels_style="@style/fab_label">
|
||||||
|
|
||||||
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
||||||
|
android:id="@+id/fab_import_file_from_other_volume"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_size="mini"
|
app:fab_size="mini"
|
||||||
app:fab_label="@string/import_from_other_volume"
|
app:fab_label="@string/import_from_other_volume"
|
||||||
android:src="@drawable/icon_add"
|
android:src="@drawable/icon_transfert"
|
||||||
android:onClick="onClickAddFileFromOtherVolume"/>
|
android:onClick="onClickAddFileFromOtherVolume"/>
|
||||||
|
|
||||||
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
||||||
|
android:id="@+id/fab_import_file"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_size="mini"
|
app:fab_size="mini"
|
||||||
app:fab_label="@string/import_files"
|
app:fab_label="@string/import_files"
|
||||||
android:src="@drawable/icon_add"
|
android:src="@drawable/icon_encrypt"
|
||||||
android:onClick="onClickAddFile"/>
|
android:onClick="onClickAddFile"/>
|
||||||
|
|
||||||
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
||||||
android:id="@+id/fab_explorer_add_file"
|
android:id="@+id/fab_add_file"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_size="mini"
|
app:fab_size="mini"
|
||||||
app:fab_label="@string/new_file"
|
app:fab_label="@string/new_file"
|
||||||
android:src="@drawable/icon_add"
|
android:src="@drawable/icon_file_unknown"
|
||||||
android:onClick="onClickCreateFile"/>
|
android:onClick="onClickCreateFile"/>
|
||||||
|
|
||||||
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
||||||
android:id="@+id/fab_explorer_add_folder"
|
android:id="@+id/fab_add_folder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_size="mini"
|
app:fab_size="mini"
|
||||||
app:fab_label="@string/mkdir"
|
app:fab_label="@string/mkdir"
|
||||||
android:src="@drawable/icon_add"
|
android:src="@drawable/icon_folder"
|
||||||
android:onClick="onClickAddFolder"
|
android:onClick="onClickAddFolder"
|
||||||
tools:ignore="OnClick" />
|
tools:ignore="OnClick" />
|
||||||
|
|
||||||
|
@ -45,12 +45,12 @@
|
|||||||
app:menu_labels_style="@style/fab_label">
|
app:menu_labels_style="@style/fab_label">
|
||||||
|
|
||||||
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
<sushi.hardcore.droidfs.widgets.ColoredFAB
|
||||||
android:id="@+id/fab_explorer_add_folder"
|
android:id="@+id/fab_add_folder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_size="mini"
|
app:fab_size="mini"
|
||||||
app:fab_label="@string/mkdir"
|
app:fab_label="@string/mkdir"
|
||||||
android:src="@drawable/icon_add"
|
android:src="@drawable/icon_folder"
|
||||||
android:onClick="onClickAddFolder"/>
|
android:onClick="onClickAddFolder"/>
|
||||||
|
|
||||||
</sushi.hardcore.droidfs.widgets.ColoredFAM>
|
</sushi.hardcore.droidfs.widgets.ColoredFAM>
|
||||||
|
@ -3,51 +3,62 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_select_all"
|
android:id="@+id/select_all"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_select_all"/>
|
android:icon="@drawable/icon_select_all"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_delete"
|
android:id="@+id/copy"
|
||||||
|
app:showAsAction="always"
|
||||||
|
android:visible="false"
|
||||||
|
android:icon="@drawable/icon_copy"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/delete"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_delete" />
|
android:icon="@drawable/icon_delete" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_share"
|
android:id="@+id/decrypt"
|
||||||
app:showAsAction="always"
|
app:showAsAction="ifRoom"
|
||||||
android:visible="false"
|
|
||||||
android:icon="@drawable/icon_share"
|
|
||||||
app:iconTint="@color/menuIcon"/>
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/explorer_menu_decrypt"
|
|
||||||
app:showAsAction="always"
|
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_decrypt"
|
android:icon="@drawable/icon_decrypt"
|
||||||
app:iconTint="@color/menuIcon"/>
|
android:title="@string/decrypt"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_external_open"
|
android:id="@+id/share"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:visible="false"
|
||||||
|
android:icon="@drawable/icon_share"
|
||||||
|
android:title="@string/share"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/external_open"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:title="@string/external_open"/>
|
android:title="@string/external_open"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_rename"
|
android:id="@+id/rename"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:title="@string/rename"/>
|
android:title="@string/rename"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_sort"
|
android:id="@+id/validate"
|
||||||
app:showAsAction="always"
|
app:showAsAction="ifRoom"
|
||||||
android:icon="@drawable/icon_sort"
|
android:visible="false"
|
||||||
app:iconTint="@color/menuIcon"/>
|
android:icon="@drawable/icon_check"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_close"
|
android:id="@+id/sort"
|
||||||
|
app:showAsAction="always"
|
||||||
|
android:icon="@drawable/icon_sort"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/close"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:icon="@drawable/icon_close"/>
|
android:icon="@drawable/icon_close"/>
|
||||||
|
|
||||||
|
@ -3,30 +3,29 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_external_open"
|
android:id="@+id/external_open"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:title="@string/external_open"/>
|
android:title="@string/external_open"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_rename"
|
android:id="@+id/rename"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:title="@string/rename"/>
|
android:title="@string/rename"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_validate"
|
android:id="@+id/validate"
|
||||||
app:showAsAction="always"
|
app:showAsAction="ifRoom"
|
||||||
android:icon="@drawable/icon_check"/>
|
android:icon="@drawable/icon_check"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_sort"
|
android:id="@+id/sort"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:icon="@drawable/icon_sort"
|
android:icon="@drawable/icon_sort"/>
|
||||||
app:iconTint="@color/menuIcon"/>
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_close"
|
android:id="@+id/close"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:icon="@drawable/icon_close"/>
|
android:icon="@drawable/icon_close"/>
|
||||||
|
|
||||||
|
@ -3,31 +3,30 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_validate"
|
android:id="@+id/validate"
|
||||||
app:showAsAction="always"
|
app:showAsAction="ifRoom"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_check"/>
|
android:icon="@drawable/icon_check"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_select_all"
|
android:id="@+id/select_all"
|
||||||
app:showAsAction="always"
|
app:showAsAction="ifRoom"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:icon="@drawable/icon_select_all"/>
|
android:icon="@drawable/icon_select_all"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_rename"
|
android:id="@+id/rename"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:visible="false"
|
android:visible="false"
|
||||||
android:title="@string/rename"/>
|
android:title="@string/rename"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_sort"
|
android:id="@+id/sort"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:icon="@drawable/icon_sort"
|
android:icon="@drawable/icon_sort"/>
|
||||||
app:iconTint="@color/menuIcon"/>
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/explorer_menu_close"
|
android:id="@+id/close"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
android:icon="@drawable/icon_close"/>
|
android:icon="@drawable/icon_close"/>
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 9.5 KiB |
BIN
app/src/main/res/mipmap/icon_launcher.png
Normal file
BIN
app/src/main/res/mipmap/icon_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
@ -12,7 +12,6 @@
|
|||||||
<color name="textColor">#FFFFFF</color>
|
<color name="textColor">#FFFFFF</color>
|
||||||
<color name="buttonBackgroundColor">#5B5A5C</color>
|
<color name="buttonBackgroundColor">#5B5A5C</color>
|
||||||
<color name="item_selected">#66666666</color>
|
<color name="item_selected">#66666666</color>
|
||||||
<color name="menuIcon">#FFFFFF</color>
|
|
||||||
<color name="infoBarBackgroundColor">#111111</color>
|
<color name="infoBarBackgroundColor">#111111</color>
|
||||||
<color name="fingerprint_wait">#FFFFFF</color>
|
<color name="fingerprint_wait">#FFFFFF</color>
|
||||||
<color name="fingerprint_success">#6CC341</color>
|
<color name="fingerprint_success">#6CC341</color>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<string name="success_import_msg">The selected files have been successfully imported.</string>
|
<string name="success_import_msg">The selected files have been successfully imported.</string>
|
||||||
<string name="import_failed">Import of %1$s failed.</string>
|
<string name="import_failed">Import of %1$s failed.</string>
|
||||||
<string name="export_failed">Export of %1$s failed.</string>
|
<string name="export_failed">Export of %1$s failed.</string>
|
||||||
<string name="success_export">Export Successful !</string>
|
<string name="success_export">Export successful !</string>
|
||||||
<string name="success_export_msg">The selected files have been successfully exported.</string>
|
<string name="success_export_msg">The selected files have been successfully exported.</string>
|
||||||
<string name="remove_failed">Deletion of %1$s failed</string>
|
<string name="remove_failed">Deletion of %1$s failed</string>
|
||||||
<string name="passwords_mismatch">Passwords don\'t match</string>
|
<string name="passwords_mismatch">Passwords don\'t match</string>
|
||||||
@ -130,4 +130,13 @@
|
|||||||
<string name="loading_msg_wipe">Wiping original files…</string>
|
<string name="loading_msg_wipe">Wiping original files…</string>
|
||||||
<string name="loading_msg_export">Exporting files…</string>
|
<string name="loading_msg_export">Exporting files…</string>
|
||||||
<string name="query_cursor_null_error_msg">Unable to access this file</string>
|
<string name="query_cursor_null_error_msg">Unable to access this file</string>
|
||||||
|
<string name="about">About</string>
|
||||||
|
<string name="github">GitHub</string>
|
||||||
|
<string name="github_summary">Want to read the documentation, request feature, report bug, read the source code… Check the DroidFS\'s repository !</string>
|
||||||
|
<string name="share">Share</string>
|
||||||
|
<string name="decrypt">Decrypt</string>
|
||||||
|
<string name="loading_msg_copy">Copying selected items...</string>
|
||||||
|
<string name="copy_failed">Copy of %1$s failed.</string>
|
||||||
|
<string name="copy_success_msg">The selected items have been successfully copied.</string>
|
||||||
|
<string name="copy_success">Copy successful !</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -24,11 +24,24 @@
|
|||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/unsafe_features">
|
<PreferenceCategory android:title="@string/unsafe_features">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:title="@string/manage_unsafe_features"
|
android:title="@string/manage_unsafe_features"
|
||||||
android:summary="@string/manage_unsafe_features_summary"
|
android:summary="@string/manage_unsafe_features_summary"
|
||||||
android:icon="@drawable/icon_warning"
|
android:icon="@drawable/icon_warning"
|
||||||
android:fragment="sushi.hardcore.droidfs.SettingsActivity$UnsafeFeaturesSettingsFragment"/>
|
android:fragment="sushi.hardcore.droidfs.SettingsActivity$UnsafeFeaturesSettingsFragment"/>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/about">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:title="@string/github"
|
||||||
|
android:summary="@string/github_summary"
|
||||||
|
android:icon="@drawable/icon_github">
|
||||||
|
<intent android:action="android.intent.action.VIEW" android:data="https://github.com/hardcore-sushi/DroidFS"/>
|
||||||
|
</Preference>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user