Compare commits

...

110 Commits

Author SHA1 Message Date
Hardcore Sushi e6761d1798
Update README & fastlane full_description.txt
2 weeks ago
Hardcore Sushi c434d79c06
Fix video title switching
2 weeks ago
Hardcore Sushi 821c853a22
Hide navigation bar in full screen mode
2 weeks ago
Hardcore Sushi 22b1522192
Optional password fallback
3 weeks ago
Hardcore Sushi 5090a7aa03
Separate color selection & black theme
3 weeks ago
Hardcore Sushi 1a1d3ea570
Multi volume openings
3 weeks ago
Hardcore Sushi 2d165c4a20
Monospace font in text editor
2 months ago
Hardcore Sushi 883874a5ab
Refactoring: Constants & FileTypes
2 months ago
Hardcore Sushi 6e500c23e5
Adaptive icon
2 months ago
Hardcore Sushi a726f7a7d0
Fix storage permission requests
2 months ago
Hardcore Sushi 1e75e9a32f
Clear password fields onStop()
2 months ago
Hardcore Sushi 5e9656970a
Update dependencies
2 months ago
Hardcore Sushi 5dbef99949
Fix EncryptedVolumeDataSource EOF
2 months ago
Hardcore Sushi d2f11c85d1
Android 11 support
2 months ago
Hardcore Sushi 25dbcca854
Update libpdfviewer & Fix FFmpeg build & onBackPressed deprecation in AddVolumeActivity
6 months ago
CyanWolf 545275dabc
Update spanish translation
6 months ago
solokot 077f5cc856
Update russian translation
6 months ago
Muhmmad14333653 2e07ee5333
Update arabic translation
6 months ago
Hardcore Sushi 34aad2596d
Async file loading in file viewers
6 months ago
Hardcore Sushi cdc269f2f7
Add pink themes
6 months ago
Hardcore Sushi 991e435e5e
Fix file viewers navigation bar color in dark mode
6 months ago
Hardcore Sushi 7c2f87109a
Allow to open & create volumes without remembering
6 months ago
Hardcore Sushi 4df1086734
More flexible password change when fingerprint is saved
6 months ago
Hardcore Sushi 7cdfc32c31
Direct encrypted files read/write & More compliant EncryptedVolumeDataSource
6 months ago
Hardcore Sushi 8f5afca823
Update dependencies & Fix some bugs
6 months ago
Hardcore Sushi 11cc15536f
Add FLAG_GRANT_READ_URI_PERMISSION for external opens
7 months ago
Hardcore Sushi 2d19895e6d
Truncate files after possible overwrite
7 months ago
Hardcore Sushi e2539a53b9
Set navigation bar color to background color
9 months ago
Hardcore Sushi 17c32f2144
Show volume type in MainActivity
9 months ago
Hardcore Sushi a5b6de1138
BUILD.md: prefer cloning from the Chapril repository
9 months ago
Hardcore Sushi d1ca164934
Allow changing password of CryFS volumes
9 months ago
Hardcore Sushi 1a21a43f05
Gocryptfs JNI cleanup
9 months ago
Hardcore Sushi 4d164944c1
Deleting files in background
9 months ago
Hardcore Sushi 8709abd7d7
Don't cancel file operations when changing configuration
9 months ago
Hardcore Sushi e01932acda
Allow opening CryFS volumes with password hash
9 months ago
Hardcore Sushi cf4927a90b
CryFS
9 months ago
CyanWolf cb5679515c
Update spanish translation
9 months ago
Muhmmad14333653 a728bd8d24
Update arabic translation
9 months ago
Hardcore Sushi 83dd759f36
Fix local reference table overflow in native_list_dir
10 months ago
Hardcore Sushi 5144947a4a
Media player fixes: better handling of RTL & orientation
11 months ago
Muhmmad14333653 6b52eed9d0
update arabic translation
11 months ago
Muhmmad14333653 2a257d91d0
update Arabic translation
11 months ago
Hardcore Sushi f837556af5
Fix explorer info bar color in dark mode
11 months ago
Hardcore Sushi b7ab267d16
Arabic translation
11 months ago
Hardcore Sushi 5ea0b8ad41
Actually fix camera icon tint bug
11 months ago
Hardcore Sushi ec348383c6
Fix camera icon tint bug
11 months ago
Hardcore Sushi c8d266150c
Fix explorer menu display
11 months ago
CyanWolf 4bbc9360b4
Update Spanish translation
11 months ago
Hardcore Sushi 8aa2be2b05
Update libgocryptfs & Small UX fix
11 months ago
Hardcore Sushi e2248220c4
DroidFS v1.10.0
11 months ago
Hardcore Sushi 7959b20b3f
Update libgocryptfs
11 months ago
solokot 8cebe499f0
Updated Russian translation
11 months ago
cyanwolfg a22b9d8fa8
Update Spanish translation
11 months ago
Hardcore Sushi cba1418417
Fix move operation
11 months ago
Hardcore Sushi b6b8bba666
Save checkbox state if opening volume fails
11 months ago
Hardcore Sushi e00abdf5bb
Switch to Kotlin coroutines
11 months ago
Hardcore Sushi 72cce1d7e1
Show a fingerprint icon when password hash is saved
11 months ago
Hardcore Sushi 55b0ac0daa
Prefill text field with current name when renaming a volume
11 months ago
Hardcore Sushi 53f28e9475
Pin passwords
11 months ago
Hardcore Sushi f1d4b07726
Show total number of selected items
11 months ago
Hardcore Sushi 339309b00d
Fix image viewer rotation handling
12 months ago
Hardcore Sushi e6a1285e0a
Fix camera output rotation
12 months ago
Hardcore Sushi ab48f9219b
CameraActivity: only bind 2 use cases at most + some other fixes
12 months ago
Hardcore Sushi c521c7f998
Open default volume on application startup
12 months ago
Hardcore Sushi 1d13dfbde3
Always show total size & Add some explorer info bar translations
12 months ago
Hardcore Sushi 36ab66fb43
Show number of files & folders in current directory
12 months ago
Hardcore Sushi 1caabc2554
Fix explorer layouts
12 months ago
Hardcore Sushi f541504e07
Refactor RecyclerView adapters
12 months ago
Hardcore Sushi 4de5b41102
Thumbnails cache & Don't do full reload on selection change
12 months ago
Hardcore Sushi 4f9aa55dfe
Explorer grid layout
12 months ago
Hardcore Sushi 91de54018d
Prompt for password if fingerprint authentification fails
12 months ago
Hardcore Sushi 2697eaf11b
Spanish translation
12 months ago
Hardcore Sushi 9e69805ade
Update ptbr translation
1 year ago
Hardcore Sushi 18d0f50094
Update to libpdfviewer 13
1 year ago
Hardcore Sushi e32e106ce3
Fix video player controls
1 year ago
Hardcore Sushi 4608a7a165
Fix MainSettingsFragment crash
1 year ago
Hardcore Sushi 985be2de59
Add HEIC to the image extension list
1 year ago
solokot f07d99efed
Updated Russian translation
1 year ago
Hardcore Sushi 4a55d826d9
Volume renaming
1 year ago
Hardcore Sushi 2ee7a5b871
Allow changing thumbnail max size
1 year ago
Hardcore Sushi 72321b8ec5
Switch to StyledPlayerView
1 year ago
Hardcore Sushi 7226cc8218
Fix UI bug on too long volume paths
1 year ago
Hardcore Sushi 55be5cd0e7
DroidFS v1.9.0
1 year ago
solokot 3c4515e4e9
Update Russian translation
1 year ago
Hardcore Sushi 29eb34e1d5
New screenshots
1 year ago
Hardcore Sushi d6f727a142
Fix error message when creating volume on external SD card
1 year ago
Hardcore Sushi 6d5fc465c7
Fix UI bug on alert dialogs
1 year ago
Hardcore Sushi ed0b5eb483
Add PDF in OpenAs dialog & libpdfviewer dialog crash fix
1 year ago
Hardcore Sushi fd0296f801
Update libpdfviewer
1 year ago
Hardcore Sushi 58391802be
More accurate directory size
1 year ago
Hardcore Sushi e01b5a3098
Volume copy
1 year ago
Hardcore Sushi bea0906f65
Display file name on video player
1 year ago
Hardcore Sushi 71a314b0a0
New home UI
1 year ago
Hardcore Sushi 842667cdee
Update ptbr translation
1 year ago
Hardcore Sushi e5bcc5cfc2
Update dependencies
1 year ago
Hardcore Sushi 32508344fe
Update PGP key
1 year ago
Hardcore Sushi ee3df7c3bf
Icon for PDF files
1 year ago
Hardcore Sushi b18232615d
PDF viewer
1 year ago
Hardcore Sushi 83efc53edc
Update PGP keyserver
1 year ago
Hardcore Sushi f546e64c34
ImageViwer: retrieve screen size with Resources.getSystem().displayMetrics
1 year ago
Hardcore Sushi 822aba9481
Better build explanation
1 year ago
Hardcore Sushi 3007bf756c
Fix video player UI bug
1 year ago
solokot 87cd88232e
Update russian translation
1 year ago
Hardcore Sushi 832fd1d34b
DoubleTapPlayerView
1 year ago
Hardcore Sushi 3ae7e4df70
Remove DialogSingleChoiceAdapter
1 year ago
Hardcore Sushi 55883172a1
Consider WebP as image
1 year ago
Hardcore Sushi b366fa1877
Fill viewport in text editor
1 year ago
Hardcore Sushi 95eed07719
Natural file name sorting
1 year ago
Hardcore Sushi f15b17c936
Fix scroll issue on OpenActivity & ChangePasswordActivity
1 year ago
Hardcore Sushi 5d6f53b37a
Change icon of mode button when switching between photo & video mode
1 year ago

