From 2ee7a5b871993d38d931cfe17b61f4663fb6807a Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Wed, 23 Mar 2022 16:35:13 +0100 Subject: [PATCH] Allow changing thumbnail max size --- .../sushi/hardcore/droidfs/ConstValues.kt | 75 +++++++++---------- .../sushi/hardcore/droidfs/GocryptfsVolume.kt | 3 +- .../hardcore/droidfs/SettingsActivity.kt | 69 ++++++++++++++++- .../sushi/hardcore/droidfs/VolumeDatabase.kt | 2 +- .../adapters/ExplorerElementAdapter.kt | 5 +- .../add_volume/CreateVolumeFragment.kt | 2 +- .../droidfs/explorers/BaseExplorerActivity.kt | 17 +++-- .../file_viewers/GocryptfsDataSource.kt | 2 +- .../droidfs/file_viewers/ImageViewer.kt | 4 +- .../droidfs/file_viewers/MediaPlayer.kt | 2 +- .../sushi/hardcore/droidfs/util/PathUtils.kt | 2 +- .../java/sushi/hardcore/droidfs/util/Wiper.kt | 8 +- .../res/drawable/icon_image_crossed_out.xml | 9 +++ app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/root_preferences.xml | 5 ++ 15 files changed, 147 insertions(+), 62 deletions(-) create mode 100644 app/src/main/res/drawable/icon_image_crossed_out.xml diff --git a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt index 51b87d1..1ee8b97 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt @@ -3,45 +3,44 @@ package sushi.hardcore.droidfs import android.net.Uri import java.io.File -class ConstValues { - companion object { - const val creator = "DroidFS" - const val gocryptfsConfFilename = "gocryptfs.conf" - const val FILE_MODE = 384 //0600 - const val DIRECTORY_MODE = 448 //0700 - const val volumeDatabaseName = "SavedVolumes" - const val sort_order_key = "sort_order" - val fakeUri: Uri = Uri.parse("fakeuri://droidfs") - const val MAX_KERNEL_WRITE = 128*1024 - const val wipe_passes = 2 - const val slideshow_delay: Long = 4000 - const val DEFAULT_THEME_VALUE = "dark_green" - private val fileExtensions = mapOf( - Pair("image", listOf("png", "jpg", "jpeg", "gif", "webp", "bmp")), - Pair("video", listOf("mp4", "webm", "mkv", "mov")), - Pair("audio", listOf("mp3", "ogg", "m4a", "wav", "flac")), - Pair("pdf", listOf("pdf")), - Pair("text", listOf("txt", "json", "conf", "log", "xml", "java", "kt", "py", "pl", "rb", "go", "c", "h", "cpp", "hpp", "rs", "sh", "bat", "js", "html", "css", "php", "yml", "yaml", "toml", "ini", "md", "properties")) - ) +object ConstValues { + const val CREATOR = "DroidFS" + const val FILE_MODE = 384 //0600 + const val DIRECTORY_MODE = 448 //0700 + const val VOLUME_DATABASE_NAME = "SavedVolumes" + const val SORT_ORDER_KEY = "sort_order" + val FAKE_URI: Uri = Uri.parse("fakeuri://droidfs") + const val MAX_KERNEL_WRITE = 128*1024 + const val WIPE_PASSES = 2 + const val SLIDESHOW_DELAY: Long = 4000 + const val DEFAULT_THEME_VALUE = "dark_green" + const val THUMBNAIL_MAX_SIZE_KEY = "thumbnail_max_size" + const val DEFAULT_THUMBNAIL_MAX_SIZE = 10_000L + private val FILE_EXTENSIONS = mapOf( + Pair("image", listOf("png", "jpg", "jpeg", "gif", "webp", "bmp")), + Pair("video", listOf("mp4", "webm", "mkv", "mov")), + Pair("audio", listOf("mp3", "ogg", "m4a", "wav", "flac")), + Pair("pdf", listOf("pdf")), + Pair("text", listOf("txt", "json", "conf", "log", "xml", "java", "kt", "py", "pl", "rb", "go", "c", "h", "cpp", "hpp", "rs", "sh", "bat", "js", "html", "css", "php", "yml", "yaml", "toml", "ini", "md", "properties")) + ) - fun isExtensionType(extensionType: String, path: String): Boolean { - return fileExtensions[extensionType]?.contains(File(path).extension.lowercase()) ?: false - } + fun isExtensionType(extensionType: String, path: String): Boolean { + return FILE_EXTENSIONS[extensionType]?.contains(File(path).extension.lowercase()) ?: false + } - fun isImage(path: String): Boolean { - return isExtensionType("image", path) - } - fun isVideo(path: String): Boolean { - return isExtensionType("video", path) - } - fun isAudio(path: String): Boolean { - return isExtensionType("audio", path) - } - fun isPDF(path: String): Boolean { - return isExtensionType("pdf", path) - } - fun isText(path: String): Boolean { - return isExtensionType("text", path) - } + fun isImage(path: String): Boolean { + return isExtensionType("image", path) + } + fun isVideo(path: String): Boolean { + return isExtensionType("video", path) + } + fun isAudio(path: String): Boolean { + return isExtensionType("audio", path) + } + fun isPDF(path: String): Boolean { + return isExtensionType("pdf", path) + } + fun isText(path: String): Boolean { + return isExtensionType("text", path) } } \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/droidfs/GocryptfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/GocryptfsVolume.kt index d5ea409..d394a25 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/GocryptfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/GocryptfsVolume.kt @@ -30,13 +30,14 @@ class GocryptfsVolume(val applicationContext: Context, var sessionID: Int) { const val KeyLen = 32 const val ScryptDefaultLogN = 16 const val DefaultBS = 4096 + const val CONFIG_FILE_NAME = "gocryptfs.conf" external fun createVolume(root_cipher_dir: String, password: CharArray, plainTextNames: Boolean, xchacha: Int, logN: Int, creator: String, returnedHash: ByteArray?): Boolean external fun init(root_cipher_dir: String, password: CharArray?, givenHash: ByteArray?, returnedHash: ByteArray?): Int external fun changePassword(root_cipher_dir: String, old_password: CharArray?, givenHash: ByteArray?, new_password: CharArray, returnedHash: ByteArray?): Boolean fun isGocryptfsVolume(path: File): Boolean { if (path.isDirectory){ - return File(path, ConstValues.gocryptfsConfFilename).isFile + return File(path, CONFIG_FILE_NAME).isFile } return false } diff --git a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt index f932c87..7809426 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt @@ -1,13 +1,20 @@ package sushi.hardcore.droidfs +import android.content.SharedPreferences import android.os.Build import android.os.Bundle import android.view.MenuItem +import android.view.inputmethod.EditorInfo +import android.widget.Toast import androidx.preference.ListPreference +import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import sushi.hardcore.droidfs.databinding.ActivitySettingsBinding +import sushi.hardcore.droidfs.databinding.DialogEditTextBinding +import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder +import java.lang.NumberFormatException class SettingsActivity : BaseActivity() { @@ -20,7 +27,7 @@ class SettingsActivity : BaseActivity() { val fragment = if (screen == "UnsafeFeaturesSettingsFragment") { UnsafeFeaturesSettingsFragment() } else { - MainSettingsFragment() + MainSettingsFragment(sharedPrefs) } supportFragmentManager .beginTransaction() @@ -38,13 +45,71 @@ class SettingsActivity : BaseActivity() { } } - class MainSettingsFragment : PreferenceFragmentCompat() { + class MainSettingsFragment(private val sharedPrefs: SharedPreferences) : PreferenceFragmentCompat() { + private lateinit var maxSizePreference: Preference + + private fun setThumbnailMaxSize(input: String) { + val value: Long + try { + value = input.toLong() + } catch (e: NumberFormatException) { + Toast.makeText(requireContext(), R.string.invalid_number, Toast.LENGTH_SHORT).show() + showMaxSizeDialog() + return + } + val size = value*1000 + if (size < 0) { + Toast.makeText(requireContext(), R.string.invalid_number, Toast.LENGTH_SHORT).show() + showMaxSizeDialog() + } else { + with(sharedPrefs.edit()) { + putLong(ConstValues.THUMBNAIL_MAX_SIZE_KEY, value) + apply() + } + maxSizePreference.summary = PathUtils.formatSize(size) + } + } + + private fun showMaxSizeDialog() { + val dialogBinding = DialogEditTextBinding.inflate(layoutInflater) + with (dialogBinding.dialogEditText) { + inputType = EditorInfo.TYPE_CLASS_NUMBER + hint = getString(R.string.size_hint) + } + val dialog = CustomAlertDialogBuilder(requireContext(), (requireActivity() as BaseActivity).themeValue) + .setTitle(R.string.thumbnail_max_size) + .setView(dialogBinding.root) + .setPositiveButton(R.string.ok) { _, _ -> + setThumbnailMaxSize(dialogBinding.dialogEditText.text.toString()) + } + .create() + dialogBinding.dialogEditText.setOnEditorActionListener { _, _, _ -> + dialog.dismiss() + setThumbnailMaxSize(dialogBinding.dialogEditText.text.toString()) + true + } + dialog.show() + } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.root_preferences, rootKey) findPreference("theme")?.setOnPreferenceChangeListener { _, newValue -> (activity as BaseActivity).onThemeChanged(newValue as String) true } + findPreference(ConstValues.THUMBNAIL_MAX_SIZE_KEY)?.let { + maxSizePreference = it + maxSizePreference.summary = getString( + R.string.thumbnail_max_size_summary, + PathUtils.formatSize(sharedPrefs.getLong( + ConstValues.THUMBNAIL_MAX_SIZE_KEY, ConstValues.DEFAULT_THUMBNAIL_MAX_SIZE + )*1000) + ) + maxSizePreference.setOnPreferenceClickListener { + showMaxSizeDialog() + false + } + } } } diff --git a/app/src/main/java/sushi/hardcore/droidfs/VolumeDatabase.kt b/app/src/main/java/sushi/hardcore/droidfs/VolumeDatabase.kt index ca2a59a..1f30d38 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/VolumeDatabase.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/VolumeDatabase.kt @@ -6,7 +6,7 @@ import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper class VolumeDatabase(context: Context): SQLiteOpenHelper(context, - ConstValues.volumeDatabaseName, null, 3) { + ConstValues.VOLUME_DATABASE_NAME, null, 3) { companion object { const val TABLE_NAME = "Volumes" const val COLUMN_NAME = "name" diff --git a/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt b/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt index 2c26e21..6b5d223 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt @@ -23,7 +23,8 @@ class ExplorerElementAdapter( val activity: AppCompatActivity, val gocryptfsVolume: GocryptfsVolume?, val onExplorerElementClick: (Int) -> Unit, - val onExplorerElementLongClick: (Int) -> Unit + val onExplorerElementLongClick: (Int) -> Unit, + val thumbnailMaxSize: Long, ) : RecyclerView.Adapter() { val dateFormat: DateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault()) var explorerElements = listOf() @@ -127,7 +128,7 @@ class ExplorerElementAdapter( adapter.gocryptfsVolume?.let { volume -> displayThumbnail = true Thread { - volume.loadWholeFile(fullPath, maxSize = 50_000_000).first?.let { + volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let { if (displayThumbnail) { adapter.activity.runOnUiThread { if (displayThumbnail) { diff --git a/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt b/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt index a47b1da..6c5ce50 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt @@ -127,7 +127,7 @@ class CreateVolumeFragment: Fragment() { var returnedHash: ByteArray? = null if (binding.checkboxSavePassword.isChecked) returnedHash = ByteArray(GocryptfsVolume.KeyLen) - if (GocryptfsVolume.createVolume(volumePath, password, false, xchacha, GocryptfsVolume.ScryptDefaultLogN, ConstValues.creator, returnedHash)) { + if (GocryptfsVolume.createVolume(volumePath, password, false, xchacha, GocryptfsVolume.ScryptDefaultLogN, ConstValues.CREATOR, returnedHash)) { val volumeName = if (isHiddenVolume) File(volumePath).name else volumePath val volume = Volume(volumeName, isHiddenVolume) volumeDatabase.apply { diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt index 329fefc..0c5ba30 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -23,11 +23,11 @@ import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import sushi.hardcore.droidfs.BaseActivity import sushi.hardcore.droidfs.ConstValues -import sushi.hardcore.droidfs.ConstValues.Companion.isAudio -import sushi.hardcore.droidfs.ConstValues.Companion.isImage -import sushi.hardcore.droidfs.ConstValues.Companion.isPDF -import sushi.hardcore.droidfs.ConstValues.Companion.isText -import sushi.hardcore.droidfs.ConstValues.Companion.isVideo +import sushi.hardcore.droidfs.ConstValues.isAudio +import sushi.hardcore.droidfs.ConstValues.isImage +import sushi.hardcore.droidfs.ConstValues.isPDF +import sushi.hardcore.droidfs.ConstValues.isText +import sushi.hardcore.droidfs.ConstValues.isVideo import sushi.hardcore.droidfs.GocryptfsVolume import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter @@ -79,7 +79,7 @@ open class BaseExplorerActivity : BaseActivity() { sortOrderValues = resources.getStringArray(R.array.sort_orders_values) foldersFirst = sharedPrefs.getBoolean("folders_first", true) mapFolders = sharedPrefs.getBoolean("map_folders", true) - currentSortOrderIndex = resources.getStringArray(R.array.sort_orders_values).indexOf(sharedPrefs.getString(ConstValues.sort_order_key, "name")) + currentSortOrderIndex = resources.getStringArray(R.array.sort_orders_values).indexOf(sharedPrefs.getString(ConstValues.SORT_ORDER_KEY, "name")) init() recycler_view_explorer = findViewById(R.id.recycler_view_explorer) refresher = findViewById(R.id.refresher) @@ -101,7 +101,8 @@ open class BaseExplorerActivity : BaseActivity() { null }, ::onExplorerItemClick, - ::onExplorerItemLongClick + ::onExplorerItemLongClick, + sharedPrefs.getLong(ConstValues.THUMBNAIL_MAX_SIZE_KEY, ConstValues.DEFAULT_THUMBNAIL_MAX_SIZE)*1000, ) explorerViewModel= ViewModelProvider(this).get(ExplorerViewModel::class.java) currentDirectoryPath = explorerViewModel.currentDirectoryPath @@ -224,7 +225,7 @@ open class BaseExplorerActivity : BaseActivity() { explorerAdapter.explorerElements = explorerElements unselectAll() val sharedPrefsEditor = sharedPrefs.edit() - sharedPrefsEditor.putString(ConstValues.sort_order_key, sortOrderValues[currentSortOrderIndex]) + sharedPrefsEditor.putString(ConstValues.SORT_ORDER_KEY, sortOrderValues[currentSortOrderIndex]) sharedPrefsEditor.apply() } diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt index 8bf1e0e..507fe67 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt @@ -21,7 +21,7 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private } override fun getUri(): Uri { - return ConstValues.fakeUri + return ConstValues.FAKE_URI } override fun close() { diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt index 454cd48..f383e2b 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt @@ -121,7 +121,7 @@ class ImageViewer: FileViewerActivity() { binding.imageButtonSlideshow.setOnClickListener { if (!slideshowActive){ slideshowActive = true - handler.postDelayed(slideshowNext, ConstValues.slideshow_delay) + handler.postDelayed(slideshowNext, ConstValues.SLIDESHOW_DELAY) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) hideUI.run() Toast.makeText(this, R.string.slideshow_started, Toast.LENGTH_SHORT).show() @@ -204,7 +204,7 @@ class ImageViewer: FileViewerActivity() { if (!slideshowSwipe) { //reset slideshow delay if user swipes handler.removeCallbacks(slideshowNext) } - handler.postDelayed(slideshowNext, ConstValues.slideshow_delay) + handler.postDelayed(slideshowNext, ConstValues.SLIDESHOW_DELAY) } } diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt index c4f1239..7dc4a1f 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt @@ -27,7 +27,7 @@ abstract class MediaPlayer: FileViewerActivity() { private fun createMediaSource(filePath: String): MediaSource { val dataSourceFactory = GocryptfsDataSource.Factory(gocryptfsVolume, filePath) return ProgressiveMediaSource.Factory(dataSourceFactory, DefaultExtractorsFactory()) - .createMediaSource(MediaItem.fromUri(ConstValues.fakeUri)) + .createMediaSource(MediaItem.fromUri(ConstValues.FAKE_URI)) } private fun initializePlayer(){ diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt index 0892d3e..89646b8 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt @@ -88,7 +88,7 @@ object PathUtils { return result } - private val units = arrayOf("B", "kB", "MB", "GB", "TB") + private val units = arrayOf("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") fun formatSize(size: Long): String { if (size <= 0) { return "0 B" diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt b/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt index 1fff7a8..6be6f0c 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt @@ -25,11 +25,11 @@ object Wiper { val buff = ByteArray(buff_size) Arrays.fill(buff, 0.toByte()) val writes = ceil(size.toDouble() / buff_size).toInt() - for (i in 0 until ConstValues.wipe_passes) { + for (i in 0 until ConstValues.WIPE_PASSES) { for (j in 0 until writes) { os.write(buff) } - if (i < ConstValues.wipe_passes - 1) { + if (i < ConstValues.WIPE_PASSES - 1) { //reopening to flush and seek os.close() os = context.contentResolver.openOutputStream(uri)!! @@ -57,11 +57,11 @@ object Wiper { val buff = ByteArray(buff_size) Arrays.fill(buff, 0.toByte()) val writes = ceil(size.toDouble() / buff_size).toInt() - for (i in 0 until ConstValues.wipe_passes) { + for (i in 0 until ConstValues.WIPE_PASSES) { for (j in 0 until writes) { os.write(buff) } - if (i < ConstValues.wipe_passes - 1) { + if (i < ConstValues.WIPE_PASSES - 1) { //reopening to flush and seek os.close() os = FileOutputStream(file) diff --git a/app/src/main/res/drawable/icon_image_crossed_out.xml b/app/src/main/res/drawable/icon_image_crossed_out.xml new file mode 100644 index 0000000..afb4128 --- /dev/null +++ b/app/src/main/res/drawable/icon_image_crossed_out.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d42e912..42b01a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -228,4 +228,8 @@ Copying volumeā€¦ A hidden volume with the same name already exists. PDF document + Maximum size for thumbnails + Maximum file size for which to load a thumbnail. Current value: %s + Size (in KB) + Invalid number diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 09d5b80..d1601fa 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -52,6 +52,11 @@ android:title="@string/thumbnails" android:summary="@string/thumbnails_summary"/> + +