Compare commits

...

61 Commits

Author SHA1 Message Date
Hardcore Sushi 5144947a4a
Media player fixes: better handling of RTL & orientation 2 weeks ago
Muhmmad14333653 6b52eed9d0
update arabic translation 2 weeks ago
Muhmmad14333653 2a257d91d0
update Arabic translation 2 weeks ago
Hardcore Sushi f837556af5
Fix explorer info bar color in dark mode 2 weeks ago
Hardcore Sushi b7ab267d16
Arabic translation 2 weeks ago
Hardcore Sushi 5ea0b8ad41
Actually fix camera icon tint bug 2 weeks ago
Hardcore Sushi ec348383c6
Fix camera icon tint bug 2 weeks ago
Hardcore Sushi c8d266150c
Fix explorer menu display 2 weeks ago
CyanWolf 4bbc9360b4
Update Spanish translation 4 weeks ago
Hardcore Sushi 8aa2be2b05
Update libgocryptfs & Small UX fix 4 weeks ago
Hardcore Sushi e2248220c4
DroidFS v1.10.0 4 weeks ago
Hardcore Sushi 7959b20b3f
Update libgocryptfs 4 weeks ago
solokot 8cebe499f0
Updated Russian translation 4 weeks ago
cyanwolfg a22b9d8fa8
Update Spanish translation 4 weeks ago
Hardcore Sushi cba1418417
Fix move operation 4 weeks ago
Hardcore Sushi b6b8bba666
Save checkbox state if opening volume fails 4 weeks ago
Hardcore Sushi e00abdf5bb
Switch to Kotlin coroutines 4 weeks ago
Hardcore Sushi 72cce1d7e1
Show a fingerprint icon when password hash is saved 1 month ago
Hardcore Sushi 55b0ac0daa
Prefill text field with current name when renaming a volume 1 month ago
Hardcore Sushi 53f28e9475
Pin passwords 1 month ago
Hardcore Sushi f1d4b07726
Show total number of selected items 1 month ago
Hardcore Sushi 339309b00d
Fix image viewer rotation handling 1 month ago
Hardcore Sushi e6a1285e0a
Fix camera output rotation 1 month ago
Hardcore Sushi ab48f9219b
CameraActivity: only bind 2 use cases at most + some other fixes 1 month ago
Hardcore Sushi c521c7f998
Open default volume on application startup 1 month ago
Hardcore Sushi 1d13dfbde3
Always show total size & Add some explorer info bar translations 1 month ago
Hardcore Sushi 36ab66fb43
Show number of files & folders in current directory 1 month ago
Hardcore Sushi 1caabc2554
Fix explorer layouts 1 month ago
Hardcore Sushi f541504e07
Refactor RecyclerView adapters 1 month ago
Hardcore Sushi 4de5b41102
Thumbnails cache & Don't do full reload on selection change 1 month ago
Hardcore Sushi 4f9aa55dfe
Explorer grid layout 1 month ago
Hardcore Sushi 91de54018d
Prompt for password if fingerprint authentification fails 2 months ago
Hardcore Sushi 2697eaf11b
Spanish translation 2 months ago
Hardcore Sushi 9e69805ade
Update ptbr translation 2 months ago
Hardcore Sushi 18d0f50094
Update to libpdfviewer 13 2 months ago
Hardcore Sushi e32e106ce3
Fix video player controls 2 months ago
Hardcore Sushi 4608a7a165
Fix MainSettingsFragment crash 2 months ago
Hardcore Sushi 985be2de59
Add HEIC to the image extension list 2 months ago
solokot f07d99efed
Updated Russian translation 2 months ago
Hardcore Sushi 4a55d826d9
Volume renaming 2 months ago
Hardcore Sushi 2ee7a5b871
Allow changing thumbnail max size 2 months ago
Hardcore Sushi 72321b8ec5
Switch to StyledPlayerView 2 months ago
Hardcore Sushi 7226cc8218
Fix UI bug on too long volume paths 2 months ago
Hardcore Sushi 55be5cd0e7
DroidFS v1.9.0 2 months ago
solokot 3c4515e4e9
Update Russian translation 2 months ago
Hardcore Sushi 29eb34e1d5
New screenshots 2 months ago
Hardcore Sushi d6f727a142
Fix error message when creating volume on external SD card 2 months ago
Hardcore Sushi 6d5fc465c7
Fix UI bug on alert dialogs 2 months ago
Hardcore Sushi ed0b5eb483
Add PDF in OpenAs dialog & libpdfviewer dialog crash fix 2 months ago
Hardcore Sushi fd0296f801
Update libpdfviewer 2 months ago
Hardcore Sushi 58391802be
More accurate directory size 2 months ago
Hardcore Sushi e01b5a3098
Volume copy 2 months ago
Hardcore Sushi bea0906f65
Display file name on video player 2 months ago
Hardcore Sushi 71a314b0a0
New home UI 2 months ago
Hardcore Sushi 842667cdee
Update ptbr translation 3 months ago
Hardcore Sushi e5bcc5cfc2
Update dependencies 3 months ago
Hardcore Sushi 32508344fe
Update PGP key 3 months ago
Hardcore Sushi ee3df7c3bf
Icon for PDF files 3 months ago
Hardcore Sushi b18232615d
PDF viewer 3 months ago
Hardcore Sushi 83efc53edc
Update PGP keyserver 4 months ago
Hardcore Sushi f546e64c34
ImageViwer: retrieve screen size with Resources.getSystem().displayMetrics 4 months ago
  1. 3
      .gitmodules
  2. 25
      README.md
  3. 29
      app/build.gradle
  4. 2
      app/libgocryptfs
  5. 62
      app/src/main/AndroidManifest.xml
  6. 6
      app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt
  7. 160
      app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt
  8. 277
      app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt
  9. 71
      app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt
  10. 180
      app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt
  11. 259
      app/src/main/java/sushi/hardcore/droidfs/FingerprintProtector.kt
  12. 107
      app/src/main/java/sushi/hardcore/droidfs/GocryptfsVolume.kt
  13. 53
      app/src/main/java/sushi/hardcore/droidfs/LoadingTask.kt
  14. 617
      app/src/main/java/sushi/hardcore/droidfs/MainActivity.kt
  15. 265
      app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt
  16. 13
      app/src/main/java/sushi/hardcore/droidfs/SensorOrientationListener.kt
  17. 94
      app/src/main/java/sushi/hardcore/droidfs/SettingsActivity.kt
  18. 53
      app/src/main/java/sushi/hardcore/droidfs/Volume.kt
  19. 397
      app/src/main/java/sushi/hardcore/droidfs/VolumeActionActivity.kt
  20. 31
      app/src/main/java/sushi/hardcore/droidfs/VolumeDatabase.kt
  21. 205
      app/src/main/java/sushi/hardcore/droidfs/adapters/ExplorerElementAdapter.kt
  22. 4
      app/src/main/java/sushi/hardcore/droidfs/adapters/IconTextDialogAdapter.kt
  23. 1
      app/src/main/java/sushi/hardcore/droidfs/adapters/OpenAsDialogAdapter.kt
  24. 113
      app/src/main/java/sushi/hardcore/droidfs/adapters/SavedVolumesAdapter.kt
  25. 80
      app/src/main/java/sushi/hardcore/droidfs/adapters/SelectableAdapter.kt
  26. 111
      app/src/main/java/sushi/hardcore/droidfs/adapters/VolumeAdapter.kt
  27. 6
      app/src/main/java/sushi/hardcore/droidfs/add_volume/Action.kt
  28. 79
      app/src/main/java/sushi/hardcore/droidfs/add_volume/AddVolumeActivity.kt
  29. 207
      app/src/main/java/sushi/hardcore/droidfs/add_volume/CreateVolumeFragment.kt
  30. 275
      app/src/main/java/sushi/hardcore/droidfs/add_volume/SelectPathFragment.kt
  31. 87
      app/src/main/java/sushi/hardcore/droidfs/content_providers/ExternalProvider.kt
  32. 353
      app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
  33. 263
      app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt
  34. 4
      app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt
  35. 6
      app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt
  36. 345
      app/src/main/java/sushi/hardcore/droidfs/file_operations/FileOperationService.kt
  37. 16
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/AudioPlayer.kt
  38. 4
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt
  39. 2
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/GocryptfsDataSource.kt
  40. 84
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt
  41. 15
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt
  42. 47
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/PdfViewer.kt
  43. 18
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/TextEditor.kt
  44. 23
      app/src/main/java/sushi/hardcore/droidfs/file_viewers/VideoPlayer.kt
  45. 18
      app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt
  46. 20
      app/src/main/java/sushi/hardcore/droidfs/util/WidgetUtil.kt
  47. 29
      app/src/main/java/sushi/hardcore/droidfs/util/Wiper.kt
  48. 7
      app/src/main/java/sushi/hardcore/droidfs/video_recording/VideoCapture.java
  49. 2
      app/src/main/java/sushi/hardcore/droidfs/widgets/CustomAlertDialogBuilder.kt
  50. 59
      app/src/main/java/sushi/hardcore/droidfs/widgets/DoubleTapPlayerView.kt
  51. 39
      app/src/main/java/sushi/hardcore/droidfs/widgets/EditTextDialog.kt
  52. 46
      app/src/main/java/sushi/hardcore/droidfs/widgets/NonScrollableColoredBorderListView.kt
  53. 30
      app/src/main/native/gocryptfs_jni.c
  54. 5
      app/src/main/res/drawable/icon_arrow_back.xml
  55. 2
      app/src/main/res/drawable/icon_delete.xml
  56. 9
      app/src/main/res/drawable/icon_file_pdf.xml
  57. 9
      app/src/main/res/drawable/icon_hidden.xml
  58. 9
      app/src/main/res/drawable/icon_image_crossed_out.xml
  59. 16
      app/src/main/res/drawable/icon_num_pad.xml
  60. 10
      app/src/main/res/drawable/icon_view_grid.xml
  61. 10
      app/src/main/res/drawable/icon_view_list.xml
  62. 9
      app/src/main/res/drawable/icon_volume.xml
  63. 10
      app/src/main/res/drawable/listview_border.xml
  64. BIN
      app/src/main/res/drawable/logo.png
  65. 8
      app/src/main/res/layout/action_bar.xml
  66. 12
      app/src/main/res/layout/activity_add_volume.xml
  67. 7
      app/src/main/res/layout/activity_audio_player.xml
  68. 173
      app/src/main/res/layout/activity_change_password.xml
  69. 100
      app/src/main/res/layout/activity_create.xml
  70. 2
      app/src/main/res/layout/activity_explorer.xml
  71. 2
      app/src/main/res/layout/activity_explorer_base.xml
  72. 2
      app/src/main/res/layout/activity_explorer_drop.xml
  73. 11
      app/src/main/res/layout/activity_image_viewer.xml
  74. 71
      app/src/main/res/layout/activity_main.xml
  75. 70
      app/src/main/res/layout/activity_open.xml
  76. 16
      app/src/main/res/layout/activity_settings.xml
  77. 36
      app/src/main/res/layout/activity_text_editor.xml
  78. 32
      app/src/main/res/layout/activity_text_editor_wrap.xml
  79. 43
      app/src/main/res/layout/activity_video_player.xml
  80. 56
      app/src/main/res/layout/adapter_explorer_element.xml
  81. 33
      app/src/main/res/layout/adapter_explorer_element_grid.xml
  82. 30
      app/src/main/res/layout/adapter_explorer_element_list.xml
  83. 25
      app/src/main/res/layout/adapter_saved_volume.xml
  84. 81
      app/src/main/res/layout/adapter_volume.xml
  85. 123
      app/src/main/res/layout/audio_exo_styled_player_control_view.xml
  86. 20
      app/src/main/res/layout/checkboxes_section.xml
  87. 24
      app/src/main/res/layout/dialog_delete_volume.xml
  88. 4
      app/src/main/res/layout/dialog_edit_text.xml
  89. 2
      app/src/main/res/layout/dialog_loading.xml
  90. 32
      app/src/main/res/layout/dialog_open_volume.xml
  91. 10
      app/src/main/res/layout/dialog_sdcard_error.xml
  92. 30
      app/src/main/res/layout/exo_center_controls.xml
  93. 73
      app/src/main/res/layout/exo_player_control_view.xml
  94. 148
      app/src/main/res/layout/exo_styled_player_control_view.xml
  95. 2
      app/src/main/res/layout/explorer_content.xml
  96. 31
      app/src/main/res/layout/explorer_element_details.xml
  97. 92
      app/src/main/res/layout/explorer_info_bar.xml
  98. 74
      app/src/main/res/layout/fragment_create_volume.xml
  99. 113
      app/src/main/res/layout/fragment_select_path.xml
  100. 19
      app/src/main/res/layout/toolbar.xml
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "app/libgocryptfs"]
path = app/libgocryptfs
url = https://forge.chapril.org/hardcoresushi/libgocryptfs.git
[submodule "libpdfviewer"]
path = libpdfviewer
url = https://forge.chapril.org/hardcoresushi/libpdfviewer.git