6
.gitmodules vendored

@ -1,3 +1,9 @@
[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
[submodule "app/libcryfs"]
path = app/libcryfs
url = https://forge.chapril.org/hardcoresushi/libcryfs.git

@ -0,0 +1,108 @@
# 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.
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
Install required packages:
```
$ 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://developer.android.com/ndk/downloads) (r23 versions are recommended).
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 libssl-dev
```
The code should be authenticated before being built. To verify the signatures, you will need my PGP key:
```
$ 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>`
# Download sources
Download DroidFS source code:
```
$ git clone --depth=1 https://forge.chapril.org/hardcoresushi/DroidFS.git
```
Verify sources:
```
$ cd DroidFS
$ git verify-commit HEAD
```
__Don't continue if the verification fails!__
Initialize submodules:
```
$ git submodule update --depth=1 --init
```
[FFmpeg](https://ffmpeg.org) is needed to record encrypted video:
```
$ cd app/ffmpeg
$ git clone --depth=1 https://git.ffmpeg.org/ffmpeg.git
```
If you want Gocryptfs support, you need to download OpenSSL:
```
$ cd ../libgocryptfs
$ wget https://www.openssl.org/source/openssl-1.1.1q.tar.gz
```
Verify OpenSSL signature:
```
$ wget https://www.openssl.org/source/openssl-1.1.1q.tar.gz.asc
$ gpg --verify openssl-1.1.1q.tar.gz.asc openssl-1.1.1q.tar.gz
```
Continue **ONLY** if the signature is **VALID**.
```
$ tar -xzf openssl-1.1.1q.tar.gz
```
If you want CryFS support, initialize libcryfs:
```
$ cd app/libcryfs
$ git submodule update --depth=1 --init
```
# Build
Retrieve your Android NDK installation path, usually something like `/home/\<user\>/Android/SDK/ndk/\<NDK version\>`. Then, make it available in your shell:
```
$ export ANDROID_NDK_HOME="<your ndk path>"
```
Start by compiling FFmpeg:
```
$ cd app/ffmpeg
$ ./build.sh ffmpeg
```
## libgocryptfs
This step is only required if you want Gocryptfs support.
```
$ cd app/libgocryptfs
$ OPENSSL_PATH="./openssl-1.1.1q" ./build.sh
```
## Compile APKs
Gradle build libgocryptfs and libcryfs by default.
To build DroidFS without Gocryptfs support, run:
```
$ ./gradlew assembleRelease -PdisableGocryptfs=true
```
To build DroidFS without CryFS support, run:
```
$ ./gradlew assembleRelease -PdisableCryFS=true
```
If you want to build DroidFS with support for both Gocryptfs and CryFS, just run:
```
$ ./gradlew assembleRelease
```
# Sign APKs
If the build succeeds, you will find the unsigned APKs in `app/build/outputs/apk/release/`. These APKs need to be signed in order to be installed on an Android device.
If you don't already have a keystore, you can create a new one by running:
```
$ keytool -genkey -keystore <output file> -alias <key alias> -keyalg EC -validity 10000
```
Then, sign the APK with:
```
$ apksigner sign --out droidfs.apk -v --ks <keystore> app/build/outputs/apk/release/<unsigned apk file>
```
Now you can install `droidfs.apk` on your device.

@ -1,6 +1,8 @@
# DroidFS
DroidFS is an alternative way to use encrypted overlay filesystems on Android that uses its own internal file explorer instead of mounting virtual volumes.
It currently only works with [gocryptfs](https://github.com/rfjakob/gocryptfs) but support for [CryFS](https://github.com/cryfs/cryfs) could be added in the future.
An alternative way to use encrypted virtual filesystems on Android that uses its own internal file explorer instead of mounting volumes.
It currently supports [gocryptfs](https://github.com/rfjakob/gocryptfs) and [CryFS](https://github.com/cryfs/cryfs).
For mortals: An encrypted file manager for Android.
<p align="center">
<img src="https://forge.chapril.org/hardcoresushi/DroidFS/raw/branch/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" height="500">
@ -8,28 +10,35 @@ It currently only works with [gocryptfs](https://github.com/rfjakob/gocryptfs) b
<img src="https://forge.chapril.org/hardcoresushi/DroidFS/raw/branch/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png" height="500">
</p>
# Disclamer
# Disclaimer
DroidFS is provided "as is", without any warranty of any kind.
It shouldn't be considered as an absolute safe way to store files.
DroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.
Do not use this app with volumes containing sensitive data unless you know exactly what you are doing.
# Features
- Compatible with original encrypted volume implementations
- Internal support for video, audio, images, text and PDF files
- Built-in camera to take on-the-fly encrypted photos and videos
- Unlocking volumes using fingerprint authentication
- Volume auto-locking when the app goes in background
# Unsafe features
DroidFS allows you to enable/disable unsafe features to fit your needs between security and comfort.
It is strongly recommended to read the documentation of a feature before enabling it.
Some available features are considered risky and are therefore disabled by default. It is strongly recommended that you read the following documentation if you wish to activate one of these options.
<ul>
<li><h4>Allow screenshots:</h4>
Disable the secure flag of DroidFS activities. This will allow you to take screenshots from the app, but will also allow other apps to record the screen while using DroidFS.
Note: apps with root access don't care about this flag: they can take screenshots or record the screen of any app without any permissions.
</li>
<li><h4>Allow opening files with other applications *:</h4>
<li><h4>Allow opening files with other applications*:</h4>
Decrypt and open file using external apps. These apps could save and send the files thus opened.
</li>
<li><h4>Allow exporting files:</h4>
Decrypt and write file to disk (external storage). Any app with storage permissions could access exported files.
</li>
<li><h4>Allow sharing files via the android share menu *:</h4>
<li><h4>Allow sharing files via the android share menu*:</h4>
Decrypt and share file with other apps. These apps could save and send the files thus shared.
</li>
<li><h4>Keep volume open when the app goes in background:</h4>
@ -46,12 +55,12 @@ It is strongly recommended to read the documentation of a feature before enablin
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">
</a>
You can download DroidFS from [F-Droid](https://f-droid.org/packages/sushi.hardcore.droidfs) or from the "Releases" section in the repo.
You can download DroidFS from [F-Droid](https://f-droid.org/packages/sushi.hardcore.droidfs) or from the "Releases" section in this repository.
APKs availables here are signed with my PGP key available on keyservers:
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 !__
@ -65,17 +74,17 @@ __Don't install the APK if the checksums don't match!__
F-Droid APKs should be signed with the F-Droid key. More details [here](https://f-droid.org/docs/Release_Channels_and_Signing_Keys).
# Permissions
DroidFS need some permissions to work properly. Here is why:
DroidFS needs some permissions for certain features. However, you are free to deny them if you do not wish to use these features.
<ul>
<li><h4>Read & write access to shared storage:</h4>
Required for creating, opening and modifying volumes and for importing/exporting files to/from volumes.
Required to access volumes located on shared storage.
</li>
<li><h4>Biometric/Fingerprint hardware:</h4>
Required to encrypt/decrypt password hashes using a fingerprint protected key.
</li>
<li><h4>Camera:</h4>
Needed to take photos & videos directly encrypted inside DroidFS. You can deny this permission if you don't want to use it.
Required to take encrypted photos or videos directly from the app.
</li>
<li><h4>Record audio:</h4>
Required if you want sound on video recorded with DroidFS.
@ -83,75 +92,24 @@ DroidFS need some permissions to work properly. Here is why:
</ul>
# Limitations
DroidFS use some parts of the original gocryptfs code, which is designed to run on Linux x86 systems: it accesses the underlying file system with file paths and syscalls. However in Android, you can't access other apps files with file paths. Instead, you must use the [ContentProvider](https://developer.android.com/guide/topics/providers/content-providers) API. And obviously, the original gocryptfs code doesn't work with this API. This is why DroidFS can't open volumes provided by other applications, such as cloud storage clients. You can only use DroidFS with volumes located on shared storage or in the app's internal storage (hidden volumes). External storage such as SD cards are only supported in read-only access for now.
# Build
Most of the original gocryptfs code was used as is (written in Go) and compiled to native code. That's why you need [Go](https://golang.org) and the [Android Native Development Kit (NDK)](https://developer.android.com/ndk/) to build DroidFS from source.
DroidFS works as a wrapper around modified versions of the original encrypted container implementations ([libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs) and [libcryfs](https://forge.chapril.org/hardcoresushi/libcryfs)). These programs were designed to run on standard x86 Linux systems: they access the underlying file system with file paths and syscalls. However, on Android, you can't access files from other applications using file paths. Instead, one has to use the [ContentProvider](https://developer.android.com/guide/topics/providers/content-providers) API. Obviously, neither Gocryptfs nor CryFS support this API. As a result, DroidFS cannot open volumes provided by other applications (such as cloud storage clients), nor can it allow other applications to access encrypted volumes once opened.
Due to Android's storage restrictions, encrypted volumes located on SD cards must be placed under `/Android/data/sushi.hardcore.droidfs/` if you want DroidFS to be able to modify them.
#### Install Requirements
- [Android Studio](https://developer.android.com/studio/)
- [Android NDK and CMake](https://developer.android.com/studio/projects/install-ndk) (OpenSSL build fails with NDK versions higher than v22. It should pass with NDK v21.4.7075529 and lower)
- [Go](https://golang.org/doc/install) (on debian: `$ sudo apt-get install golang-go`)
#### Download Sources
```
$ git clone https://github.com/hardcore-sushi/DroidFS.git
```
Download [libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs):
```
$ cd DroidFS
$ git submodule update --init
```
libgocryptfs needs OpenSSL:
```
$ cd app/libgocryptfs
$ wget https://www.openssl.org/source/openssl-1.1.1m.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
```
Continue **ONLY** if the signature is **VALID**.
```
$ tar -xvzf openssl-1.1.1m.tar.gz
```
DroidFS also need [FFmpeg](https://ffmpeg.org) to record encrypted video:
```
$ cd app/ffmpeg
$ git clone --depth=1 https://git.ffmpeg.org/ffmpeg.git
```
#### Build
First, we need to install some dependencies:
```
$ sudo apt-get install libcrypto++-dev libssl-dev pkg-config
```
And also Go dependencies:
```
$ go get golang.org/x/sys/unix golang.org/x/sys/cpu golang.org/x/crypto/hkdf github.com/jacobsa/crypto/siv github.com/rfjakob/eme
```
Then, retrieve your Android NDK installation path, usually someting like "/home/\<user\>/Android/SDK/ndk/\<NDK version\>". We can now build libgocryptfs:
```
$ cd DroidFS/app/libgocryptfs
$ env ANDROID_NDK_HOME="<your ndk path>" OPENSSL_PATH="./openssl-1.1.1m" ./build.sh
```
And also FFmpeg:
```
$ cd app/ffmpeg
$ env ANDROID_NDK_HOME="<your ndk path>" ./build.sh ffmpeg
```
Then, open the DroidFS project with Android Studio. \
If a device (virtual or physical) is connected, just click on "Run". \
If you want to generate a signed APK, you can follow this [post](https://stackoverflow.com/a/28938286).
# Building from source
You can follow the instructions in [BUILD.md](BUILD.md) to build DroidFS from source.
# Third party code
Thanks to these open source projects that DroidFS uses:
### Modified code:
- [gocryptfs](https://github.com/rfjakob/gocryptfs) to encrypt your data
- Encrypted filesystems (to protect your data):
- [libgocryptfs](https://forge.chapril.org/hardcoresushi/libgocryptfs) (forked from [gocryptfs](https://github.com/rfjakob/gocryptfs))
- [libcryfs](https://forge.chapril.org/hardcoresushi/libcryfs) (forked from [CryFS](https://github.com/cryfs/cryfs))
- [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
### Libraries:
- [Cyanea](https://github.com/jaredrummler/Cyanea) to customize UI
- [Glide](https://github.com/bumptech/glide/) to display pictures
- [Glide](https://github.com/bumptech/glide) to display pictures
- [ExoPlayer](https://github.com/google/ExoPlayer) to play media files

@ -1,27 +1,27 @@
cmake_minimum_required(VERSION 3.10)
add_library(
gocryptfs
SHARED
IMPORTED
)
project(DroidFS)
set_target_properties(
gocryptfs
PROPERTIES IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/libgocryptfs/build/${ANDROID_ABI}/libgocryptfs.so
)
option(GOCRYPTFS "build libgocryptfs" ON)
option(CRYFS "build libcryfs" ON)
add_library(
gocryptfs_jni
SHARED
src/main/native/gocryptfs_jni.c
)
if (GOCRYPTFS)
add_library(gocryptfs SHARED IMPORTED)
set_target_properties(
gocryptfs
PROPERTIES IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/libgocryptfs/build/${ANDROID_ABI}/libgocryptfs.so
)
add_library(gocryptfs_jni SHARED src/main/native/gocryptfs_jni.c)
target_include_directories(gocryptfs_jni PRIVATE ${PROJECT_SOURCE_DIR}/libgocryptfs/build/${ANDROID_ABI})
target_link_libraries(gocryptfs_jni gocryptfs)
endif()
target_link_libraries(
gocryptfs_jni
gocryptfs
)
if (CRYFS)
add_subdirectory(${PROJECT_SOURCE_DIR}/libcryfs)
add_library(cryfs_jni SHARED src/main/native/libcryfs.c)
target_link_libraries(cryfs_jni libcryfs-jni)
endif()
add_library(
avformat
@ -65,14 +65,11 @@ add_library(
src/main/native/libmux.c
)
target_include_directories(mux PRIVATE ${PROJECT_SOURCE_DIR}/ffmpeg/build/${ANDROID_ABI})
target_link_libraries(
mux
avformat
avcodec
avutil
)
include_directories(
${PROJECT_SOURCE_DIR}/libgocryptfs/build/${ANDROID_ABI}
${PROJECT_SOURCE_DIR}/ffmpeg/build/${ANDROID_ABI}
)

@ -1,10 +1,23 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
if (hasProperty("disableCryFS")) {
ext.disableCryFS = getProperty("disableCryFS")
} else {
ext.disableCryFS = false
}
if (hasProperty("disableGocryptfs")) {
ext.disableGocryptfs = getProperty("disableGocryptfs")
} else {
ext.disableGocryptfs = false
}
android {
compileSdkVersion 31
buildToolsVersion "31"
ndkVersion "23.1.7779620"
compileSdkVersion 33
buildToolsVersion "33.0.0"
ndkVersion "25.1.8937393"
namespace "sushi.hardcore.droidfs"
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
@ -13,16 +26,26 @@ android {
defaultConfig {
applicationId "sushi.hardcore.droidfs"
minSdkVersion 21
targetSdkVersion 29
versionCode 21
versionName "1.7.1"
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 30
versionCode 29
versionName "2.0.0-alpha2"
ndk {
abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
}
externalNativeBuild.cmake {
if (project.ext.disableGocryptfs) {
arguments "-DGOCRYPTFS=OFF"
}
if (project.ext.disableCryFS) {
arguments "-DCRYFS=OFF"
}
}
}
if (!file("fdroid").exists()) {
if (!hasProperty("nosplits")) {
splits {
abi {
enable true
@ -33,6 +56,8 @@ android {
applicationVariants.all { variant ->
variant.resValue "string", "versionName", variant.versionName
buildConfigField "boolean", "CRYFS_DISABLED", "${project.ext.disableCryFS}"
buildConfigField "boolean", "GOCRYPTFS_DISABLED", "${project.ext.disableGocryptfs}"
}
buildFeatures {
@ -41,9 +66,13 @@ android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
postprocessing {
removeUnusedCode true
removeUnusedResources true
obfuscate false
optimizeCode true
proguardFiles 'proguard-rules.pro'
}
}
}
@ -55,29 +84,32 @@ 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.core:core-ktx:1.9.0'
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
def lifecycle_version = "2.5.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.sqlite:sqlite-ktx:2.2.0"
implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.sqlite:sqlite-ktx:2.3.0"
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.github.bumptech.glide:glide:4.12.0"
implementation "androidx.biometric:biometric:1.1.0"
implementation 'com.google.android.material:material:1.8.0'
implementation "com.github.bumptech.glide:glide:4.13.2"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
def exoplayer_version = "2.16.1"
def exoplayer_version = "2.18.2"
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.2.0-beta02"
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"
}

@ -1,9 +1,11 @@
#!/bin/bash
if [ -z ${ANDROID_NDK_HOME+x} ]; then
echo "Error: \$ANDROID_NDK_HOME is not defined."
echo "Error: \$ANDROID_NDK_HOME is not defined." >&2
exit 1
elif [ $# -lt 1 ]; then
echo "Usage: $0 <FFmpeg source directory> [<ABI>]"
echo "Usage: $0 <FFmpeg source directory> [<ABI>]" >&2
exit 1
else
FFMPEG_DIR=$1
compile_for_arch() {
@ -61,6 +63,7 @@ else
--disable-sndio \
--disable-schannel \
--disable-securetransport \
--disable-vulkan \
--disable-xlib \
--disable-zlib \
--disable-cuvid \
@ -76,7 +79,8 @@ else
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/libavcodec/*.h $FFMPEG_DIR/libavcodec/libavcodec.so build/$1/libavcodec &&
cp $FFMPEG_DIR/libavutil/*.h $FFMPEG_DIR/libavutil/libavutil.so build/$1/libavutil
cp $FFMPEG_DIR/libavutil/*.h $FFMPEG_DIR/libavutil/libavutil.so build/$1/libavutil ||
exit 1
}
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH

@ -0,0 +1 @@
Subproject commit 43de737624ceeb1c41012d2ea4f0d5dcba8a19e5

@ -1 +1 @@
Subproject commit b232bb782605eb0c6b23aef5f7ea56fd5bd3b7fd
Subproject commit 27232cbdb7257be13a12545b71fa32ee193cb11b

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="sushi.hardcore.droidfs"
android:installLocation="auto">
<permission
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.CAMERA" />
@ -26,65 +26,36 @@
<application
android:allowBackup="false"
android:icon="@mipmap/icon_launcher"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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"
android:windowSoftInputMode="adjustPan">
android:theme="@style/BaseTheme"
android:name=".VolumeManagerApp">
<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"
android:windowSoftInputMode="adjustPan" />
<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"/>
<activity android:name=".file_viewers.VideoPlayer" android:configChanges="screenSize|orientation" />
<activity android:name=".file_viewers.PdfViewer" android:configChanges="screenSize|orientation" android:theme="@style/AppTheme" />
<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"/>

@ -4,50 +4,21 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
open class BaseActivity: AppCompatActivity() {
protected lateinit var sharedPrefs: SharedPreferences
protected lateinit var themeValue: String
protected var shouldCheckTheme = true
protected var applyCustomTheme: Boolean = true
lateinit var theme: Theme
override fun onCreate(savedInstanceState: Bundle?) {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
if (shouldCheckTheme) {
themeValue = sharedPrefs.getString("theme", "dark_green")!!
when (themeValue) {
"black_green" -> setTheme(R.style.BlackGreen)
"dark_red" -> setTheme(R.style.DarkRed)
"black_red" -> setTheme(R.style.BlackRed)
"dark_blue" -> setTheme(R.style.DarkBlue)
"black_blue" -> setTheme(R.style.BlackBlue)
"dark_yellow" -> setTheme(R.style.DarkYellow)
"black_yellow" -> setTheme(R.style.BlackYellow)
"dark_orange" -> setTheme(R.style.DarkOrange)
"black_orange" -> setTheme(R.style.BlackOrange)
"dark_purple" -> setTheme(R.style.DarkPurple)
"black_purple" -> setTheme(R.style.BlackPurple)
}
} else {
shouldCheckTheme = true
}
super.onCreate(savedInstanceState)
if (!sharedPrefs.getBoolean("usf_screenshot", false)){
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
sharedPrefs = (application as VolumeManagerApp).sharedPreferences
theme = Theme.fromSharedPrefs(sharedPrefs)
if (applyCustomTheme) {
setTheme(theme.toResourceId())
}
}
override fun onStart() {
super.onStart()
val newThemeValue = sharedPrefs.getString("theme", "dark_green")!!
onThemeChanged(newThemeValue)
}
fun onThemeChanged(newThemeValue: String) {
if (newThemeValue != themeValue) {
themeValue = newThemeValue
shouldCheckTheme = false
recreate()
if (!sharedPrefs.getBoolean("usf_screenshot", false)) {
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
}
}

@ -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,14 +25,19 @@ import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
import sushi.hardcore.droidfs.util.IntentUtils
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 java.io.*
import sushi.hardcore.droidfs.widgets.EditTextDialog
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executor
@ -60,14 +61,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
binding.imageTimer.setImageResource(R.drawable.icon_timer_off)
}
}
private var usf_keep_open = false
private lateinit var sensorOrientationListener: SensorOrientationListener
private var previousOrientation: Float = 0f
private lateinit var orientedIcons: List<ImageView>
private lateinit var gocryptfsVolume: GocryptfsVolume
private lateinit var encryptedVolume: EncryptedVolume
private lateinit var outputDirectory: String
private var isFinishingIntentionally = false
private var isAskingPermissions = false
private var permissionsGranted = false
private lateinit var executor: Executor
private lateinit var cameraProvider: ProcessCameraProvider
@ -79,6 +77,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
@ -88,17 +87,16 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
binding = ActivityCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
gocryptfsVolume = GocryptfsVolume(applicationContext, intent.getIntExtra("sessionID", -1))
supportActionBar?.hide()
encryptedVolume = IntentUtils.getParcelableExtra(intent, "volume")!!
outputDirectory = intent.getStringExtra("path")!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
permissionsGranted = true
} else {
isAskingPermissions = true
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
}
} else {
@ -125,9 +123,9 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
} else {
1
}
CustomAlertDialogBuilder(this, themeValue)
CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.camera_optimization)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, arrayOf(R.string.maximize_quality, R.string.minimize_latency).map { getString(it) }), currentIndex) { dialog, which ->
.setSingleChoiceItems(arrayOf(getString(R.string.maximize_quality), getString(R.string.minimize_latency)), currentIndex) { dialog, which ->
val resId: Int
val newCaptureMode = if (which == 0) {
resId = R.drawable.icon_high_quality
@ -139,7 +137,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()
}
@ -148,41 +150,29 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
}
binding.imageRatio.setOnClickListener {
resolutions?.let {
CustomAlertDialogBuilder(this, themeValue)
CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.choose_resolution)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, it.map { size -> size.toString() }), currentResolutionIndex) { dialog, which ->
setupCamera(resolutions!![which])
dialog.dismiss()
.setSingleChoiceItems(it.map { size -> size.toString() }.toTypedArray(), currentResolutionIndex) { dialog, which ->
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,20 +207,24 @@ 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
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
isAskingPermissions = true
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION_REQUEST_CODE)
}
}
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
R.drawable.icon_flash_off
} else {
binding.recordVideoButton.visibility = View.GONE
binding.takePhotoButton.visibility = View.VISIBLE
binding.imageModeSwitch.setImageResource(R.drawable.icon_video)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO
R.drawable.icon_flash_auto
})
@ -248,6 +242,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
}
true
}
resolutions = null
setupCamera()
}
binding.takePhotoButton.onClick = ::onClickTakePhoto
@ -282,21 +277,17 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
@RequiresApi(Build.VERSION_CODES.M)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
isAskingPermissions = false
if (grantResults.size == 1) {
when (requestCode) {
CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionsGranted = true
setupCamera()
} else {
CustomAlertDialogBuilder(this, themeValue)
CustomAlertDialogBuilder(this, theme)
.setTitle(R.string.error)
.setMessage(R.string.camera_perm_needed)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ ->
isFinishingIntentionally = true
finish()
}.show()
.setPositiveButton(R.string.ok) { _, _ -> finish() }.show()
}
AUDIO_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (videoCapture != null) {
@ -310,52 +301,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()