Compare commits

..

No commits in common. "b84bb3c6a2473df4ded66154e4ae3f8ab73765ec" and "708c981fedfc6dfa4227290e0850e4db3937a41e" have entirely different histories.

48 changed files with 468 additions and 912 deletions

View File

@ -1,21 +1,18 @@
# Introduction # Introduction
DroidFS relies on modified versions of the original encrypted filesystems programs to open volumes. [CryFS](https://github.com/cryfs/cryfs) is written in C++ while [gocryptfs](https://github.com/rfjakob/gocryptfs) is written in [Go](https://golang.org). Thus, building DroidFS requires the compilation of native code. However, for the sake of simplicity, the application has been designed in a modular way: you can build a version of DroidFS that supports both Gocryptfs and CryFS, or only one of the two. DroidFS relies on modified versions of the original encrypted filesystems programs to open volumes. [CryFS](https://github.com/cryfs/cryfs) is written in C++ while [gocryptfs](https://github.com/rfjakob/gocryptfs) is written in [Go](https://golang.org). Thus, building DroidFS requires the compilation of native code. However, for the sake of simplicity, the application has been designed in a modular way: you can build a version of DroidFS that supports both Gocryptfs and CryFS, or only one of the two.
Moreover, DroidFS aims to be accessible to as many people as possible. If you encounter any problems or need help with the build, feel free to open an issue, a discussion, or contact me (currently the main developer) by [email](mailto:gh@arkensys.dedyn.io) or on [Matrix](https://matrix.org): @hardcoresushi:matrix.underworld.fr Moreover, DroidFS aims to be accessible to as many people as possible. If you encounter any problems or need help with the build, feel free to open an issue, a discussion, or contact me by [email](mailto:hardcore.sushi@disroot.org) or on [Matrix](https://matrix.org): @hardcoresushi:matrix.underworld.fr
# Setup # Setup
The following two steps assume you're using a Debian-based Linux distribution. Package names might be similar for other distributions. Don't hesitate to ask if you're having trouble with this.
Install required packages: Install required packages:
``` ```
$ sudo apt-get install openjdk-17-jdk-headless build-essential pkg-config git gnupg2 wget apksigner npm $ sudo apt-get install openjdk-11-jdk-headless build-essential pkg-config git gnupg2 wget apksigner
``` ```
You also need to manually install the [Android SDK](https://developer.android.com/studio/index.html#command-tools) and the [Android Native Development Kit (NDK)](https://github.com/android/ndk/wiki/Unsupported-Downloads#r25c) version `25.2.9519653` (r25c). libcryfs cannot be built with newer NDK versions at the moment due to compatibility issues with [boost](https://www.boost.org). If you succeed in building it with a more recent version of NDK, please report it. You also need to manually install the [Android SDK](https://developer.android.com/studio/index.html#command-tools) and the [Android Native Development Kit (NDK)](https://developer.android.com/ndk/downloads) (r23 versions are recommended).
If you want a support for Gocryptfs volumes, you need to install [Go](https://golang.org/doc/install): If you want a support for Gocryptfs volumes, you must install [Go](https://golang.org/doc/install) and libssl:
``` ```
$ sudo apt-get install golang-go $ sudo apt-get install golang-go libssl-dev
``` ```
The code should be authenticated before being built. To verify the signatures, you will need my PGP key: The code should be authenticated before being built. To verify the signatures, you will need my PGP key:
``` ```
@ -48,16 +45,16 @@ $ git clone --depth=1 https://git.ffmpeg.org/ffmpeg.git
If you want Gocryptfs support, you need to download OpenSSL: If you want Gocryptfs support, you need to download OpenSSL:
``` ```
$ cd ../libgocryptfs $ cd ../libgocryptfs
$ wget https://openssl.org/source/openssl-3.3.1.tar.gz $ wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz
``` ```
Verify OpenSSL signature: Verify OpenSSL signature:
``` ```
$ https://openssl.org/source/openssl-3.3.1.tar.gz.asc $ wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz.asc
$ gpg --verify openssl-3.3.1.tar.gz.asc openssl-3.3.1.tar.gz $ gpg --verify openssl-1.1.1w.tar.gz.asc openssl-1.1.1w.tar.gz
``` ```
Continue **ONLY** if the signature is **VALID**. Continue **ONLY** if the signature is **VALID**.
``` ```
$ tar -xzf openssl-3.3.1.tar.gz $ tar -xzf openssl-1.1.1w.tar.gz
``` ```
If you want CryFS support, initialize libcryfs: If you want CryFS support, initialize libcryfs:
``` ```
@ -65,6 +62,14 @@ $ cd app/libcryfs
$ git submodule update --depth=1 --init $ git submodule update --depth=1 --init
``` ```
To be able to open PDF files internally, [pdf.js](https://github.com/mozilla/pdf.js) must be downloaded:
```
$ mkdir libpdfviewer/app/pdfjs-dist && cd libpdfviewer/app/pdfjs-dist
$ wget https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.8.162.tgz
$ tar xf pdfjs-dist-3.8.162.tgz package/build/pdf.min.js package/build/pdf.worker.min.js
$ mv package/build . && rm pdfjs-dist-3.8.162.tgz
```
# Build # Build
Retrieve your Android NDK installation path, usually something like `/home/\<user\>/Android/SDK/ndk/\<NDK version\>`. Then, make it available in your shell: Retrieve your Android NDK installation path, usually something like `/home/\<user\>/Android/SDK/ndk/\<NDK version\>`. Then, make it available in your shell:
``` ```
@ -79,8 +84,8 @@ $ ./build.sh ffmpeg
This step is only required if you want Gocryptfs support. This step is only required if you want Gocryptfs support.
``` ```
$ cd app/libgocryptfs $ cd app/libgocryptfs
$ ANDROID_NDK_ROOT="$ANDROID_NDK_HOME" OPENSSL_PATH="./openssl-3.3.1" ./build.sh $ OPENSSL_PATH="./openssl-1.1.1w" ./build.sh
``` ```
## Compile APKs ## Compile APKs
Gradle build libgocryptfs and libcryfs by default. Gradle build libgocryptfs and libcryfs by default.

View File

@ -62,7 +62,7 @@ Some available features are considered risky and are therefore disabled by defau
</li> </li>
</ul> </ul>
\* These features can work in two ways: temporarily writing the plain file to disk (DroidFS internal storage) or sharing it via memory. By default, DroidFS will choose to keep the file only in memory as it's more secure, but will fallback to disk export if the file is too large to be held in memory. This behavior can be changed with the *"Export method"* parameter in the settings. Please note that some applications require the file to be stored on disk, and therefore do not work with memory-exported files. \* These features may require temporarily writing the plain file to disk (DroidFS internal storage). This file can be read by applications with root access or by physical access if your device is not encrypted. For files small enough and on a 3.17+ kernel, DroidFS will try to use memory-only storage using `memfd_create(2)` (can break some apps).
# Download # Download
<a href="https://f-droid.org/packages/sushi.hardcore.droidfs"> <a href="https://f-droid.org/packages/sushi.hardcore.droidfs">

View File

@ -21,7 +21,7 @@ if (hasProperty("nosplits")) {
android { android {
compileSdk 34 compileSdk 34
ndkVersion '25.2.9519653' ndkVersion "25.2.9519653"
namespace "sushi.hardcore.droidfs" namespace "sushi.hardcore.droidfs"
compileOptions { compileOptions {
@ -58,7 +58,6 @@ android {
splits { splits {
abi { abi {
enable true enable true
reset() // fix unknown bug (https://ru.stackoverflow.com/questions/1557805/abis-armeabi-mips-mips64-riscv64-are-not-supported-for-platform)
universalApk true universalApk true
} }
} }
@ -104,28 +103,28 @@ android {
dependencies { dependencies {
implementation project(":libpdfviewer:app") implementation project(":libpdfviewer:app")
implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.core:core-ktx:1.12.0'
implementation "androidx.appcompat:appcompat:1.7.0" implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.constraintlayout:constraintlayout:2.1.4"
def lifecycle_version = "2.8.1" def lifecycle_version = "2.6.2"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.sqlite:sqlite-ktx:2.4.0" implementation "androidx.sqlite:sqlite-ktx:2.3.1"
implementation "androidx.preference:preference-ktx:1.2.1" implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'com.google.android.material:material:1.12.0' implementation 'com.google.android.material:material:1.9.0'
implementation 'com.github.bumptech.glide:glide:4.16.0' implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05" implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
def media3_version = "1.3.1" def media3_version = "1.1.1"
implementation "androidx.media3:media3-exoplayer:$media3_version" implementation "androidx.media3:media3-exoplayer:$media3_version"
implementation "androidx.media3:media3-ui:$media3_version" implementation 'androidx.media3:media3-ui:1.1.1'
implementation "androidx.media3:media3-datasource:$media3_version" implementation "androidx.media3:media3-datasource:$media3_version"
implementation "androidx.concurrent:concurrent-futures:1.1.0" implementation "androidx.concurrent:concurrent-futures:1.1.0"
def camerax_version = "1.3.3" def camerax_version = "1.3.0-rc02"
implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:$camerax_version" implementation "androidx.camera:camera-view:$camerax_version"

View File

@ -74,7 +74,7 @@ else
--disable-appkit \ --disable-appkit \
--disable-alsa \ --disable-alsa \
--disable-debug \ --disable-debug \
&& >/dev/null &&
make -j $(nproc --all) >/dev/null) && make -j $(nproc --all) >/dev/null) &&
mkdir -p build/$1/libavformat build/$1/libavcodec build/$1/libavutil && mkdir -p build/$1/libavformat build/$1/libavcodec build/$1/libavutil &&
cp $FFMPEG_DIR/libavformat/*.h $FFMPEG_DIR/libavformat/libavformat.so build/$1/libavformat && cp $FFMPEG_DIR/libavformat/*.h $FFMPEG_DIR/libavformat/libavformat.so build/$1/libavformat &&

@ -1 +1 @@
Subproject commit 0398d48b0963c01092976c5c7012b02327e564f0 Subproject commit 6388eaf433a4196f10389921d5e346c90ee3d793

View File

@ -53,7 +53,6 @@
<activity android:name=".file_viewers.AudioPlayer" android:configChanges="screenSize|orientation" /> <activity android:name=".file_viewers.AudioPlayer" android:configChanges="screenSize|orientation" />
<activity android:name=".file_viewers.TextEditor" android:configChanges="screenSize|orientation" /> <activity android:name=".file_viewers.TextEditor" android:configChanges="screenSize|orientation" />
<activity android:name=".CameraActivity" android:screenOrientation="nosensor" /> <activity android:name=".CameraActivity" android:screenOrientation="nosensor" />
<activity android:name=".LogcatActivity"/>
<service android:name=".WiperService" android:exported="false" android:stopWithTask="false"/> <service android:name=".WiperService" android:exported="false" android:stopWithTask="false"/>
<service android:name=".file_operations.FileOperationService" android:exported="false"/> <service android:name=".file_operations.FileOperationService" android:exported="false"/>

View File

@ -5,7 +5,7 @@ Create the `new` folder if needed:
mkdir -p new mkdir -p new
``` ```
Put the new CameraX files from upstream (`androidx.camera.video.Recorder`, `androidx.camera.video.Recording`, `androidx.camera.video.PendingRecording` and `androidx.camera.video.internal.encoder.EncoderImpl`) in the `new` folder. Put new CameraX files from upstream in the `new` folder.
Perform the 3 way merge: Perform the 3 way merge:
``` ```

View File

@ -16,7 +16,7 @@ import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.filesystems.GocryptfsVolume import sushi.hardcore.droidfs.filesystems.GocryptfsVolume
import sushi.hardcore.droidfs.util.IntentUtils import sushi.hardcore.droidfs.util.IntentUtils
import sushi.hardcore.droidfs.util.ObjRef import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.UIUtils import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.util.* import java.util.*
@ -89,8 +89,8 @@ class ChangePasswordActivity: BaseActivity() {
} }
private fun changeVolumePassword() { private fun changeVolumePassword() {
val newPassword = UIUtils.encodeEditTextContent(binding.editNewPassword) val newPassword = WidgetUtil.encodeEditTextContent(binding.editNewPassword)
val newPasswordConfirm = UIUtils.encodeEditTextContent(binding.editPasswordConfirm) val newPasswordConfirm = WidgetUtil.encodeEditTextContent(binding.editPasswordConfirm)
@SuppressLint("NewApi") @SuppressLint("NewApi")
if (!newPassword.contentEquals(newPasswordConfirm)) { if (!newPassword.contentEquals(newPasswordConfirm)) {
Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
@ -135,7 +135,7 @@ class ChangePasswordActivity: BaseActivity() {
null null
} }
val currentPassword = if (givenHash == null) { val currentPassword = if (givenHash == null) {
UIUtils.encodeEditTextContent(binding.editCurrentPassword) WidgetUtil.encodeEditTextContent(binding.editCurrentPassword)
} else { } else {
null null
} }

View File

@ -7,7 +7,6 @@ import android.os.Handler
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.system.Os import android.system.Os
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.filesystems.EncryptedVolume import sushi.hardcore.droidfs.filesystems.EncryptedVolume
@ -23,23 +22,6 @@ class EncryptedFileProvider(context: Context) {
companion object { companion object {
private const val TAG = "EncryptedFileProvider" private const val TAG = "EncryptedFileProvider"
fun getTmpFilesDir(context: Context) = File(context.cacheDir, "tmp") fun getTmpFilesDir(context: Context) = File(context.cacheDir, "tmp")
var exportMethod = ExportMethod.AUTO
}
enum class ExportMethod {
AUTO,
DISK,
MEMORY;
companion object {
fun parse(value: String) = when (value) {
"auto" -> EncryptedFileProvider.ExportMethod.AUTO
"disk" -> EncryptedFileProvider.ExportMethod.DISK
"memory" -> EncryptedFileProvider.ExportMethod.MEMORY
else -> throw IllegalArgumentException("Invalid export method: $value")
}
}
} }
private val memoryInfo = ActivityManager.MemoryInfo() private val memoryInfo = ActivityManager.MemoryInfo()
@ -51,11 +33,6 @@ class EncryptedFileProvider(context: Context) {
(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo( (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(
memoryInfo memoryInfo
) )
PreferenceManager.getDefaultSharedPreferences(context)
.getString("export_method", null)?.let {
exportMethod = ExportMethod.parse(it)
}
} }
class ExportedDiskFile private constructor( class ExportedDiskFile private constructor(
@ -141,18 +118,16 @@ class EncryptedFileProvider(context: Context) {
path: String, path: String,
size: Long, size: Long,
): ExportedFile? { ): ExportedFile? {
val diskFile by lazy { ExportedDiskFile.create(path, tmpFilesDir, handler) } return if (size > memoryInfo.availMem * 0.8) {
val memFile by lazy { ExportedMemFile.create(path, size) } ExportedDiskFile.create(
return when (exportMethod) { path,
ExportMethod.MEMORY -> memFile tmpFilesDir,
ExportMethod.DISK -> diskFile handler,
ExportMethod.AUTO -> { )
if (isMemFileSupported && size < memoryInfo.availMem * 0.8) { } else if (isMemFileSupported) {
memFile ExportedMemFile.create(path, size) as ExportedFile
} else { } else {
diskFile null
}
}
} }
} }

View File

@ -6,7 +6,7 @@ object FileTypes {
private val FILE_EXTENSIONS = mapOf( private val FILE_EXTENSIONS = mapOf(
Pair("image", listOf("png", "jpg", "jpeg", "gif", "webp", "bmp", "heic")), Pair("image", listOf("png", "jpg", "jpeg", "gif", "webp", "bmp", "heic")),
Pair("video", listOf("mp4", "webm", "mkv", "mov")), Pair("video", listOf("mp4", "webm", "mkv", "mov")),
Pair("audio", listOf("mp3", "ogg", "m4a", "wav", "flac", "opus")), Pair("audio", listOf("mp3", "ogg", "m4a", "wav", "flac")),
Pair("pdf", listOf("pdf")), Pair("pdf", listOf("pdf")),
Pair("text", listOf( Pair("text", listOf(
"asc", "asc",

View File

@ -1,88 +0,0 @@
package sushi.hardcore.droidfs
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.databinding.ActivityLogcatBinding
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.InterruptedIOException
import java.io.OutputStreamWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LogcatActivity: BaseActivity() {
private lateinit var binding: ActivityLogcatBinding
private var process: Process? = null
private val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.getDefault())
}
private val saveAs = registerForActivityResult(ActivityResultContracts.CreateDocument("text/*")) { uri ->
uri?.let {
saveTo(it)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLogcatBinding.inflate(layoutInflater)
setContentView(binding.root)
title = getString(R.string.logcat_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
lifecycleScope.launch(Dispatchers.IO) {
try {
BufferedReader(InputStreamReader(Runtime.getRuntime().exec("logcat").also {
process = it
}.inputStream)).forEachLine {
binding.content.post {
binding.content.append("$it\n")
}
}
} catch (_: InterruptedIOException) {}
}
}
override fun onDestroy() {
super.onDestroy()
process?.destroy()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.logcat, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
finish()
true
}
R.id.save -> {
saveAs.launch("DroidFS_${dateFormat.format(Date())}.log")
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun saveTo(uri: Uri) {
lifecycleScope.launch(Dispatchers.IO) {
BufferedWriter(OutputStreamWriter(contentResolver.openOutputStream(uri))).use {
it.write(binding.content.text.toString())
}
launch(Dispatchers.Main) {
Toast.makeText(this@LogcatActivity, R.string.logcat_saved, Toast.LENGTH_SHORT).show()
}
}
}
}

View File

@ -28,7 +28,6 @@ import sushi.hardcore.droidfs.file_operations.FileOperationService
import sushi.hardcore.droidfs.file_operations.TaskResult import sushi.hardcore.droidfs.file_operations.TaskResult
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.util.UIUtils
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import sushi.hardcore.droidfs.widgets.EditTextDialog import sushi.hardcore.droidfs.widgets.EditTextDialog
import java.io.File import java.io.File
@ -355,11 +354,7 @@ class MainActivity : BaseActivity(), VolumeAdapter.Listener {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_activity, menu) menuInflater.inflate(R.menu.main_activity, menu)
val settingsVisible = !explorerRouter.pickMode && !explorerRouter.dropMode menu.findItem(R.id.settings).isVisible = !explorerRouter.pickMode && !explorerRouter.dropMode
menu.findItem(R.id.settings).isVisible = settingsVisible
if (settingsVisible) {
UIUtils.getMenuIconNeutralTint(this, menu).applyTo(R.id.settings, R.drawable.icon_settings)
}
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 { menu.findItem(R.id.lock).isVisible = isSelecting && volumeAdapter.selectedItems.any {

View File

@ -1,6 +1,5 @@
package sushi.hardcore.droidfs package sushi.hardcore.droidfs
import android.app.ActivityOptions
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
@ -91,15 +90,9 @@ class SettingsActivity : BaseActivity() {
private fun refreshTheme() { private fun refreshTheme() {
with(requireActivity()) { with(requireActivity()) {
startActivity( startActivity(Intent(this, SettingsActivity::class.java))
Intent(this, SettingsActivity::class.java),
ActivityOptions.makeCustomAnimation(
this,
android.R.anim.fade_in,
android.R.anim.fade_out
).toBundle()
)
finish() finish()
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
} }
} }
@ -127,10 +120,6 @@ class SettingsActivity : BaseActivity() {
false false
} }
} }
findPreference<Preference>("logcat")?.setOnPreferenceClickListener { _ ->
startActivity(Intent(requireContext(), LogcatActivity::class.java))
true
}
} }
} }
@ -190,7 +179,17 @@ class SettingsActivity : BaseActivity() {
true true
} }
switchExpose.setOnPreferenceChangeListener { _, checked -> switchExpose.setOnPreferenceChangeListener { _, checked ->
VolumeProvider.usfExpose = checked as Boolean if (checked as Boolean) {
if (!Compat.isMemFileSupported()) {
CustomAlertDialogBuilder(requireContext(), (requireActivity() as BaseActivity).theme)
.setTitle(R.string.error)
.setMessage("Your current kernel does not support memfd_create(). This feature requires a minimum kernel version of ${Compat.MEMFD_CREATE_MINIMUM_KERNEL_VERSION}.")
.setPositiveButton(R.string.ok, null)
.show()
return@setOnPreferenceChangeListener false
}
}
VolumeProvider.usfExpose = checked
updateView(usfExpose = checked) updateView(usfExpose = checked)
VolumeProvider.notifyRootsChanged(requireContext()) VolumeProvider.notifyRootsChanged(requireContext())
true true
@ -200,19 +199,6 @@ class SettingsActivity : BaseActivity() {
TemporaryFileProvider.usfSafWrite = checked TemporaryFileProvider.usfSafWrite = checked
true true
} }
findPreference<ListPreference>("export_method")!!.setOnPreferenceChangeListener { _, newValue ->
if (newValue as String == "memory" && !Compat.isMemFileSupported()) {
CustomAlertDialogBuilder(requireContext(), (requireActivity() as BaseActivity).theme)
.setTitle(R.string.error)
.setMessage(getString(R.string.memfd_create_unsupported, Compat.MEMFD_CREATE_MINIMUM_KERNEL_VERSION))
.setPositiveButton(R.string.ok, null)
.show()
return@setOnPreferenceChangeListener false
}
EncryptedFileProvider.exportMethod = EncryptedFileProvider.ExportMethod.parse(newValue)
true
}
} }
} }
} }

View File

@ -13,7 +13,7 @@ import sushi.hardcore.droidfs.Constants.DEFAULT_VOLUME_KEY
import sushi.hardcore.droidfs.databinding.DialogOpenVolumeBinding import sushi.hardcore.droidfs.databinding.DialogOpenVolumeBinding
import sushi.hardcore.droidfs.filesystems.EncryptedVolume import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.util.ObjRef import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.UIUtils import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.util.* import java.util.*
@ -123,7 +123,7 @@ class VolumeOpener(
apply() apply()
} }
} }
val password = UIUtils.encodeEditTextContent(dialogBinding!!.editPassword) val password = WidgetUtil.encodeEditTextContent(dialogBinding!!.editPassword)
val savePasswordHash = dialogBinding!!.checkboxSavePassword.isChecked val savePasswordHash = dialogBinding!!.checkboxSavePassword.isChecked
dialogBinding = null dialogBinding = null
// openVolumeWithPassword is responsible for wiping the password // openVolumeWithPassword is responsible for wiping the password

View File

@ -20,7 +20,7 @@ import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.filesystems.GocryptfsVolume import sushi.hardcore.droidfs.filesystems.GocryptfsVolume
import sushi.hardcore.droidfs.util.Compat import sushi.hardcore.droidfs.util.Compat
import sushi.hardcore.droidfs.util.ObjRef import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.UIUtils import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -146,8 +146,8 @@ class CreateVolumeFragment: Fragment() {
} }
private fun createVolume() { private fun createVolume() {
val password = UIUtils.encodeEditTextContent(binding.editPassword) val password = WidgetUtil.encodeEditTextContent(binding.editPassword)
val passwordConfirm = UIUtils.encodeEditTextContent(binding.editPasswordConfirm) val passwordConfirm = WidgetUtil.encodeEditTextContent(binding.editPasswordConfirm)
if (!password.contentEquals(passwordConfirm)) { if (!password.contentEquals(passwordConfirm)) {
Toast.makeText(requireContext(), R.string.passwords_mismatch, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
Arrays.fill(password, 0) Arrays.fill(password, 0)

View File

@ -236,21 +236,14 @@ class VolumeProvider: DocumentsProvider() {
): String? { ): String? {
if (!usfExpose || !usfSafWrite) return null if (!usfExpose || !usfSafWrite) return null
val document = parseDocumentId(parentDocumentId) ?: return null val document = parseDocumentId(parentDocumentId) ?: return null
val path = PathUtils.pathJoin(document.path, displayName) val newFile = PathUtils.pathJoin(document.path, displayName)
var success = false val f = document.encryptedVolume.openFileWriteMode(newFile)
if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) { return if (f == -1L) {
success = document.encryptedVolume.mkdir(path) Log.e(TAG, "Failed to create file: $document")
} else {
val f = document.encryptedVolume.openFileWriteMode(path)
if (f != -1L) {
document.encryptedVolume.closeFile(f)
success = true
}
}
return if (success) {
document.rootId+path
} else {
null null
} else {
document.encryptedVolume.closeFile(f)
document.rootId+newFile
} }
} }

View File

@ -11,12 +11,10 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -25,13 +23,10 @@ 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.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.yield import kotlinx.coroutines.withContext
import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.BaseActivity
import sushi.hardcore.droidfs.Constants import sushi.hardcore.droidfs.Constants
import sushi.hardcore.droidfs.EncryptedFileProvider import sushi.hardcore.droidfs.EncryptedFileProvider
@ -54,7 +49,6 @@ import sushi.hardcore.droidfs.file_viewers.VideoPlayer
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.PathUtils import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.UIUtils
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import sushi.hardcore.droidfs.widgets.EditTextDialog import sushi.hardcore.droidfs.widgets.EditTextDialog
@ -75,7 +69,6 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
protected lateinit var fileOperationService: FileOperationService protected lateinit var fileOperationService: FileOperationService
protected val activityScope = MainScope() protected val activityScope = MainScope()
private var directoryLoadingTask: Job? = null
protected lateinit var explorerElements: MutableList<ExplorerElement> protected lateinit var explorerElements: MutableList<ExplorerElement>
protected lateinit var explorerAdapter: ExplorerElementAdapter protected lateinit var explorerAdapter: ExplorerElementAdapter
protected lateinit var app: VolumeManagerApp protected lateinit var app: VolumeManagerApp
@ -86,7 +79,6 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
private lateinit var titleText: TextView private lateinit var titleText: TextView
private lateinit var recycler_view_explorer: RecyclerView private lateinit var recycler_view_explorer: RecyclerView
private lateinit var refresher: SwipeRefreshLayout private lateinit var refresher: SwipeRefreshLayout
private lateinit var loader: ProgressBar
private lateinit var textDirEmpty: TextView private lateinit var textDirEmpty: TextView
private lateinit var currentPathText: TextView private lateinit var currentPathText: TextView
private lateinit var numberOfFilesText: TextView private lateinit var numberOfFilesText: TextView
@ -109,7 +101,6 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
init() init()
recycler_view_explorer = findViewById(R.id.recycler_view_explorer) recycler_view_explorer = findViewById(R.id.recycler_view_explorer)
refresher = findViewById(R.id.refresher) refresher = findViewById(R.id.refresher)
loader = findViewById(R.id.loader)
textDirEmpty = findViewById(R.id.text_dir_empty) textDirEmpty = findViewById(R.id.text_dir_empty)
currentPathText = findViewById(R.id.current_path_text) currentPathText = findViewById(R.id.current_path_text)
numberOfFilesText = findViewById(R.id.number_of_files_text) numberOfFilesText = findViewById(R.id.number_of_files_text)
@ -268,27 +259,6 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
.show() .show()
} }
protected fun createNewFile(callback: (Long) -> Unit) {
EditTextDialog(this, R.string.enter_file_name) {
if (it.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
createNewFile(callback)
} else {
val filePath = PathUtils.pathJoin(currentDirectoryPath, it)
val handleID = encryptedVolume.openFileWriteMode(filePath)
if (handleID == -1L) {
CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.error)
.setMessage(R.string.file_creation_failed)
.setPositiveButton(R.string.ok, null)
.show()
} else {
callback(handleID)
}
}
}.show()
}
private fun setVolumeNameTitle() { private fun setVolumeNameTitle() {
titleText.text = getString(R.string.volume, volumeName) titleText.text = getString(R.string.volume, volumeName)
} }
@ -342,15 +312,17 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
private fun displayExplorerElements() { private fun displayExplorerElements() {
synchronized(this) {
ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements) ExplorerElement.sortBy(sortOrderValues[currentSortOrderIndex], foldersFirst, explorerElements)
}
unselectAll(false) unselectAll(false)
loader.isVisible = false
recycler_view_explorer.isVisible = true
explorerAdapter.explorerElements = explorerElements explorerAdapter.explorerElements = explorerElements
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(Constants.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
sharedPrefsEditor.apply()
} }
private suspend fun recursiveSetSize(directory: ExplorerElement) { private fun recursiveSetSize(directory: ExplorerElement) {
yield()
for (child in encryptedVolume.readDir(directory.fullPath) ?: return) { for (child in encryptedVolume.readDir(directory.fullPath) ?: return) {
if (child.isDirectory) { if (child.isDirectory) {
recursiveSetSize(child) recursiveSetSize(child)
@ -374,25 +346,26 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
} }
protected fun setCurrentPath(path: String, onDisplayed: (() -> Unit)? = null) = lifecycleScope.launch { protected fun setCurrentPath(path: String, onDisplayed: (() -> Unit)? = null) {
directoryLoadingTask?.cancelAndJoin() synchronized(this) {
recycler_view_explorer.isVisible = false explorerElements = encryptedVolume.readDir(path) ?: return
loader.isVisible = true
explorerElements = encryptedVolume.readDir(path) ?: return@launch
if (path != "/") { if (path != "/") {
explorerElements.add( explorerElements.add(
0, 0,
ExplorerElement("..", Stat.parentFolderStat(), parentPath = currentDirectoryPath) ExplorerElement("..", Stat.parentFolderStat(), parentPath = currentDirectoryPath)
) )
} }
}
textDirEmpty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.GONE textDirEmpty.visibility = if (explorerElements.size == 0) View.VISIBLE else View.GONE
currentDirectoryPath = path currentDirectoryPath = path
currentPathText.text = getString(R.string.location, currentDirectoryPath) currentPathText.text = getString(R.string.location, currentDirectoryPath)
displayNumberOfElements(numberOfFilesText, R.string.one_file, R.string.multiple_files, explorerElements.count { it.isRegularFile }) displayNumberOfElements(numberOfFilesText, R.string.one_file, R.string.multiple_files, explorerElements.count { it.isRegularFile })
displayNumberOfElements(numberOfFoldersText, R.string.one_folder, R.string.multiple_folders, explorerElements.count { it.isDirectory }) displayNumberOfElements(numberOfFoldersText, R.string.one_folder, R.string.multiple_folders, explorerElements.count { it.isDirectory })
if (mapFolders) { if (mapFolders) {
lifecycleScope.launch {
var totalSize: Long = 0 var totalSize: Long = 0
directoryLoadingTask = launch(Dispatchers.IO) { withContext(Dispatchers.IO) {
synchronized(this@BaseExplorerActivity) {
for (element in explorerElements) { for (element in explorerElements) {
if (element.isDirectory) { if (element.isDirectory) {
recursiveSetSize(element) recursiveSetSize(element)
@ -400,10 +373,11 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
totalSize += element.stat.size totalSize += element.stat.size
} }
} }
directoryLoadingTask!!.join() }
displayExplorerElements() displayExplorerElements()
totalSizeText.text = getString(R.string.total_size, PathUtils.formatSize(totalSize)) totalSizeText.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
onDisplayed?.invoke() onDisplayed?.invoke()
}
} else { } else {
displayExplorerElements() displayExplorerElements()
totalSizeText.text = getString( totalSizeText.text = getString(
@ -586,6 +560,14 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
} }
} }
private fun setMenuIconTint(menu: Menu, iconColor: Int, menuItemId: Int, drawableId: Int) {
menu.findItem(menuItemId)?.let {
it.icon = ContextCompat.getDrawable(this, drawableId)?.apply {
setTint(iconColor)
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menu.findItem(R.id.rename).isVisible = false menu.findItem(R.id.rename).isVisible = false
menu.findItem(R.id.open_as)?.isVisible = false menu.findItem(R.id.open_as)?.isVisible = false
@ -593,10 +575,9 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
menu.findItem(R.id.external_open)?.isVisible = false menu.findItem(R.id.external_open)?.isVisible = false
} }
val noItemSelected = explorerAdapter.selectedItems.isEmpty() val noItemSelected = explorerAdapter.selectedItems.isEmpty()
with(UIUtils.getMenuIconNeutralTint(this, menu)) { val iconColor = ContextCompat.getColor(this, R.color.neutralIconTint)
applyTo(R.id.sort, R.drawable.icon_sort) setMenuIconTint(menu, iconColor, R.id.sort, R.drawable.icon_sort)
applyTo(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.lock).isVisible = noItemSelected
menu.findItem(R.id.close).isVisible = noItemSelected menu.findItem(R.id.close).isVisible = noItemSelected
@ -626,13 +607,7 @@ open class BaseExplorerActivity : BaseActivity(), ExplorerElementAdapter.Listene
.setTitle(R.string.sort_order) .setTitle(R.string.sort_order)
.setSingleChoiceItems(sortOrderEntries, currentSortOrderIndex) { dialog, which -> .setSingleChoiceItems(sortOrderEntries, currentSortOrderIndex) { dialog, which ->
currentSortOrderIndex = which currentSortOrderIndex = which
// displayExplorerElements must not be called if directoryLoadingTask is active
if (directoryLoadingTask?.isActive != true) {
displayExplorerElements() displayExplorerElements()
}
val sharedPrefsEditor = sharedPrefs.edit()
sharedPrefsEditor.putString(Constants.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex])
sharedPrefsEditor.apply()
dialog.dismiss() dialog.dismiss()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)

View File

@ -68,11 +68,7 @@ class ExplorerActivity : BaseExplorerActivity() {
private val pickFiles = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris -> private val pickFiles = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
if (uris != null) { if (uris != null) {
for (uri in uris) { for (uri in uris) {
try {
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
} catch (e: SecurityException) {
e.printStackTrace()
}
} }
importFilesFromUris(uris) { importFilesFromUris(uris) {
onImportComplete(uris) onImportComplete(uris)
@ -193,11 +189,9 @@ class ExplorerActivity : BaseExplorerActivity() {
pickImportDirectory.launch(null) pickImportDirectory.launch(null)
} }
"createFile" -> { "createFile" -> {
createNewFile { EditTextDialog(this, R.string.enter_file_name) {
encryptedVolume.closeFile(it) createNewFile(it)
setCurrentPath(currentDirectoryPath) }.show()
invalidateOptionsMenu()
}
} }
"createFolder" -> { "createFolder" -> {
openDialogCreateFolder() openDialogCreateFolder()
@ -225,6 +219,26 @@ class ExplorerActivity : BaseExplorerActivity() {
cancelItemAction() cancelItemAction()
} }
private fun createNewFile(fileName: String){
if (fileName.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else {
val filePath = PathUtils.pathJoin(currentDirectoryPath, fileName)
val handleID = encryptedVolume.openFileWriteMode(filePath)
if (handleID == -1L) {
CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.error)
.setMessage(R.string.file_creation_failed)
.setPositiveButton(R.string.ok, null)
.show()
} else {
encryptedVolume.closeFile(handleID)
setCurrentPath(currentDirectoryPath)
invalidateOptionsMenu()
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu) menuInflater.inflate(R.menu.explorer, menu)
val result = super.onCreateOptionsMenu(menu) val result = super.onCreateOptionsMenu(menu)

View File

@ -9,8 +9,6 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.IntentUtils import sushi.hardcore.droidfs.util.IntentUtils
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.nio.CharBuffer
import java.nio.charset.StandardCharsets
class ExplorerActivityDrop : BaseExplorerActivity() { class ExplorerActivityDrop : BaseExplorerActivity() {
@ -32,15 +30,15 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
return when (item.itemId) { return when (item.itemId) {
R.id.validate -> { R.id.validate -> {
val extras = intent.extras val extras = intent.extras
val success = if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)) { val errorMsg: String? = if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)) {
when (intent.action) { when (intent.action) {
Intent.ACTION_SEND -> { Intent.ACTION_SEND -> {
val uri = IntentUtils.getParcelableExtra<Uri>(intent, Intent.EXTRA_STREAM) val uri = IntentUtils.getParcelableExtra<Uri>(intent, Intent.EXTRA_STREAM)
if (uri == null) { if (uri == null) {
false getString(R.string.share_intent_parsing_failed)
} else { } else {
importFilesFromUris(listOf(uri), ::onImported) importFilesFromUris(listOf(uri), ::onImported)
true null
} }
} }
Intent.ACTION_SEND_MULTIPLE -> { Intent.ACTION_SEND_MULTIPLE -> {
@ -52,34 +50,20 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
} }
if (uris != null) { if (uris != null) {
importFilesFromUris(uris, ::onImported) importFilesFromUris(uris, ::onImported)
true null
} else { } else {
false getString(R.string.share_intent_parsing_failed)
} }
} }
else -> false else -> getString(R.string.share_intent_parsing_failed)
} }
} else if ((intent.clipData?.itemCount ?: 0) > 0) {
val byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(intent.clipData!!.getItemAt(0).text))
val byteArray = ByteArray(byteBuffer.remaining())
byteBuffer.get(byteArray)
val size = byteArray.size.toLong()
createNewFile {
var offset = 0L
while (offset < size) {
offset += encryptedVolume.write(it, offset, byteArray, offset, size-offset)
}
encryptedVolume.closeFile(it)
onImported()
}
true
} else { } else {
false getString(R.string.share_intent_parsing_failed)
} }
if (!success) { errorMsg?.let {
CustomAlertDialogBuilder(this, theme) CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.share_intent_parsing_failed) .setMessage(it)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }

View File

@ -3,7 +3,6 @@ package sushi.hardcore.droidfs.util
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.storage.StorageManager import android.os.storage.StorageManager
import android.provider.DocumentsContract import android.provider.DocumentsContract
@ -112,8 +111,6 @@ object PathUtils {
} }
} }
Log.d(PATH_RESOLVER_TAG, "getExternalFilesDirs failed") Log.d(PATH_RESOLVER_TAG, "getExternalFilesDirs failed")
// Don't risk to be killed by SELinux on newer Android versions
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
try { try {
val process = ProcessBuilder("mount").redirectErrorStream(true).start().apply { waitFor() } val process = ProcessBuilder("mount").redirectErrorStream(true).start().apply { waitFor() }
process.inputStream.readBytes().decodeToString().split("\n").forEach { line -> process.inputStream.readBytes().decodeToString().split("\n").forEach { line ->
@ -132,7 +129,6 @@ object PathUtils {
e.printStackTrace() e.printStackTrace()
} }
Log.d(PATH_RESOLVER_TAG, "mount processing failed") Log.d(PATH_RESOLVER_TAG, "mount processing failed")
}
return null return null
} }

View File

@ -1,42 +0,0 @@
package sushi.hardcore.droidfs.util
import android.content.Context
import android.view.Menu
import android.widget.EditText
import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R
import java.nio.CharBuffer
import java.nio.charset.StandardCharsets
import java.util.*
object UIUtils {
fun encodeEditTextContent(editText: EditText): ByteArray {
val charArray = CharArray(editText.text.length)
editText.text.getChars(0, editText.text.length, charArray, 0)
val byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(charArray))
Arrays.fill(charArray, Char.MIN_VALUE)
val byteArray = ByteArray(byteBuffer.remaining())
byteBuffer.get(byteArray)
Wiper.wipe(byteBuffer)
return byteArray
}
class MenuIconColor(
private val context: Context,
private val menu: Menu,
private val color: Int
) {
fun applyTo(menuItemId: Int, drawableId: Int) {
menu.findItem(menuItemId)?.let {
it.icon = ContextCompat.getDrawable(context, drawableId)?.apply {
setTint(color)
}
}
}
}
fun getMenuIconNeutralTint(context: Context, menu: Menu) = MenuIconColor(
context, menu,
ContextCompat.getColor(context, R.color.neutralIconTint),
)
}

View File

@ -0,0 +1,19 @@
package sushi.hardcore.droidfs.util
import android.widget.EditText
import java.nio.CharBuffer
import java.nio.charset.StandardCharsets
import java.util.*
object WidgetUtil {
fun encodeEditTextContent(editText: EditText): ByteArray {
val charArray = CharArray(editText.text.length)
editText.text.getChars(0, editText.text.length, charArray, 0)
val byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(charArray))
Arrays.fill(charArray, Char.MIN_VALUE)
val byteArray = ByteArray(byteBuffer.remaining())
byteBuffer.get(byteArray)
Wiper.wipe(byteBuffer)
return byteArray
}
}

View File

@ -2,13 +2,23 @@ package sushi.hardcore.droidfs.widgets
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.media.session.PlaybackState import android.media.session.PlaybackState
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.AttributeSet import android.util.AttributeSet
import android.view.GestureDetector import android.view.GestureDetector
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import sushi.hardcore.droidfs.R
class DoubleTapPlayerView @JvmOverloads constructor( class DoubleTapPlayerView @JvmOverloads constructor(
context: Context, context: Context,
@ -65,7 +75,22 @@ class DoubleTapPlayerView @JvmOverloads constructor(
handler.postDelayed(stopDoubleTap, 700) handler.postDelayed(stopDoubleTap, 700)
} }
} }
private val gestureDetector = GestureDetector(context, gestureListener) private val gestureDetector = GestureDetectorCompat(context, gestureListener)
private val density by lazy {
context.resources.displayMetrics.density
}
private val originalExoIconPaddingBottom by lazy {
resources.getDimension(R.dimen.exo_icon_padding_bottom)
}
private val originalExoIconSize by lazy {
resources.getDimension(R.dimen.exo_icon_size)
}
init {
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
handleOrientationChange(Configuration.ORIENTATION_LANDSCAPE)
}
}
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean { override fun onTouchEvent(event: MotionEvent): Boolean {
@ -110,4 +135,35 @@ class DoubleTapPlayerView @JvmOverloads constructor(
} }
} }
} }
private fun updateButtonSize(orientation: Int) {
val size = (if (orientation == Configuration.ORIENTATION_LANDSCAPE) 45*density else originalExoIconSize).toInt()
listOf(R.id.exo_prev, R.id.exo_rew_with_amount, R.id.exo_play_pause, R.id.exo_ffwd_with_amount, R.id.exo_next).forEach {
findViewById<View>(it).updateLayoutParams {
width = size
height = size
}
}
// fix text vertical alignment inside icons
val paddingBottom = (if (orientation == Configuration.ORIENTATION_LANDSCAPE) 15*density else originalExoIconPaddingBottom).toInt()
listOf(R.id.exo_rew_with_amount, R.id.exo_ffwd_with_amount).forEach {
findViewById<Button>(it).updatePadding(bottom = paddingBottom)
}
}
private fun handleOrientationChange(orientation: Int) {
val centerControls = findViewById<LinearLayout>(R.id.exo_center_controls)
(centerControls.parent as ViewGroup).removeView(centerControls)
findViewById<FrameLayout>(if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
R.id.center_controls_bar
} else {
R.id.center_controls_external
}).addView(centerControls)
updateButtonSize(orientation)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
handleOrientationChange(newConfig.orientation)
}
} }

View File

@ -40,7 +40,7 @@ struct Muxer {
jmethodID seek_method_id; jmethodID seek_method_id;
}; };
int write_packet(void* opaque, const uint8_t* buff, int buff_size) { int write_packet(void* opaque, uint8_t* buff, int buff_size) {
struct Muxer* muxer = opaque; struct Muxer* muxer = opaque;
JNIEnv *env; JNIEnv *env;
(*muxer->jvm)->GetEnv(muxer->jvm, (void **) &env, JNI_VERSION_1_6); (*muxer->jvm)->GetEnv(muxer->jvm, (void **) &env, JNI_VERSION_1_6);
@ -108,8 +108,8 @@ Java_sushi_hardcore_droidfs_video_1recording_FFmpegMuxer_addVideoTrack(JNIEnv *e
stream->codecpar->height = height; stream->codecpar->height = height;
stream->codecpar->format = AV_PIX_FMT_YUVJ420P; stream->codecpar->format = AV_PIX_FMT_YUVJ420P;
stream->time_base = (AVRational) {1, frame_rate}; stream->time_base = (AVRational) {1, frame_rate};
AVPacketSideData *side_data_packet = av_packet_side_data_new(&stream->codecpar->coded_side_data, &stream->codecpar->nb_coded_side_data, AV_PKT_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9, 0); uint8_t* matrix = av_stream_new_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9);
av_display_rotation_set((int32_t *) side_data_packet->data, orientation_hint); av_display_rotation_set((int32_t *) matrix, orientation_hint);
return stream->index; return stream->index;
} }

View File

@ -1,5 +0,0 @@
<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="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4L5,12v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM15,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
</vector>

View File

@ -1,5 +1,5 @@
<vector android:height="24dp" <vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24" android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?attr/colorAccent" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/> <path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
</vector> </vector>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:typeface="monospace" />
</HorizontalScrollView>
</ScrollView>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@id/exo_center_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:gravity="center"
android:padding="@dimen/exo_styled_controls_padding"
android:clipToPadding="false"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_prev"
style="@style/ExoStyledControls.Button.Center.Previous"/>
<include layout="@layout/exo_player_control_rewind_button" />
<ImageButton android:id="@id/exo_play_pause"
style="@style/ExoStyledControls.Button.Center.PlayPause"/>
<include layout="@layout/exo_player_control_ffwd_button" />
<ImageButton android:id="@id/exo_next"
style="@style/ExoStyledControls.Button.Center.Next"/>
</LinearLayout>
</merge>

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 0dp dimensions are used to prevent this view from influencing the size of
the parent view if it uses "wrap_content". It is expanded to occupy the
entirety of the parent in code, after the parent's size has been
determined. See: https://github.com/google/ExoPlayer/issues/8726.
-->
<View android:id="@id/exo_controls_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/exo_black_opacity_60"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<FrameLayout
android:id="@+id/center_controls_external"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal">
<include layout="@layout/exo_center_controls"/>
</FrameLayout>
<FrameLayout android:id="@id/exo_bottom_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_bottom_bar_height"
android:background="@color/exo_bottom_bar_background"
android:layoutDirection="ltr">
<LinearLayout android:id="@id/exo_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding"
android:layout_gravity="center_vertical|start"
android:layoutDirection="ltr">
<TextView android:id="@id/exo_position"
style="@style/ExoStyledControls.TimeText.Position"/>
<TextView
style="@style/ExoStyledControls.TimeText.Separator"/>
<TextView android:id="@id/exo_duration"
style="@style/ExoStyledControls.TimeText.Duration"/>
</LinearLayout>
<FrameLayout
android:id="@+id/center_controls_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<LinearLayout android:id="@id/exo_basic_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_vr"
style="@style/ExoStyledControls.Button.Bottom.VR"/>
<ImageButton android:id="@id/exo_shuffle"
style="@style/ExoStyledControls.Button.Bottom.Shuffle"/>
<ImageButton android:id="@id/exo_repeat_toggle"
style="@style/ExoStyledControls.Button.Bottom.RepeatToggle"/>
<ImageButton android:id="@id/exo_subtitle"
style="@style/ExoStyledControls.Button.Bottom.CC"/>
<ImageButton android:id="@id/exo_settings"
style="@style/ExoStyledControls.Button.Bottom.Settings"/>
<ImageButton android:id="@id/exo_fullscreen"
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
<ImageButton android:id="@id/exo_overflow_show"
style="@style/ExoStyledControls.Button.Bottom.OverflowShow"/>
</LinearLayout>
<HorizontalScrollView android:id="@id/exo_extra_controls_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:visibility="invisible">
<LinearLayout android:id="@id/exo_extra_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_overflow_hide"
style="@style/ExoStyledControls.Button.Bottom.OverflowHide"/>
</LinearLayout>
</HorizontalScrollView>
</FrameLayout>
</LinearLayout>
<View android:id="@id/exo_progress_placeholder"
android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_progress_layout_height"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/exo_styled_progress_margin_bottom"/>
<LinearLayout android:id="@id/exo_minimal_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/exo_styled_minimal_controls_margin_bottom"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_minimal_fullscreen"
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
</LinearLayout>
</merge>

View File

@ -10,12 +10,6 @@
android:text="@string/dir_empty" android:text="@string/dir_empty"
android:visibility="gone"/> android:visibility="gone"/>
<ProgressBar
android:id="@+id/loader"
android:layout_centerInParent="true"
android:layout_width="40dp"
android:layout_height="40dp"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresher" android:id="@+id/refresher"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/save"
app:showAsAction="ifRoom"
android:icon="@drawable/icon_save"
android:title="@string/save" />
</menu>

View File

@ -252,20 +252,4 @@
<string name="black_theme">لون أسود داكن</string> <string name="black_theme">لون أسود داكن</string>
<string name="password_fallback">العودة إلى كلمة المرور</string> <string name="password_fallback">العودة إلى كلمة المرور</string>
<string name="password_fallback_summary">طلب كلمة المرور في حال فشل المصادقة ببصمة الأصبع</string> <string name="password_fallback_summary">طلب كلمة المرور في حال فشل المصادقة ببصمة الأصبع</string>
<string name="unknown_error_code">خطأ غير معروف: %d</string>
<string name="config_load_error">لا يمكن تحميل ملف الإعدادات. تأكد من صحّة مسار المجلد الآمن.</string>
<string name="wrong_password">لقد تعذر فك تشفر ملف الإعدادات. رجاءً قم بالتحقق من كلمة المرور.</string>
<string name="filesystem_id_changed">لقد إختلف معرف نظام الملفات الموجود في ملف الإعدادات عن آخر مرة فتحنا فيها هذا المجلد. قد يعني هذا أن أحد ما قد قام باستبدال ملفاتك بملفات أخرى بهدف إختراقك.</string>
<string name="inaccessible_base_dir">إن المجلد المشفر غير موجود أو لا يمكن الوصول إليه.</string>
<string name="task_failed">لقد فشلت عملية: %s</string>
<string name="usf_expose">كشف المجلدات المفتوحة</string>
<string name="usf_expose_summary">السماح للتطبيقات الأخرى بالوصول إلى المجلدات المشفرة عن طريق نظام "توفير المستندات" الخاص بنظام الأندرويد</string>
<string name="usf_saf_write">منح صلاحيات الكتابة</string>
<string name="usf_saf_write_summary">منح صلاحيات الكتابة عند فتح الملفات مع التطبيقات الأخرى</string>
<string name="saf">"إطار الوصول للقرص"</string>
<string name="tmp_export_failed">لقد فشل تصدير: %s</string>
<string name="export_failed_create">تعذر إنشاء الملف المستخرج</string>
<string name="export_failed_export">فشل إستخراج الملف</string>
<string name="export_mem">يتم الإستخراج إلى الذاكرة المؤقتة…</string>
<string name="export_disk">يتم الإستخراج إلى القرص…</string>
</resources> </resources>

View File

@ -242,7 +242,6 @@
<string name="file_op_delete_msg">Eliminando archivos…</string> <string name="file_op_delete_msg">Eliminando archivos…</string>
<string name="volume_type">(%s)</string> <string name="volume_type">(%s)</string>
<string name="volume_type_read_only">(%s, Sólo lectura)</string> <string name="volume_type_read_only">(%s, Sólo lectura)</string>
<string name="volume_type_inaccessible">(%s, inaccesible)</string>
<string name="io_error">I/O Error.</string> <string name="io_error">I/O Error.</string>
<string name="use_fingerprint">Utilizar huella dactilar en lugar de la contraseña actual</string> <string name="use_fingerprint">Utilizar huella dactilar en lugar de la contraseña actual</string>
<string name="remember_volume">Recordar volumen</string> <string name="remember_volume">Recordar volumen</string>
@ -273,10 +272,4 @@
<string name="export_failed_export">Error al exportar el archivo</string> <string name="export_failed_export">Error al exportar el archivo</string>
<string name="export_mem">Exportando a memoria…</string> <string name="export_mem">Exportando a memoria…</string>
<string name="export_disk">Exportar a disco…</string> <string name="export_disk">Exportar a disco…</string>
<string name="memfd_create_unsupported">El kernel actual no soporta memfd_create(). Esta característica requiere una versión mínima del kernel de %s.</string>
<string name="export_method">Método de exportación</string>
<string name="export_method_summary">Método de exportación de archivos. Se utiliza para compartir, abrir externamente y acceder a archivos expuestos.</string>
<string name="debug">Depurar</string>
<string name="logcat_title">Logcat de DroidFS</string>
<string name="logcat_saved">Logcat guardado</string>
</resources> </resources>

View File

@ -17,10 +17,4 @@
<item>Фиолетовый</item> <item>Фиолетовый</item>
<item>Розовый</item> <item>Розовый</item>
</string-array> </string-array>
<string-array name="export_methods">
<item>Автовыбор (зависит от доступной памяти)</item>
<item>Временный файл в хранилище (надёжно, но могут остаться следы)</item>
<item>Файл в памяти (безопаснее, но не всегда возможно)</item>
</string-array>
</resources> </resources>

View File

@ -262,10 +262,4 @@
<string name="export_failed_export">невозможно экспортировать файл</string> <string name="export_failed_export">невозможно экспортировать файл</string>
<string name="export_mem">Экспорт в память…</string> <string name="export_mem">Экспорт в память…</string>
<string name="export_disk">Экспорт в хранилище…</string> <string name="export_disk">Экспорт в хранилище…</string>
<string name="memfd_create_unsupported">Текущее ядро не поддерживает memfd_create(). Для работы данной функции требуется версия ядра не ниже %s.</string>
<string name="export_method">Метод экспорта</string>
<string name="export_method_summary">Метод экспорта файлов. Используется для обмена, открытия во внешнем приложении и доступа к открытым файлам.</string>
<string name="debug">Отладка</string>
<string name="logcat_title">Журнал logcat DroidFS</string>
<string name="logcat_saved">Журнал logcat сохранён</string>
</resources> </resources>

View File

@ -1,72 +0,0 @@
<resources>
<string-array name="gocryptfs_encryption_ciphers">
<item>AES-GCM</item>
<item>XChaCha20-Poly1305</item>
<item>@string/auto</item>
</string-array>
<string-array name="cryfs_encryption_ciphers">
<item>xchacha20-poly1305</item>
<item>aes-256-gcm</item>
<item>aes-128-gcm</item>
<item>twofish-256-gcm</item>
<item>twofish-128-gcm</item>
<item>serpent-256-gcm</item>
<item>serpent-128-gcm</item>
<item>cast-256-gcm</item>
<item>mars-448-gcm</item>
<item>mars-256-gcm</item>
<item>mars-128-gcm</item>
</string-array>
<string-array name="sort_orders_entries">
<item>Ad</item>
<item>Boyut</item>
<item>Tarih</item>
<item>Ad (azalan)</item>
<item>Boyut (azalan)</item>
<item>Tarih (azalan)</item>
</string-array>
<string-array name="color_names">
<item>Yeşil</item>
<item>Kırmızı</item>
<item>Mavi</item>
<item>Sarı</item>
<item>Turuncu</item>
<item>Mor</item>
<item>Pembe</item>
</string-array>
<string-array name="export_methods">
<item>Otomatik (kullanılabilir belleğe bağlı olarak)</item>
<item>Diske geçici dışa aktarma (güvenilir ancak iz bırakabilir)</item>
<item>Bellek dosyası (daha güvenlidir ancak her zaman işe yaramaz)</item>
</string-array>
<!-- don't translate the following otherwise the app will crash -->
<string-array name="sort_orders_values">
<item>name</item>
<item>size</item>
<item>date</item>
<item>name_desc</item>
<item>size_desc</item>
<item>date_desc</item>
</string-array>
<string-array name="color_values">
<item>green</item>
<item>red</item>
<item>blue</item>
<item>yellow</item>
<item>orange</item>
<item>purple</item>
<item>pink</item>
</string-array>
<string-array name="export_methods_values">
<item>auto</item>
<item>disk</item>
<item>memory</item>
</string-array>
</resources>

View File

@ -1,282 +0,0 @@
<resources>
<string name="app_name">DroidFS</string>
<string name="create_volume">Birim oluştur</string>
<string name="open"></string>
<string name="create">Oluştur</string>
<string name="change_password">Şifreyi değiştir</string>
<string name="password">Şifre</string>
<string name="import_files">Dosyaları içe aktar/Şifrele</string>
<string name="import_folder">Klasörü İçe Aktar/Şifrele</string>
<string name="discovering_files">Dosyalar keşfediliyor…</string>
<string name="mkdir">Klasör oluştur</string>
<string name="dir_empty">Klasör boş</string>
<string name="warning">Dikkat !</string>
<string name="ask_lock_volume">Bu birimi kilitlemek istediğinizden emin misiniz?</string>
<string name="ok">Tamam</string>
<string name="cancel">İptal</string>
<string name="enter_folder_name">Klasör adı:</string>
<string name="error">Hata</string>
<string name="error_filename_empty">Lütfen bir isim girin</string>
<string name="error_mkdir">Klasör oluşturulamadı.</string>
<string name="success_import">Başarılı bir şekilde içe aktarıldı !</string>
<string name="success_import_msg">Seçili dosyalar başarılı bir şekilde içe aktarıldı.</string>
<string name="import_failed">İçe aktarılamadı: %s</string>
<string name="export_failed">Dışa aktarılamadı: %s</string>
<string name="success_export">Başarılı bir şekilde dışa aktarıldı !</string>
<string name="remove_failed">Silinemedi: %s</string>
<string name="passwords_mismatch">Şifreler uyuşmuyor</string>
<string name="dir_not_empty">Seçili klasör boş değil</string>
<string name="create_volume_failed">Birim oluşturualamadı.</string>
<string name="open_volume_failed">ılamadı</string>
<string name="share_chooser">Dosyayı paylaş</string>
<string name="storage_perm_denied">Depolama izni reddedildi</string>
<string name="storage_perm_denied_msg">DroidFS, depolama izinleri olmadan çalışamaz.</string>
<string name="get_size_failed">Dosya boyutu alınamadı.</string>
<string name="parent_folder">Ana klasör</string>
<string name="enter_volume_path">Lütfen birim yolunu girin</string>
<string name="enter_volume_name">Lütfen birim adını girin</string>
<string name="external_open">Harici uygulamayla aç</string>
<string name="single_delete_confirm">Silmek istediğimizden emin misiniz: %s</string>
<string name="multiple_delete_confirm">Bunları silmek istediğinizden emin misiniz: %s</string>
<string name="location">Konum: %s</string>
<string name="total_size">Toplam boyut: %s</string>
<string name="import_from_other_volume">Başka bir birimden içe aktar</string>
<string name="read_file_failed">Bu dosya açılamadı.</string>
<string name="volume">Birim: %s</string>
<string name="yes">Evet</string>
<string name="no">Hayır</string>
<string name="ask_for_wipe">Orijinal dosyaları silmek istiyor musunuz ?</string>
<string name="wipe_failed">Silinemedi: %s</string>
<string name="wipe_successful">Dosyalar başarılı bir şekilde silindi !</string>
<string name="rename">Yeniden adlandır</string>
<string name="rename_title">Yeni ad:</string>
<string name="rename_failed">Yeniden adlandırılamadı: %s</string>
<string name="sort_order">Sıralama biçimi:</string>
<string name="change_password_failed">İşlem başarısız oldu. Lütfen eski şifrenizi kontrol edin.</string>
<string name="share_menu_label">DroidFS ile şifrele</string>
<string name="share_intent_parsing_failed">Paylaşım isteği işlenemedi.</string>
<string name="listdir_null_error_msg">Bu dizine erişilemiyor</string>
<string name="fingerprint_save_checkbox_text">Parmak izini kullanarak şifre hash değerini kaydedin</string>
<string name="fingerprint_instruction">Lütfen parmak izi sensörüne dokunun</string>
<string name="illegal_block_size_exception">IllegalBlockSizeException</string>
<string name="illegal_block_size_exception_msg">Yeni bir parmak izi eklediyseniz bu durum meydana gelebilir. Hash depolamasının sıfırlanması bu sorunu çözebilir.</string>
<string name="reset_hash_storage">Hash depolamasını sıfırla</string>
<string name="MAC_verification_failed">İmza/MAC doğrulaması başarısız oldu. Android KeyStore veya kaydedilen hash değeri değiştirildi. Hash depolamasını sıfırlamak bu sorunu çözebilir.</string>
<string name="hash_storage_reset">Hash depolaması başarıyla sıfırlandı</string>
<string name="encrypt_action_description">Şifre hash değerini şifreleme ve kaydetme.</string>
<string name="decrypt_action_description">Şifre hash değeri şifreleniyor.</string>
<string name="title_activity_settings">DroidFS ayarları</string>
<string name="explorer">Tarayıcı</string>
<string name="settings_title_sort_order">Varsayılan sıralama düzeni</string>
<string name="usf_decrypt">Dosyaların dışa aktarılmasına/şifresinin çözülmesine izin ver</string>
<string name="usf_share">Android paylaşım menüsü aracılığıyla dosya paylaşımına izin ver</string>
<string name="usf_open">Dosyaların diğer uygulamalarla açılmasına izin ver</string>
<string name="usf_screenshot">Ekran görüntüsü almaya izin ver</string>
<string name="usf_fingerprint">Parmak izi kullanılarak şifre hash değerinin kaydedilmesine izin ver</string>
<string name="usf_volume_management">Birim yönetimi</string>
<string name="usf_keep_open">Uygulama arka plana geçtiğinde birimi açık tutun</string>
<string name="unsafe_features">Güvenli olmayan özellikler</string>
<string name="manage_unsafe_features">Güvenli olmayan özellikleri yönetin</string>
<string name="manage_unsafe_features_summary">Güvenli olmayan özellikleri etkinleştirme/devre dışı bırakma</string>
<string name="usf_home_warning_msg">DroidFS mümkün olduğunca güvenli olmaya çalışır. Ancak güvenlik çoğu zaman konfor eksikliğini de beraberinde getirir. Bu nedenle DroidFS, ihtiyaçlarınıza göre etkinleştirebileceğiniz/devre dışı bırakabileceğiniz güvenli olmayan ek özellikler sunar.\n\nDikkat: bu özellikler GÜVENLİ OLMAYABİLİR. Ne yaptığınızı tam olarak bilmiyorsanız bunları kullanmayın. Bunları etkinleştirmeden önce belgeleri okumanız önemle tavsiye edilir.</string>
<string name="see_unsafe_features">Güvenli olmayan özellikleri görün</string>
<string name="open_as">Farklı</string>
<string name="image">Resim</string>
<string name="video">Video</string>
<string name="audio">Ses</string>
<string name="playing_failed">Bu dosya oynatılamadı: %s</string>
<string name="text">Metin</string>
<string name="save_failed">Kaydedilemedi</string>
<string name="file_saved">Dosya kaydedildi !</string>
<string name="ask_save">Dosya kaydedilmemiş değişiklikler içeriyor. Çıkmadan önce bunları kaydetmek istiyor musunuz ?</string>
<string name="save">Kaydet</string>
<string name="discard">Gözardı et</string>
<string name="word_wrap">Kelime kaydırma</string>
<string name="outofmemoryerror_msg">OutOfMemoryError: Bu dosya belleğe yüklenemeyecek kadar büyük.</string>
<string name="new_file">Yeni dosya oluştur</string>
<string name="enter_file_name">Dosya adı:</string>
<string name="file_creation_failed">Dosya oluşturulamadı.</string>
<string name="loading">Yükleniyor…</string>
<string name="loading_msg_create">Birim oluşturuluyor…</string>
<string name="loading_msg_change_password">Şifre değiştiriliyor…</string>
<string name="loading_msg_open">Birim açılıyor…</string>
<string name="loading_msg_export">Dosyalar dışa aktarılıyor…</string>
<string name="query_cursor_null_error_msg">Bu dosyaya erişilemiyor</string>
<string name="about">Hakkında</string>
<string name="github">GitHub</string>
<string name="github_summary">DroidFS Github deposu. Kaynak kodu, dokümantasyon, hata izleyici…</string>
<string name="gitea">Gitea</string>
<string name="gitea_summary">Chapril Gitea bulut sunucusundaki DroidFS deposu GitHub\'tan farklı olarak Gitea tamamen ücretsiz bir yazılımdır ve kendi kendine barındırılır. Kaynak kodu, belgeler, hata izleyici…</string>
<string name="share">Paylaş</string>
<string name="decrypt_files">Dışa aktar/Şifre çöz</string>
<string name="copy_failed">Kopyalanamadı: %s</string>
<string name="copy_success">Başarıyla kopyalandı !</string>
<string name="add">Ekle</string>
<string name="camera">Kamera</string>
<string name="picture_save_success">Resim şuraya kaydedildi: %s</string>
<string name="picture_save_failed">Bu resim kaydedilemedi.</string>
<string name="video_save_success">Resim şuraya kaydedildi: %s</string>
<string name="file_overwrite_question">%s zaten mevcut, üzerine yazmak istiyor musunuz ?</string>
<string name="dir_overwrite_question">%s zaten mevcut, içeriğini birleştirmek istiyor musunuz ?</string>
<string name="enter_new_name">Yeni ad girin</string>
<string name="copy_menu_title">Kopyala</string>
<string name="move_failed">Taşınamadı: %s</string>
<string name="move_success">Başarıyla taşındı !</string>
<string name="enter_timer_duration">Zamanlayıcı süresini girin (saniye olarak)</string>
<string name="path_error">Seçilen yol alınamadı.</string>
<string name="create_cant_write_error_msg">DroidFS\'nin bu yola yazma erişimi yok. Lütfen başka bir konum deneyin.</string>
<string name="add_cant_write_warning">DroidFS\'nin bu yola yazma erişimi yok. Salt okunur erişimle birim ekleniyor.</string>
<string name="sdcard_error_header">DroidFS yalnızca aşağıdaki durumlarda çıkarılabilir SD kartlara yazabilir:</string>
<string name="sdcard_error_add_footer">Salt okunur erişimle birim ekleme.</string>
<string name="sdcard_error_create_footer">Lütfen bu yolun bir alt dizinini veya dahili depolamayı kullanın.</string>
<string name="slideshow_stopped">Slayt gösterisi durduruldu</string>
<string name="slideshow_started">Slayt gösterisi başlatıldı</string>
<string name="ask_save_img_rotated">Resim döndürüldü. Bu değişiklikleri kaydedip orijinal resmin üzerine yazmak istiyor musunuz ?</string>
<string name="image_saved_successfully">Resim değişiklikleri başarıyla kaydedildi.</string>
<string name="bitmap_compress_failed">Bitmap sıkıştırılamadı.</string>
<string name="file_write_failed">Dosya yazılamadı.</string>
<string name="error_not_a_volume">Şifrelenmiş birim tanınmadı. Lütfen seçilen yolu kontrol edin.</string>
<string name="version">Versiyon</string>
<string name="error_cipher_null">Hata şifre boş</string>
<string name="key_permanently_invalidated_exception">KeyPermanentlyInvalidatedException</string>
<string name="key_permanently_invalidated_exception_msg">Yeni bir parmak izi eklemişsiniz gibi görünüyor. Kaydedilen şifrelerin hash değeri kullanılamaz hale geldi.</string>
<string name="usf_read_doc">Bu seçeneklerden herhangi birini etkinleştirmeden önce dikkatlice okumalısınız.</string>
<string name="usf_doc">Güvenli olmayan özellikler dokümantasyonu</string>
<string name="error_retrieving_filename">Şu URI için dosya adı alınamıyor: %s</string>
<string name="hidden_volume">Gizli birim</string>
<string name="error_slash_in_name">Birim adı eğik çizgi sembolü içeremez</string>
<string name="hidden_volume_warning">Gizli birimler uygulamanın dahili deposunda saklanır. Diğer uygulamalar bu birimleri root erişimi olmadan göremez. Ancak DroidFS\'yi kaldırırsanız veya uygulamanın verilerini temizlerseniz tüm gizli birimleriniz KAYBOLACAKTIR. Yedekleme yaptığınızdan emin olun !</string>
<string name="camera_perm_needed">Fotoğraf çekebilmek için kamera izni gerekiyor.</string>
<string name="choose_resolution">Bir çözünürlük seçin</string>
<string name="file_operations">Dosya işlemleri</string>
<string name="file_op_copy_msg">Dosyalar kopyalanıyor…</string>
<string name="file_op_import_msg">Dosyalar içe aktarılıyor...</string>
<string name="file_op_export_msg">Dosyalar dışa aktarılıyor...</string>
<string name="file_op_move_msg">Dosyalar taşınıyor…</string>
<string name="file_op_wiping_msg">Dosyalar siliniyor…</string>
<string name="folders_first">Önce klasörler</string>
<string name="folders_first_summary">Klasörleri listenin başında göster</string>
<string name="auto_fit_title">Video oynatıcı ekranı otomatik döndürme</string>
<string name="auto_fit_summary">Video boyutlarına uyacak şekilde ekranı otomatik olarak döndürün</string>
<string name="open_tree_failed">Dosya tarayıcısı bulunamadı. Lütfen bir tane yükleyin ve tekrar deneyin.</string>
<string name="close_volume">Birimi kapat</string>
<string name="sort_by">Sırala</string>
<string name="cut">Kes</string>
<string name="map_folders">Klasörleri eşleme</string>
<string name="map_folders_summary">Boyutlarını hesaplamak için klasörleri yinelemeli olarak eşleyin (büyük birimleri açarken bunu devre dışı bırakmalısınız)</string>
<string name="camera_optimization">Kamera optimizasyonu</string>
<string name="maximize_quality">Kaliteyi maksimuma çıkarın</string>
<string name="minimize_latency">Gecikmeyi minimuma indirin</string>
<string name="auto">Otomatik</string>
<string name="encryption_cipher_label">Şifreleme şifresi:</string>
<string name="theme">Tema</string>
<string name="thumbnails">Küçük resimler</string>
<string name="thumbnails_summary">Resimlerin ve videoların küçük resimlerini göster</string>
<string name="seek_seconds_forward">+%d saniye</string>
<string name="seek_seconds_backward">-%d saniye</string>
<string name="add_volume">Birim ekle</string>
<string name="pick_directory">Klasör seç</string>
<string name="volume_alread_saved">Birim zaten kayıtlı</string>
<string name="open_dialog_title">ılıyor %s:</string>
<string name="remove">Kaldır</string>
<string name="settings">Ayarlar</string>
<string name="select_all">Tümünü seç</string>
<string name="remove_fingerprint">Parmak izini kaldır</string>
<string name="unrecoverable_key_exception_msg">%s. Şifreleme anahtarı yüklenemedi.</string>
<string name="unrecoverable_key_exception">UnrecoverableKeyException</string>
<string name="delete_hidden_volume_question">%s gizli, sadece birimin yolunu unutmak mı yoksa tüm İÇERİĞİNİ SİLMEK mi istiyorsunuz?</string>
<string name="forget_only">Sadece unut</string>
<string name="delete_volume">Birimi sil</string>
<string name="hidden_volume_description">Birimi DroidFS dahili deposunda saklayın</string>
<string name="error_is_file">Hata: dosya zaten mevcut</string>
<string name="volume_path_label">Birimin yolunu seçin:</string>
<string name="volume_name_label">Birimin adını girin:</string>
<string name="volume_path_hint">Birim yolu</string>
<string name="volume_name_hint">Birim adı</string>
<string name="password_label">Birim şifresini girin:</string>
<string name="password_confirmation_label">Şifreyi tekrarlayın:</string>
<string name="password_confirmation_hint">Şifre (doğrulama)</string>
<string name="password_hash_saved">şifre hash değeri kaydedildi</string>
<string name="no_volumes_text">Kaydedilmiş birim yok, + düğmesini tıklayarak biraz ekleyin</string>
<string name="fingerprint_error_msg">Parmak izi kimlik doğrulaması kullanılamaz: %s.</string>
<string name="keyguard_not_secure">tuş kilidi güvenli değil</string>
<string name="no_hardware">uygun donanım bulunamadı</string>
<string name="hardware_unavailable">donanım mevcut değil</string>
<string name="no_fingerprint">kayıtlı parmak izi yok</string>
<string name="unknown_error">bilinmeyen hata</string>
<string name="biometric_error">Biyometri hatası: %s</string>
<string name="apply_to_all">Bu seçimi tüm gizli birimlere uygula</string>
<string name="select_volume">Birimi seç</string>
<string name="current_password_label">Mevcut birim parolasını girin:</string>
<string name="current_password_hint">Mevcut şifre</string>
<string name="new_password_label">Yeni birim şifresini girin:</string>
<string name="new_password_hint">Yeni şifre</string>
<string name="new_password_confirmation_label">Yeni şifreyi tekrarlayın:</string>
<string name="error_marshmallow_required">Bu özellik yalnızca Android 6.0 (Marshmallow) veya üzeri sürümlerde mevcuttur.</string>
<string name="copy_hidden_volume">Paylaşılan depolamaya kopyala</string>
<string name="copy_external_volume">Gizli bir kopya oluştur</string>
<string name="copy_volume_notification">Birim kopyalanıyor…</string>
<string name="hidden_volume_already_exists">Aynı ada sahip bir gizli birim zaten mevcut.</string>
<string name="pdf_document">PDF dökümanı</string>
<string name="thumbnail_max_size">Küçük resimler için maksimum boyut</string>
<string name="thumbnail_max_size_summary">Küçük resmin yüklenebileceği maksimum dosya boyutu. Mevcut değer: %s</string>
<string name="size_hint">Boyut (KB olarak)</string>
<string name="invalid_number">Geçersiz numara</string>
<string name="new_volume_name">Yeni birim adı:</string>
<string name="volume_rename_failed">Birim yeniden adlandırılamadı</string>
<string name="switch_display_layout">Ekran düzenini değiştir</string>
<string name="one_file">1 dosya</string>
<string name="multiple_files">%d dosya</string>
<string name="one_folder">1 klasör</string>
<string name="multiple_folders">%d klasör</string>
<string name="default_open">Uygulamayı başlattığınızda bu birimi açın</string>
<string name="remove_default_open">Varsayılan olarak açma</string>
<string name="elements_selected">%d/%d seçildi</string>
<string name="pin_passwords_title">Sayısal tuş takımı düzeni</string>
<string name="pin_passwords_summary">Birim parolalarını girerken sayısal tuş takımı düzeni kullanın</string>
<string name="volume_type_label">Birim türü:</string>
<string name="gocryptfs">Gocryptfs</string>
<string name="cryfs">CryFS</string>
<string name="gocryptfs_disabled">Gocryptfs desteği devre dışı bırakıldı</string>
<string name="cryfs_disabled">CryFS desteği devre dışı bırakıldı</string>
<string name="file_op_delete_msg">Dosyalar silindi…</string>
<string name="volume_type">(%s)</string>
<string name="volume_type_read_only">(%s, salt-okunur)</string>
<string name="volume_type_inaccessible">(%s, erişilemez)</string>
<string name="io_error">I/O hatası.</string>
<string name="use_fingerprint">Mevcut şifre yerine parmak izini kullan</string>
<string name="remember_volume">Birimi hatırla</string>
<string name="open_volume">Birimi aç</string>
<string name="choose_existing_volume">Lütfen mevcut bir birimi seçin</string>
<string name="volume_unlocked">Birimin kilidi açıldı</string>
<string name="lock_volume">Birimi kilitle</string>
<string name="lock">Kilitle</string>
<string name="ux">UX</string>
<string name="theme_color">Tema rengi</string>
<string name="theme_color_summary">Uygulama teması rengini değiştirme</string>
<string name="black_theme">Siyah tema</string>
<string name="password_fallback">Şifreye geri dönme</string>
<string name="password_fallback_summary">Parmak iziyle kimlik doğrulama iptal edildiğinde şifre sor</string>
<string name="unknown_error_code">Bilinmeyen hata kodu: %d</string>
<string name="config_load_error">Yapılandırma dosyası yüklenemiyor. Birimin erişilebilir olduğundan emin olun.</string>
<string name="wrong_password">Yapılandırma dosyasının şifresi çözülemiyor. Lütfen şifrenizi kontrol edin.</string>
<string name="filesystem_id_changed">Yapılandırma dosyasındaki dosya sistemi kimliği, bu birimi son açtığımız zamandan farklı. Bu, saldırganın dosya sistemini farklı bir sistemle değiştirdiği anlamına gelebilir.</string>
<string name="inaccessible_base_dir">Birim mevcut değil veya erişilemiyor.</string>
<string name="task_failed">Görev başarısız oldu: %s</string>
<string name="usf_expose">ık birimleri ortaya çıkarın</string>
<string name="usf_expose_summary">Diğer uygulamaların belge sağlayıcıları olarak açık birimlere göz atmasına izin ver</string>
<string name="usf_saf_write">Yazma erişimi ver</string>
<string name="usf_saf_write_summary">Dosyaları diğer uygulamalarla açarken yazma erişimi verin</string>
<string name="saf">Depolama Erişim Sistemi</string>
<string name="tmp_export_failed">Dışa aktarma başarısız oldu: %s</string>
<string name="export_failed_create">dışa aktarılan dosya oluşturulamıyor</string>
<string name="export_failed_export">dosya dışa aktarılamadı</string>
<string name="export_mem">Belleğe aktarılıyor…</string>
<string name="export_disk">Diske dışarı aktarılıyor…</string>
<string name="memfd_create_unsupported">Mevcut çekirdeğiniz memfd_create() özelliğini desteklemiyor. Bu özellik minimum %s çekirdek sürümünü gerektirir.</string>
<string name="export_method">Dışa aktarma yöntemi</string>
<string name="export_method_summary">Dosya dışa aktarma yöntemi. Açıkta kalan dosyaları paylaşmak, harici olarak açmak ve bunlara erişmek için kullanılır.</string>
<string name="debug">Debug</string>
<string name="logcat_title">DroidFS Logcat</string>
<string name="logcat_saved">Logcat kaydedildi</string>
</resources>

View File

@ -1,20 +0,0 @@
<resources>
<string-array name="sort_orders_entries">
<item>名称</item>
<item>大小</item>
<item>日期</item>
<item>名称 (降序)</item>
<item>大小 (降序)</item>
<item>日期 (降序)</item>
</string-array>
<string-array name="color_names">
<item>绿</item>
<item></item>
<item></item>
<item></item>
<item></item>
<item></item>
<item></item>
</string-array>
</resources>

View File

@ -6,9 +6,9 @@
<string name="create">创建</string> <string name="create">创建</string>
<string name="change_password">修改密码</string> <string name="change_password">修改密码</string>
<string name="password">密码</string> <string name="password">密码</string>
<string name="import_files">导入文件</string> <string name="import_files">导入/加密 文件</string>
<string name="import_folder">导入文件夹</string> <string name="import_folder">导入/加密 文件夹</string>
<string name="discovering_files">正在清点文件…</string> <string name="discovering_files">发现文件…</string>
<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>
@ -31,11 +31,11 @@
<string name="open_volume_failed">打开失败</string> <string name="open_volume_failed">打开失败</string>
<string name="share_chooser">分享文件</string> <string name="share_chooser">分享文件</string>
<string name="storage_perm_denied">存储空间权限被拒绝</string> <string name="storage_perm_denied">存储空间权限被拒绝</string>
<string name="storage_perm_denied_msg">没有存储权限时DroidFS无法工作</string> <string name="storage_perm_denied_msg">没有存储权限时DroidFS无法工作</string>
<string name="get_size_failed">无法获取文件大小</string> <string name="get_size_failed">无法获取文件大小</string>
<string name="parent_folder">上一级文件夹</string> <string name="parent_folder">文件夹</string>
<string name="enter_volume_path">输入卷路径</string> <string name="enter_volume_path">输入卷路径</string>
<string name="enter_volume_name">输入卷名称</string> <string name="enter_volume_name">输入卷名称</string>
<string name="external_open">使用外部软件打开</string> <string name="external_open">使用外部软件打开</string>
<string name="single_delete_confirm">确认删除%s?</string> <string name="single_delete_confirm">确认删除%s?</string>
<string name="multiple_delete_confirm">确认删除多个文件:%s</string> <string name="multiple_delete_confirm">确认删除多个文件:%s</string>
@ -46,14 +46,14 @@
<string name="volume">卷: %s</string> <string name="volume">卷: %s</string>
<string name="yes">确认</string> <string name="yes">确认</string>
<string name="no">取消</string> <string name="no">取消</string>
<string name="ask_for_wipe">确认擦除原始文件?</string> <string name="ask_for_wipe">确认擦除文件?</string>
<string name="wipe_failed">擦除失败: %s</string> <string name="wipe_failed">擦除失败: %s</string>
<string name="wipe_successful">擦除成功!</string> <string name="wipe_successful">擦除成功!</string>
<string name="rename">重命名</string> <string name="rename">重命名</string>
<string name="rename_title">新名称:</string> <string name="rename_title">新名称:</string>
<string name="rename_failed">无法重命名: %s</string> <string name="rename_failed">无法重命名: %s</string>
<string name="sort_order">排序方法:</string> <string name="sort_order">排序方法:</string>
<string name="change_password_failed">操作失败,请检查你的密码</string> <string name="change_password_failed">操作失败,请检查你的密码</string>
<string name="share_menu_label">使用DroidFS加密</string> <string name="share_menu_label">使用DroidFS加密</string>
<string name="share_intent_parsing_failed">处理分享请求失败</string> <string name="share_intent_parsing_failed">处理分享请求失败</string>
<string name="listdir_null_error_msg">无法访问这个文件夹</string> <string name="listdir_null_error_msg">无法访问这个文件夹</string>
@ -69,13 +69,13 @@
<string name="title_activity_settings">DroidFS设置</string> <string name="title_activity_settings">DroidFS设置</string>
<string name="explorer">浏览</string> <string name="explorer">浏览</string>
<string name="settings_title_sort_order">默认排序方法</string> <string name="settings_title_sort_order">默认排序方法</string>
<string name="usf_decrypt">允许导出文件</string> <string name="usf_decrypt">允许导出/解密文件</string>
<string name="usf_share">允许通过系统分享菜单分享文件</string> <string name="usf_share">允许通过系统分享菜单分享文件</string>
<string name="usf_open">允许其他应用打开文件</string> <string name="usf_open">允许其他应用打开文件</string>
<string name="usf_screenshot">允许截屏</string> <string name="usf_screenshot">允许截屏</string>
<string name="usf_fingerprint">允许通过指纹保存密码哈希</string> <string name="usf_fingerprint">允许通过指纹保存密码哈希</string>
<string name="usf_volume_management">加密卷管理</string> <string name="usf_volume_management">加密卷管理</string>
<string name="usf_keep_open">当应用切入后台时已打开的卷不再自动锁上</string> <string name="usf_keep_open">当应用切入后台保持卷打开状态</string>
<string name="unsafe_features">以下功能会降低安全性</string> <string name="unsafe_features">以下功能会降低安全性</string>
<string name="manage_unsafe_features">管理非安全功能</string> <string name="manage_unsafe_features">管理非安全功能</string>
<string name="manage_unsafe_features_summary">打开/关闭非安全功能</string> <string name="manage_unsafe_features_summary">打开/关闭非安全功能</string>
@ -89,7 +89,7 @@
<string name="text">文本</string> <string name="text">文本</string>
<string name="save_failed">保存失败</string> <string name="save_failed">保存失败</string>
<string name="file_saved">文件已保存!</string> <string name="file_saved">文件已保存!</string>
<string name="ask_save">有些更改未保存,在关闭文件之前要看看吗</string> <string name="ask_save">有些更改未保存,在文件关闭之前要看看吗</string>
<string name="save">保存</string> <string name="save">保存</string>
<string name="discard">丢弃</string> <string name="discard">丢弃</string>
<string name="word_wrap">自动换行</string> <string name="word_wrap">自动换行</string>
@ -109,7 +109,7 @@
<string name="gitea">Gitea</string> <string name="gitea">Gitea</string>
<string name="gitea_summary">DroidFS在Chapril Gitea站上的仓库。与Github不同, Gitea是完全的自主搭建的自由软件.存有源码文档以及BUG追踪等</string> <string name="gitea_summary">DroidFS在Chapril Gitea站上的仓库。与Github不同, Gitea是完全的自主搭建的自由软件.存有源码文档以及BUG追踪等</string>
<string name="share">分享</string> <string name="share">分享</string>
<string name="decrypt_files">导出</string> <string name="decrypt_files">导出/解密</string>
<string name="copy_failed">复制%s失败</string> <string name="copy_failed">复制%s失败</string>
<string name="copy_success">复制成功</string> <string name="copy_success">复制成功</string>
<string name="add">添加</string> <string name="add">添加</string>
@ -140,13 +140,13 @@
<string name="version">版本</string> <string name="version">版本</string>
<string name="error_cipher_null">错误: 密文为空</string> <string name="error_cipher_null">错误: 密文为空</string>
<string name="key_permanently_invalidated_exception">KeyPermanentlyInvalidatedException</string> <string name="key_permanently_invalidated_exception">KeyPermanentlyInvalidatedException</string>
<string name="key_permanently_invalidated_exception_msg">看起来你添加了新的指纹。原有的密码哈希将失效</string> <string name="key_permanently_invalidated_exception_msg">看起来你添加了新的指纹。原有的密码哈希将不可用</string>
<string name="usf_read_doc">在启用这些功能之前请仔细阅读</string> <string name="usf_read_doc">你在启用这些功能之前应当仔细阅读</string>
<string name="usf_doc">非安全功能的说明文档</string> <string name="usf_doc">非安全功能文档</string>
<string name="error_retrieving_filename">通过URI检索文件失败: %s</string> <string name="error_retrieving_filename">通过URI检索文件失败: %s</string>
<string name="hidden_volume">隐藏卷</string> <string name="hidden_volume">隐藏卷</string>
<string name="error_slash_in_name">卷名不应当包含斜杠</string> <string name="error_slash_in_name">卷名不应当包含斜杠</string>
<string name="hidden_volume_warning">隐藏卷存放在DroidFS的私有文件夹内其他应用在没有Root权限的情况下无法读取隐藏卷。如果你卸载DroidFS或者在应用管理器中清除了DroidFS的数据隐藏卷内的文件会全部消失。请提前做好备份!</string> <string name="hidden_volume_warning">隐藏卷保存在DroidFS的私有存储空间下。隐藏卷在其他应用没有Root权限的情况下均不可见。但若你卸载DroidFS或者在应用管理器中清除DroidFS的数据这些隐藏卷的数据都会丢失。确保不会发生误操作或者做好备份!</string>
<string name="camera_perm_needed">照相需要授予相机权限</string> <string name="camera_perm_needed">照相需要授予相机权限</string>
<string name="choose_resolution">选择分辨率</string> <string name="choose_resolution">选择分辨率</string>
<string name="file_operations">文件操作</string> <string name="file_operations">文件操作</string>
@ -163,8 +163,8 @@
<string name="close_volume">关闭卷</string> <string name="close_volume">关闭卷</string>
<string name="sort_by">分类方式</string> <string name="sort_by">分类方式</string>
<string name="cut">剪切</string> <string name="cut">剪切</string>
<string name="map_folders">显示文件夹大小</string> <string name="map_folders">文件夹映射</string>
<string name="map_folders_summary">通过递归映射来计算文件夹的大小(如果文件夹很大启用该功能将导致APP卡顿)</string> <string name="map_folders_summary">通过递归映射文件夹计算大小(如果映射的文件夹很大则应当关闭该选项)</string>
<string name="camera_optimization">相机优化</string> <string name="camera_optimization">相机优化</string>
<string name="maximize_quality">最大质量</string> <string name="maximize_quality">最大质量</string>
<string name="minimize_latency">最低延迟</string> <string name="minimize_latency">最低延迟</string>
@ -185,15 +185,15 @@
<string name="remove_fingerprint">移除指纹验证</string> <string name="remove_fingerprint">移除指纹验证</string>
<string name="unrecoverable_key_exception_msg">%s. 无法加载加密密钥</string> <string name="unrecoverable_key_exception_msg">%s. 无法加载加密密钥</string>
<string name="unrecoverable_key_exception">UnrecoverableKeyException</string> <string name="unrecoverable_key_exception">UnrecoverableKeyException</string>
<string name="delete_hidden_volume_question">%s 是隐藏的,你希望DroidFS暂时不在UI上显示该卷的入口还是希望DroidFS删除卷内的文件?</string> <string name="delete_hidden_volume_question">%s 是隐藏的,你希望仅仅移除对该卷的记录还是完全擦除其内容?</string>
<string name="forget_only">仅删除UI上的入口</string> <string name="forget_only">仅删除记录</string>
<string name="delete_volume">删除卷</string> <string name="delete_volume">删除卷</string>
<string name="hidden_volume_description">在DroidFS的私有文件夹中存放该卷</string> <string name="hidden_volume_description">在DroidFS的私有(内部)存储中存放该卷</string>
<string name="error_is_file">出错: 文件已存在</string> <string name="error_is_file">出错: 文件已存在</string>
<string name="volume_path_label">选择加密卷的路径:</string> <string name="volume_path_label">选择加密卷的路径:</string>
<string name="volume_name_label">输入卷:</string> <string name="volume_name_label">输入卷名:</string>
<string name="volume_path_hint">路径</string> <string name="volume_path_hint">卷路径</string>
<string name="volume_name_hint"></string> <string name="volume_name_hint">卷名</string>
<string name="password_label">输入卷的密码:</string> <string name="password_label">输入卷的密码:</string>
<string name="password_confirmation_label">再次输入密码:</string> <string name="password_confirmation_label">再次输入密码:</string>
<string name="password_confirmation_hint">密码(确认)</string> <string name="password_confirmation_hint">密码(确认)</string>
@ -208,7 +208,7 @@
<string name="biometric_error">生物识别错误: %s</string> <string name="biometric_error">生物识别错误: %s</string>
<string name="apply_to_all">将该设置应用到所有隐藏卷</string> <string name="apply_to_all">将该设置应用到所有隐藏卷</string>
<string name="select_volume">选择卷</string> <string name="select_volume">选择卷</string>
<string name="current_password_label">输入该卷密码</string> <string name="current_password_label">输入该卷密码</string>
<string name="current_password_hint">当前密码</string> <string name="current_password_hint">当前密码</string>
<string name="new_password_label">输入新密码:</string> <string name="new_password_label">输入新密码:</string>
<string name="new_password_hint">新密码</string> <string name="new_password_hint">新密码</string>
@ -234,8 +234,8 @@
<string name="remove_default_open">不要默认打开</string> <string name="remove_default_open">不要默认打开</string>
<string name="elements_selected">已选 %d/%d</string> <string name="elements_selected">已选 %d/%d</string>
<string name="pin_passwords_title">数字键盘布局</string> <string name="pin_passwords_title">数字键盘布局</string>
<string name="pin_passwords_summary">输入卷的密码时使用纯数字键盘</string> <string name="pin_passwords_summary">键入卷密码时使用纯数字键盘</string>
<string name="volume_type_label">类型:</string> <string name="volume_type_label">卷类型:</string>
<string name="gocryptfs">Gocryptfs</string> <string name="gocryptfs">Gocryptfs</string>
<string name="cryfs">CryFS</string> <string name="cryfs">CryFS</string>
<string name="gocryptfs_disabled">Gocrytfs支持已关闭</string> <string name="gocryptfs_disabled">Gocrytfs支持已关闭</string>
@ -265,9 +265,9 @@
<string name="inaccessible_base_dir">卷不存在或者不可访问</string> <string name="inaccessible_base_dir">卷不存在或者不可访问</string>
<string name="task_failed">任务失败: %s</string> <string name="task_failed">任务失败: %s</string>
<string name="usf_expose">暴露已打开的卷</string> <string name="usf_expose">暴露已打开的卷</string>
<string name="usf_expose_summary">允许其他应用软件通过DocumentsProviders接口读取卷内的文件</string> <string name="usf_expose_summary">允许其他应用软件通过文档提供接口访问已经打开的卷</string>
<string name="usf_saf_write">允许写入</string> <string name="usf_saf_write">允许写入</string>
<string name="usf_saf_write_summary">允许其他应用读取和删改卷内的文件</string> <string name="usf_saf_write_summary">允许其他应用打开卷文件时写入内容</string>
<string name="saf">存储访问框架(SAF)</string> <string name="saf">存储访问框架(SAF)</string>
<string name="tmp_export_failed">导出失败: %s</string> <string name="tmp_export_failed">导出失败: %s</string>
<string name="export_failed_create">无法创建导出文件</string> <string name="export_failed_create">无法创建导出文件</string>

View File

@ -38,12 +38,6 @@
<item>Pink</item> <item>Pink</item>
</string-array> </string-array>
<string-array name="export_methods">
<item>Auto (depending on available memory)</item>
<item>Temporary export on disk (reliable but may leave traces)</item>
<item>Memory file (safer but doesn\'t always work)</item>
</string-array>
<!-- don't translate the following otherwise the app will crash --> <!-- don't translate the following otherwise the app will crash -->
<string-array name="sort_orders_values"> <string-array name="sort_orders_values">
<item>name</item> <item>name</item>
@ -63,10 +57,4 @@
<item>purple</item> <item>purple</item>
<item>pink</item> <item>pink</item>
</string-array> </string-array>
<string-array name="export_methods_values">
<item>auto</item>
<item>disk</item>
<item>memory</item>
</string-array>
</resources> </resources>

View File

@ -273,10 +273,4 @@
<string name="export_failed_export">failed to export file</string> <string name="export_failed_export">failed to export file</string>
<string name="export_mem">Exporting to memory…</string> <string name="export_mem">Exporting to memory…</string>
<string name="export_disk">Exporting to disk…</string> <string name="export_disk">Exporting to disk…</string>
<string name="memfd_create_unsupported">Your current kernel does not support memfd_create(). This feature requires a minimum kernel version of %s.</string>
<string name="export_method">Export method</string>
<string name="export_method_summary">File export method. Used for sharing, external opening and accessing exposed files.</string>
<string name="debug">Debug</string>
<string name="logcat_title">DroidFS Logcat</string>
<string name="logcat_saved">Logcat saved</string>
</resources> </resources>

View File

@ -93,16 +93,6 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/debug">
<Preference
android:key="logcat"
android:title="Logcat"
android:summary="View the DroidFS logcat"
android:icon="@drawable/icon_debug"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/about"> <PreferenceCategory android:title="@string/about">
<Preference <Preference
@ -120,6 +110,7 @@
</Preference> </Preference>
<Preference <Preference
android:key="version"
android:icon="@drawable/icon_info" android:icon="@drawable/icon_info"
android:title="@string/version" android:title="@string/version"
android:summary="@string/versionName"/> <!--added by gradle at build time--> android:summary="@string/versionName"/> <!--added by gradle at build time-->

View File

@ -78,15 +78,6 @@
android:title="@string/usf_saf_write" android:title="@string/usf_saf_write"
android:summary="@string/usf_saf_write_summary" /> android:summary="@string/usf_saf_write_summary" />
<ListPreference
android:key="export_method"
android:entries="@array/export_methods"
android:entryValues="@array/export_methods_values"
android:defaultValue="auto"
android:title="@string/export_method"
android:summary="@string/export_method_summary"
android:icon="@drawable/icon_settings"/>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -1,6 +1,13 @@
plugins { buildscript {
id("com.android.application") version '8.4.0' apply false ext.kotlin_version = '1.9.0'
id("org.jetbrains.kotlin.android") version "1.9.24" apply false repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
} }
allprojects { allprojects {

View File

@ -16,4 +16,3 @@ kotlin.code.style=official
android.native.buildOutput=verbose android.native.buildOutput=verbose
android.nonTransitiveRClass=false android.nonTransitiveRClass=false
org.gradle.configuration-cache=true

View File

@ -1,8 +1,7 @@
#Sun Jun 02 15:50:33 UTC 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -1 +1 @@
Subproject commit 48631380a7a8b5f0932078e2b643e06c3f433890 Subproject commit 22965932759f232328810eadf3f02671b5c6ff99

View File

@ -1,8 +1,2 @@
pluginManagement {
repositories {
google()
mavenCentral()
}
}
include ':app', ':libpdfviewer:app' include ':app', ':libpdfviewer:app'
rootProject.name = "DroidFS" rootProject.name = "DroidFS"