25
README.md

@ -50,8 +50,8 @@ You can download DroidFS from [F-Droid](https://f-droid.org/packages/sushi.hardc
APKs available here are signed with my PGP key available on keyservers:
`gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 007F84120107191E` \
Fingerprint: `BD5621479E7B74D36A405BE8007F84120107191E` \
`gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys AFE384344A45E13A` \
Fingerprint: `B64E FE86 CEE1 D054 F082 1711 AFE3 8434 4A45 E13A` \
Email: `Hardcore Sushi <hardcore.sushi@disroot.org>`
To verify APKs, save the PGP-signed message to a file and run `gpg --verify <the file>`. __Don't install any APK if the verification fails !__
@ -101,26 +101,22 @@ You also need to install the Android SDK build tools and the [Android NDK](https
#### Download Sources
```
$ git clone https://github.com/hardcore-sushi/DroidFS.git
```
Download [libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs):
```
$ git clone --recurse-submodules https://github.com/hardcore-sushi/DroidFS.git
$ cd DroidFS
$ git submodule update --init
```
libgocryptfs needs OpenSSL:
[libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs) needs OpenSSL:
```
$ cd app/libgocryptfs
$ wget https://www.openssl.org/source/openssl-1.1.1m.tar.gz
$ wget https://www.openssl.org/source/openssl-1.1.1n.tar.gz
```
Verify OpenSSL signature:
```
$ wget https://www.openssl.org/source/openssl-1.1.1m.tar.gz.asc
$ gpg --verify openssl-1.1.1m.tar.gz.asc openssl-1.1.1m.tar.gz
$ wget https://www.openssl.org/source/openssl-1.1.1n.tar.gz.asc
$ gpg --verify openssl-1.1.1n.tar.gz.asc openssl-1.1.1n.tar.gz
```
Continue **ONLY** if the signature is **VALID**.
```
$ tar -xvzf openssl-1.1.1m.tar.gz
$ tar -xvzf openssl-1.1.1n.tar.gz
```
DroidFS also need [FFmpeg](https://ffmpeg.org) to record encrypted video:
```
@ -138,7 +134,7 @@ $ keytool -genkey -keystore <output file> -alias <key alias> -keyalg EC -validit
Retrieve your Android NDK installation path, usually something like "/home/\<user\>/Android/SDK/ndk/\<NDK version\>". Now you can build libgocryptfs:
```
$ cd DroidFS/app/libgocryptfs
$ env ANDROID_NDK_HOME="<your ndk path>" OPENSSL_PATH="./openssl-1.1.1m" ./build.sh
$ env ANDROID_NDK_HOME="<your ndk path>" OPENSSL_PATH="./openssl-1.1.1n" ./build.sh
```
Then FFmpeg:
```
@ -159,7 +155,8 @@ Now you can install `droidfs.apk` on your device.
Thanks to these open source projects that DroidFS uses:
### Modified code:
- [gocryptfs](https://github.com/rfjakob/gocryptfs) to encrypt your data
- [libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs) (forked from [gocryptfs](https://github.com/rfjakob/gocryptfs)) to encrypt your data
- [libpdfviewer](https://forge.chapril.org/hardcoresushi/libpdfviewer) (forked from [PdfViewer](https://github.com/GrapheneOS/PdfViewer)) to open PDF files
- [DoubleTapPlayerView](https://github.com/vkay94/DoubleTapPlayerView) to add double-click controls to the video player
### Borrowed code:
- [MaterialFiles](https://github.com/zhanghai/MaterialFiles) for kotlin natural sorting implementation

29
app/build.gradle

@ -13,9 +13,10 @@ android {
defaultConfig {
applicationId "sushi.hardcore.droidfs"
minSdkVersion 21
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 29
versionCode 22
versionName "1.7.2"
versionCode 27
versionName "1.10.1"
ndk {
abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
@ -55,29 +56,29 @@ android {
}
dependencies {
implementation project(":libpdfviewer:app")
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.core:core-ktx:1.7.0'
implementation "androidx.appcompat:appcompat:1.4.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
implementation "androidx.sqlite:sqlite-ktx:2.2.0"
implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.google.android.material:material:1.5.0'
implementation "com.github.bumptech.glide:glide:4.12.0"
implementation "androidx.biometric:biometric:1.1.0"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha04"
def exoplayer_version = "2.16.1"
def exoplayer_version = "2.17.1"
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
implementation "androidx.concurrent:concurrent-futures:1.1.0"
def camerax_v1 = "1.1.0-alpha12"
implementation "androidx.camera:camera-camera2:$camerax_v1"
implementation "androidx.camera:camera-lifecycle:$camerax_v1"
def camerax_v2 = "1.0.0-alpha32"
implementation "androidx.camera:camera-view:$camerax_v2"
implementation "androidx.camera:camera-extensions:$camerax_v2"
def camerax_version = "1.1.0-beta03"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:$camerax_version"
implementation "androidx.camera:camera-extensions:$camerax_version"
}

2
app/libgocryptfs

@ -1 +1 @@
Subproject commit 1da2407a614f17a3c64d14ee34fb41e081db9a71
Subproject commit 9e98192442b08362660b45f4e2e50221ba7bc65b

62
app/src/main/AndroidManifest.xml

@ -31,58 +31,30 @@
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/BaseTheme">
<activity
android:name=".CameraActivity"
android:screenOrientation="nosensor" />
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".MainActivity" />
<activity android:name=".explorers.ExplorerActivity" />
<activity android:name=".explorers.ExplorerActivityPick" />
<activity android:name=".explorers.ExplorerActivityDrop" />
<activity
android:name=".OpenActivity"
android:parentActivityName=".MainActivity"
android:screenOrientation="nosensor">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:label="@string/share_menu_label">
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<activity
android:name=".CreateActivity"
android:parentActivityName=".MainActivity"
android:screenOrientation="nosensor" />
<activity
android:name=".ChangePasswordActivity"
android:parentActivityName=".MainActivity"
android:screenOrientation="nosensor" />
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".file_viewers.ImageViewer"
android:configChanges="screenSize|orientation" /> <!-- don't reload content on configuration change -->
<activity
android:name=".file_viewers.VideoPlayer"
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=".SettingsActivity" android:label="@string/title_activity_settings"/>
<activity android:name=".add_volume.AddVolumeActivity" android:windowSoftInputMode="adjustResize"/>
<activity android:name=".ChangePasswordActivity" android:windowSoftInputMode="adjustResize"/>
<activity android:name=".explorers.ExplorerActivity"/>
<activity android:name=".explorers.ExplorerActivityPick"/>
<activity android:name=".explorers.ExplorerActivityDrop"/>
<activity android:name=".file_viewers.ImageViewer" android:configChanges="screenSize|orientation" /> <!-- don't reload content on configuration change -->
<activity android:name=".file_viewers.VideoPlayer" android:configChanges="screenSize|orientation" />
<activity android:name=".file_viewers.PdfViewer" 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=".CameraActivity" android:screenOrientation="nosensor" />
<service android:name=".file_operations.FileOperationService" android:exported="false"/>

6
app/src/main/java/sushi/hardcore/droidfs/BaseActivity.kt

@ -8,13 +8,13 @@ import androidx.preference.PreferenceManager
open class BaseActivity: AppCompatActivity() {
protected lateinit var sharedPrefs: SharedPreferences
protected lateinit var themeValue: String
protected var shouldCheckTheme = true
lateinit var themeValue: String
private var shouldCheckTheme = true
override fun onCreate(savedInstanceState: Bundle?) {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
if (shouldCheckTheme) {
themeValue = sharedPrefs.getString("theme", "dark_green")!!
themeValue = sharedPrefs.getString("theme", ConstValues.DEFAULT_THEME_VALUE)!!
when (themeValue) {
"black_green" -> setTheme(R.style.BlackGreen)
"dark_red" -> setTheme(R.style.DarkRed)

160
app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt

@ -10,14 +10,10 @@ import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.util.Size
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.view.WindowManager
import android.view.*
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import android.widget.EditText
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.Toast
@ -29,12 +25,16 @@ import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.video_recording.SeekableWriter
import sushi.hardcore.droidfs.video_recording.VideoCapture
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import sushi.hardcore.droidfs.widgets.EditTextDialog
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
@ -79,6 +79,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
private var camera: Camera? = null
private var resolutions: List<Size>? = null
private var currentResolutionIndex: Int = 0
private var currentResolution: Size? = null
private var captureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
private var isBackCamera = true
private var isInVideoMode = false
@ -91,6 +92,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
binding = ActivityCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
gocryptfsVolume = GocryptfsVolume(applicationContext, intent.getIntExtra("sessionID", -1))
outputDirectory = intent.getStringExtra("path")!!
@ -139,7 +141,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
if (newCaptureMode != captureMode) {
captureMode = newCaptureMode
binding.imageCaptureMode.setImageResource(resId)
setupCamera()
if (!isInVideoMode) {
cameraProvider.unbind(imageCapture)
refreshImageCapture()
cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture)
}
}
dialog.dismiss()
}
@ -151,38 +157,26 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.choose_resolution)
.setSingleChoiceItems(it.map { size -> size.toString() }.toTypedArray(), currentResolutionIndex) { dialog, which ->
setupCamera(resolutions!![which])
dialog.dismiss()
currentResolution = resolutions!![which]
currentResolutionIndex = which
setupCamera()
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}
binding.imageTimer.setOnClickListener {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
dialogEditText.inputType = InputType.TYPE_CLASS_NUMBER
val dialog = CustomAlertDialogBuilder(this, themeValue)
.setView(dialogEditTextView)
.setTitle(getString(R.string.enter_timer_duration))
.setPositiveButton(R.string.ok) { _, _ ->
val enteredValue = dialogEditText.text.toString()
if (enteredValue.isEmpty()){
Toast.makeText(this, getString(R.string.timer_empty_error_msg), Toast.LENGTH_SHORT).show()
} else {
timerDuration = enteredValue.toInt()
}
with (EditTextDialog(this, R.string.enter_timer_duration) {
try {
timerDuration = it.toInt()
} catch (e: NumberFormatException) {
Toast.makeText(this, R.string.invalid_number, Toast.LENGTH_SHORT).show()
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
timerDuration = dialogEditText.text.toString().toInt()
dialog.dismiss()
true
}) {
binding.dialogEditText.inputType = InputType.TYPE_CLASS_NUMBER
show()
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
binding.imageFlash.setOnClickListener {
binding.imageFlash.setImageResource(if (isInVideoMode) {
@ -217,6 +211,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
}
binding.imageModeSwitch.setOnClickListener {
isInVideoMode = !isInVideoMode
setupCamera()
binding.imageFlash.setImageResource(if (isInVideoMode) {
binding.recordVideoButton.visibility = View.VISIBLE
binding.takePhotoButton.visibility = View.GONE
@ -226,7 +221,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION_REQUEST_CODE)
}
}
binding.imageModeSwitch.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.icon_photo)?.also {
binding.imageModeSwitch.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.icon_photo)?.mutate()?.also {
it.setTint(ContextCompat.getColor(this, R.color.neutralIconTint))
})
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
@ -252,6 +247,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
}
true
}
resolutions = null
setupCamera()
}
binding.takePhotoButton.onClick = ::onClickTakePhoto
@ -314,52 +310,67 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
private fun adaptPreviewSize(resolution: Size) {
val screenWidth = resources.displayMetrics.widthPixels
binding.cameraPreview.layoutParams = if (screenWidth < resolution.width) {
RelativeLayout.LayoutParams(
screenWidth,
(resolution.height * (screenWidth.toFloat() / resolution.width)).toInt()
)
} else {
RelativeLayout.LayoutParams(resolution.width, resolution.height)
val screenHeight = resources.displayMetrics.heightPixels
var height = (resolution.height * (screenWidth.toFloat() / resolution.width)).toInt()
var width = screenWidth
if (height > screenHeight) {
width = (width * (screenHeight.toFloat() / height)).toInt()
height = screenHeight
}
binding.cameraPreview.layoutParams = RelativeLayout.LayoutParams(width, height).apply {
addRule(RelativeLayout.CENTER_IN_PARENT)
}
(binding.cameraPreview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT)
}
@SuppressLint("RestrictedApi")
private fun setupCamera(resolution: Size? = null){
if (permissionsGranted && ::extensionsManager.isInitialized && ::cameraProvider.isInitialized) {
imageCapture = ImageCapture.Builder()
.setCaptureMode(captureMode)
.setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO)
.apply {
resolution?.let {
setTargetResolution(it)
}
}
.build()
videoCapture = VideoCapture.Builder().apply {
resolution?.let {
private fun refreshImageCapture() {
imageCapture = ImageCapture.Builder()
.setCaptureMode(captureMode)
.setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO)
.apply {
currentResolution?.let {
setTargetResolution(it)
}
}.build()
}
.build()
}
private fun refreshVideoCapture() {
videoCapture = VideoCapture.Builder().apply {
currentResolution?.let {
setTargetResolution(it)
}
}.build()
}
@SuppressLint("RestrictedApi")
private fun setupCamera() {
if (permissionsGranted && ::extensionsManager.isInitialized && ::cameraProvider.isInitialized) {
cameraSelector = if (isBackCamera){ CameraSelector.DEFAULT_BACK_CAMERA } else { CameraSelector.DEFAULT_FRONT_CAMERA }
if (extensionsManager.isExtensionAvailable(cameraSelector, ExtensionMode.HDR)) {
cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.HDR)
if (extensionsManager.isExtensionAvailable(cameraSelector, ExtensionMode.AUTO)) {
cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.AUTO)
}
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture, videoCapture)
adaptPreviewSize(resolution ?: imageCapture!!.attachedSurfaceResolution!!.swap())
val currentUseCase = (if (isInVideoMode) {
refreshVideoCapture()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, videoCapture)
videoCapture
} else {
refreshImageCapture()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture)
imageCapture
})!!
adaptPreviewSize(currentResolution ?: currentUseCase.attachedSurfaceResolution!!.swap())
if (resolutions == null) {
val info = Camera2CameraInfo.from(camera!!.cameraInfo)
val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
val characteristics = cameraManager.getCameraCharacteristics(info.cameraId)
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { streamConfigurationMap ->
resolutions = streamConfigurationMap.getOutputSizes(imageCapture!!.imageFormat).map { it.swap() }
resolutions = streamConfigurationMap.getOutputSizes(currentUseCase.imageFormat).map { it.swap() }
}
}
}
@ -378,17 +389,17 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
if (timerDuration > 0){
binding.textTimer.visibility = View.VISIBLE
isWaitingForTimer = true
Thread{
lifecycleScope.launch {
for (i in timerDuration downTo 1){
runOnUiThread { binding.textTimer.text = i.toString() }
Thread.sleep(1000)
binding.textTimer.text = i.toString()
delay(1000)
}
runOnUiThread {
if (!isFinishing) {
action()
binding.textTimer.visibility = View.GONE
}
isWaitingForTimer = false
}.start()
}
} else {
action()
}
@ -499,21 +510,24 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
}
override fun onOrientationChange(newOrientation: Int) {
val reversedOrientation = when (newOrientation){
90 -> 270
270 -> 90
else -> newOrientation
}.toFloat()
val realOrientation = when (newOrientation) {
Surface.ROTATION_0 -> 0f
Surface.ROTATION_90 -> 90f
Surface.ROTATION_180 -> 180f
else -> 270f
}
val rotateAnimation = RotateAnimation(previousOrientation, when {
reversedOrientation - previousOrientation > 180 -> reversedOrientation - 360
reversedOrientation - previousOrientation < -180 -> reversedOrientation + 360
else -> reversedOrientation
realOrientation - previousOrientation > 180 -> realOrientation - 360
realOrientation - previousOrientation < -180 -> realOrientation + 360
else -> realOrientation
}, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
rotateAnimation.duration = 300
rotateAnimation.interpolator = LinearInterpolator()
rotateAnimation.fillAfter = true
orientedIcons.map { it.startAnimation(rotateAnimation) }
previousOrientation = reversedOrientation
previousOrientation = realOrientation
imageCapture?.targetRotation = newOrientation
videoCapture?.setTargetRotation(newOrientation)
}
}

277
app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt

@ -1,195 +1,168 @@
package sushi.hardcore.droidfs
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.AdapterView.OnItemClickListener
import android.text.InputType
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import androidx.lifecycle.lifecycleScope
import sushi.hardcore.droidfs.databinding.ActivityChangePasswordBinding
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File
import java.util.*
class ChangePasswordActivity : VolumeActionActivity() {
private lateinit var savedVolumesAdapter: SavedVolumesAdapter
class ChangePasswordActivity: BaseActivity() {
private lateinit var binding: ActivityChangePasswordBinding
private lateinit var volume: Volume
private lateinit var volumeDatabase: VolumeDatabase
private var fingerprintProtector: FingerprintProtector? = null
private var usfFingerprint: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
volume = intent.getParcelableExtra("volume")!!
binding = ActivityChangePasswordBinding.inflate(layoutInflater)
setContentView(binding.root)
setupLayout()
setupFingerprintStuff()
savedVolumesAdapter = SavedVolumesAdapter(this, themeValue, volumeDatabase)
if (savedVolumesAdapter.count > 0){
binding.savedPathListview.adapter = savedVolumesAdapter
binding.savedPathListview.onItemClickListener = OnItemClickListener { _, _, position, _ ->
val volume = savedVolumesAdapter.getItem(position)
currentVolumeName = volume.name
if (volume.isHidden){
switchHiddenVolume.isChecked = true
editVolumeName.setText(currentVolumeName)
} else {
switchHiddenVolume.isChecked = false
editVolumePath.setText(currentVolumeName)
}
onClickSwitchHiddenVolume()
title = getString(R.string.change_password)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.textVolumeName.text = volume.name
volumeDatabase = VolumeDatabase(this)
usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fingerprintProtector = FingerprintProtector.new(this, themeValue, volumeDatabase)
if (fingerprintProtector != null && volume.encryptedHash != null) {
binding.textCurrentPasswordLabel.visibility = View.GONE
binding.editCurrentPassword.visibility = View.GONE
}
} else {
WidgetUtil.hideWithPadding(binding.savedPathListview)
}
val textWatcher = object: TextWatcher{
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (volumeDatabase.isVolumeSaved(s.toString())){
checkboxRememberPath.isEnabled = false
checkboxRememberPath.isChecked = true
binding.editOldPassword.apply {
if (volumeDatabase.isHashSaved(s.toString())){
text = null
hint = getString(R.string.hash_saved_hint)
isEnabled = false
} else {
hint = null
isEnabled = true
}
}
} else {
checkboxRememberPath.isEnabled = true
binding.editOldPassword.apply {
hint = null
isEnabled = true
}
}
if (!usfFingerprint || fingerprintProtector == null) {
binding.checkboxSavePassword.visibility = View.GONE
}
if (sharedPrefs.getBoolean(ConstValues.PIN_PASSWORDS_KEY, false)) {
arrayOf(binding.editCurrentPassword, binding.editNewPassword, binding.editPasswordConfirm).forEach {
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
}
}
editVolumePath.addTextChangedListener(textWatcher)
editVolumeName.addTextChangedListener(textWatcher)
binding.editNewPasswordConfirm.setOnEditorActionListener { _, _, _ ->
checkVolumePathThenChangePassword()
binding.editPasswordConfirm.setOnEditorActionListener { _, _, _ ->
changeVolumePassword()
true
}
binding.buttonChangePassword.setOnClickListener {
checkVolumePathThenChangePassword()
}
binding.button.setOnClickListener { changeVolumePassword() }
}
fun checkVolumePathThenChangePassword() {
loadVolumePath {
val volumeFile = File(currentVolumePath)
if (!GocryptfsVolume.isGocryptfsVolume(volumeFile)){
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.error)
.setMessage(R.string.error_not_a_volume)
.setPositiveButton(R.string.ok, null)
.show()
} else if (!volumeFile.canWrite()){
errorDirectoryNotWritable(R.string.change_pwd_cant_write_error_msg)
} else {
changePassword()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == android.R.id.home) {
finish()
true
} else super.onOptionsItemSelected(item)
}
private fun showCurrentPasswordInput() {
binding.textCurrentPasswordLabel.visibility = View.VISIBLE
binding.editCurrentPassword.visibility = View.VISIBLE
}
private fun changePassword(givenHash: ByteArray? = null){
val newPassword = binding.editNewPassword.text.toString().toCharArray()
val newPasswordConfirm = binding.editNewPasswordConfirm.text.toString().toCharArray()
private fun changeVolumePassword() {
val newPassword = CharArray(binding.editNewPassword.text.length)
binding.editNewPassword.text.getChars(0, newPassword.size, newPassword, 0)
val newPasswordConfirm = CharArray(binding.editPasswordConfirm.text.length)
binding.editPasswordConfirm.text.getChars(0, newPasswordConfirm.size, newPasswordConfirm, 0)
@SuppressLint("NewApi")
if (!newPassword.contentEquals(newPasswordConfirm)) {
Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
Arrays.fill(newPassword, 0.toChar())
} else {
object : LoadingTask(this, themeValue, R.string.loading_msg_change_password) {
override fun doTask(activity: AppCompatActivity) {
val oldPassword = binding.editOldPassword.text.toString().toCharArray()
var returnedHash: ByteArray? = null
if (checkboxSavePassword.isChecked) {
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
}
var changePasswordImmediately = true
if (givenHash == null) {
var volume: Volume? = null
volumeDatabase.getVolumes().forEach { testVolume ->
if (testVolume.name == currentVolumeName){
volume = testVolume
var changeWithCurrentPassword = true
volume.encryptedHash?.let { encryptedHash ->
volume.iv?.let { iv ->
fingerprintProtector?.let {
changeWithCurrentPassword = false
it.listener = object : FingerprintProtector.Listener {
override fun onHashStorageReset() {
showCurrentPasswordInput()
volume.encryptedHash = null
volume.iv = null
}
}
volume?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
it.hash?.let { hash ->
it.iv?.let { iv ->
currentVolumePath = if (it.isHidden){
PathUtils.pathJoin(filesDir.path, it.name)
} else {
it.name
}
stopTask {
loadPasswordHash(hash, iv, ::changePassword)
}
changePasswordImmediately = false
}
}
override fun onPasswordHashDecrypted(hash: ByteArray) {
changeVolumePassword(newPassword, hash)
}
override fun onPasswordHashSaved() {}
override fun onFailed(pending: Boolean) {
Arrays.fill(newPassword, 0.toChar())
}
}
it.loadPasswordHash(volume.name, encryptedHash, iv)
}
if (changePasswordImmediately) {
if (GocryptfsVolume.changePassword(currentVolumePath, oldPassword, givenHash, newPassword, returnedHash)) {
val volume = Volume(currentVolumeName, switchHiddenVolume.isChecked)
if (volumeDatabase.isHashSaved(currentVolumeName)) {
volumeDatabase.removeHash(volume)
}
}
if (changeWithCurrentPassword) {
changeVolumePassword(newPassword)
}
}
Arrays.fill(newPasswordConfirm, 0.toChar())
}
private fun changeVolumePassword(newPassword: CharArray, givenHash: ByteArray? = null) {
var returnedHash: ByteArray? = null
if (binding.checkboxSavePassword.isChecked) {
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
}
var currentPassword: CharArray? = null
if (givenHash == null) {
currentPassword = CharArray(binding.editCurrentPassword.text.length)
binding.editCurrentPassword.text.getChars(0, currentPassword.size, currentPassword, 0)
}
object : LoadingTask<Boolean>(this, themeValue, R.string.loading_msg_change_password) {
override suspend fun doTask(): Boolean {
val success = GocryptfsVolume.changePassword(volume.getFullPath(filesDir.path), currentPassword, givenHash, newPassword, returnedHash)
if (success) {
if (volumeDatabase.isHashSaved(volume.name)) {
volumeDatabase.removeHash(volume)
}
}
if (currentPassword != null)
Arrays.fill(currentPassword, 0.toChar())
Arrays.fill(newPassword, 0.toChar())
if (givenHash != null)
Arrays.fill(givenHash, 0)
return success
}
}.startTask(lifecycleScope) { success ->
if (success) {
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
if (binding.checkboxSavePassword.isChecked && returnedHash != null) {
fingerprintProtector!!.let {
it.listener = object : FingerprintProtector.Listener {
override fun onHashStorageReset() {
// retry
it.savePasswordHash(volume, returnedHash)
}
if (checkboxRememberPath.isChecked) {
volumeDatabase.saveVolume(volume)
override fun onPasswordHashDecrypted(hash: ByteArray) {}
override fun onPasswordHashSaved() {
Arrays.fill(returnedHash, 0)
finish()
}
if (checkboxSavePassword.isChecked && returnedHash != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
stopTask {
savePasswordHash(returnedHash) {
onPasswordChanged()
}
override fun onFailed(pending: Boolean) {
if (!pending) {
Arrays.fill(returnedHash, 0)
finish()
}
} else {
stopTask { onPasswordChanged() }
}
} else {
stopTask {
CustomAlertDialogBuilder(activity, themeValue)
.setTitle(R.string.error)
.setMessage(R.string.change_password_failed)
.setPositiveButton(R.string.ok, null)
.show()
}
}
it.savePasswordHash(volume, returnedHash)
}
Arrays.fill(oldPassword, 0.toChar())
}
override fun doFinally(activity: AppCompatActivity) {
Arrays.fill(newPassword, 0.toChar())
Arrays.fill(newPasswordConfirm, 0.toChar())
} else {
finish()
}
} else {
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.error)
.setMessage(R.string.change_password_failed)
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
private fun onPasswordChanged(){
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.success_change_password)
.setMessage(R.string.success_change_password_msg)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ -> finish() }
.show()
}
override fun onDestroy() {
super.onDestroy()
Wiper.wipeEditText(binding.editOldPassword)
Wiper.wipeEditText(binding.editNewPassword)
Wiper.wipeEditText(binding.editNewPasswordConfirm)
}
}

71
app/src/main/java/sushi/hardcore/droidfs/ConstValues.kt

@ -3,40 +3,45 @@ 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
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("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
const val PIN_PASSWORDS_KEY = "pin_passwords"
private val FILE_EXTENSIONS = mapOf(
Pair("image", listOf("png", "jpg", "jpeg", "gif", "webp", "bmp", "heic")),
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 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)
}
}

180
app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt

@ -1,180 +0,0 @@
package sushi.hardcore.droidfs
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.droidfs.databinding.ActivityCreateBinding
import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File
import java.util.*
class CreateActivity : VolumeActionActivity() {
private var sessionID = -1
private var isStartingExplorer = false
private lateinit var binding: ActivityCreateBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCreateBinding.inflate(layoutInflater)
setContentView(binding.root)
setupLayout()
setupFingerprintStuff(mayDecrypt = false)
binding.editPasswordConfirm.setOnEditorActionListener { _, _, _ ->
createVolume()
true
}
binding.spinnerXchacha.adapter = ArrayAdapter(
this@CreateActivity,
android.R.layout.simple_spinner_item,
resources.getStringArray(R.array.encryption_cipher)
).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
binding.spinnerXchacha.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (position == 1) {
CustomAlertDialogBuilder(this@CreateActivity, themeValue)
.setTitle(R.string.warning)
.setMessage(R.string.xchacha_warning)
.setPositiveButton(R.string.ok, null)
.show()
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
binding.buttonCreate.setOnClickListener {
createVolume()
}
}
override fun onClickSwitchHiddenVolume() {
super.onClickSwitchHiddenVolume()
if (switchHiddenVolume.isChecked){
CustomAlertDialogBuilder(this, themeValue)
.setTitle(R.string.warning)
.setMessage(R.string.hidden_volume_warning)
.setPositiveButton(R.string.ok, null)
.show()
}
}
fun createVolume() {
loadVolumePath {
val password = binding.editPassword.text.toString().toCharArray()
val passwordConfirm = binding.editPasswordConfirm.text.toString().toCharArray()
if (!password.contentEquals(passwordConfirm)) {
Toast.makeText(this, R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()