Take Photo feature

This commit is contained in:
Hardcore Sushi 2020-08-05 14:06:54 +02:00
parent 03d5b468f2
commit 0bfb952365
48 changed files with 564 additions and 394 deletions

View File

@ -15,7 +15,7 @@ android {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.1.2"
versionName "1.1.3"
ndk {
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
@ -40,15 +40,16 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
implementation "androidx.core:core-ktx:1.3.1"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation 'androidx.sqlite:sqlite:2.1.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.github.clans:fab:1.6.4'
implementation 'com.jaredrummler:cyanea:1.0.2'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.google.android.exoplayer:exoplayer-core:2.11.7'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.7'
implementation "androidx.sqlite:sqlite:2.1.0"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "com.jaredrummler:cyanea:1.0.2"
implementation "com.github.bumptech.glide:glide:4.11.0"
implementation "com.google.android.exoplayer:exoplayer-core:2.11.7"
implementation "com.google.android.exoplayer:exoplayer-ui:2.11.7"
implementation "com.otaliastudios:cameraview:2.6.3"
}

View File

@ -4,25 +4,27 @@
<permission
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
android:protectionLevel="signature"/>
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:name=".ColoredApplication"
android:allowBackup="false"
android:icon="@mipmap/icon_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true">
android:theme="@style/AppTheme">
<activity android:name=".CameraActivity"/>
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".MainActivity"/>
android:parentActivityName=".MainActivity" />
<activity android:name=".explorers.ExplorerActivity" />
<activity android:name=".explorers.ExplorerActivityPick" />
<activity android:name=".explorers.ExplorerActivityDrop" />
@ -34,7 +36,9 @@
<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>
@ -52,19 +56,28 @@
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=".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" />
<provider
android:name=".provider.RestrictedFileProvider"
android:authorities="${applicationId}.temporary_provider"
android:exported="true"
android:writePermission="${applicationId}.WRITE_TEMPORARY_STORAGE"/>
android:writePermission="${applicationId}.WRITE_TEMPORARY_STORAGE" />
</application>
</manifest>
</manifest>

View File

@ -0,0 +1,89 @@
package sushi.hardcore.droidfs
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import com.otaliastudios.cameraview.CameraListener
import com.otaliastudios.cameraview.PictureResult
import com.otaliastudios.cameraview.controls.Facing
import com.otaliastudios.cameraview.controls.Flash
import kotlinx.android.synthetic.main.activity_camera.*
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.MiscUtils
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.ByteArrayInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.*
class CameraActivity : AppCompatActivity() {
companion object {
private val flashModes = listOf(Flash.AUTO, Flash.ON, Flash.OFF)
private const val fileNameRandomMin = 100000
private const val fileNameRandomMax = 999999
private val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss")
private val random = Random()
}
private var currentFlashModeIndex = 0
private lateinit var gocryptfsVolume: GocryptfsVolume
private lateinit var outputDirectory: String
private lateinit var fileName: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
gocryptfsVolume = GocryptfsVolume(intent.getIntExtra("sessionID", -1))
outputDirectory = intent.getStringExtra("path")!!
camera.setLifecycleOwner(this)
camera.addCameraListener(object: CameraListener(){
override fun onPictureTaken(result: PictureResult) {
take_photo_button.onPhotoTaken()
val inputStream = ByteArrayInputStream(result.data)
if (gocryptfsVolume.importFile(inputStream, PathUtils.path_join(outputDirectory, fileName))){
Toast.makeText(applicationContext, getString(R.string.picture_save_success, fileName), Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(applicationContext)
.setTitle(R.string.error)
.setMessage(R.string.picture_save_failed)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ -> finish() }
.show()
}
}
})
take_photo_button.onClick = ::onClickTakePhoto
}
private fun onClickTakePhoto() {
val baseName = "IMG_"+dateFormat.format(Date())+"_"
do {
fileName = baseName+(random.nextInt(fileNameRandomMax-fileNameRandomMin)+fileNameRandomMin)+".jpg"
} while (gocryptfsVolume.pathExists(fileName))
camera.takePicture()
}
fun onClickFlash(view: View) {
currentFlashModeIndex = MiscUtils.incrementIndex(currentFlashModeIndex, flashModes)
camera.flash = flashModes[currentFlashModeIndex]
image_flash.setImageResource(when (camera.flash) {
Flash.AUTO -> R.drawable.icon_flash_auto
Flash.ON -> R.drawable.icon_flash_on
else -> R.drawable.icon_flash_off
})
}
fun onClickCameraSwitch(view: View) {
camera.toggleFacing()
if (camera.facing == Facing.FRONT){
image_camera_switch.setImageResource(R.drawable.icon_camera_back)
} else {
image_camera_switch.setImageResource(R.drawable.icon_camera_front)
Thread {
Thread.sleep(25)
camera.flash = flashModes[currentFlashModeIndex] //refresh flash mode after switching camera
}.start()
}
}
}

View File

@ -113,15 +113,4 @@ class ExplorerElementAdapter(private val context: Context) : BaseAdapter() {
unSelectAll()
this.explorerElements = explorer_elements
}
val currentDirectoryTotalSize: Long
get() {
var totalSize: Long = 0
for (e in explorerElements) {
if (e.isRegularFile) {
totalSize += e.size
}
}
return totalSize
}
}

View File

@ -0,0 +1,32 @@
package sushi.hardcore.droidfs.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.TextView
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ColoredImageView
open class IconTextDialogAdapter(private val context: Context): BaseAdapter() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
lateinit var items: List<List<Any>>
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view: View = convertView ?: inflater.inflate(R.layout.adapter_dialog_icon_text, parent, false)
val text = view.findViewById<TextView>(R.id.text)
text.text = context.getString(items[position][1] as Int)
val icon = view.findViewById<ColoredImageView>(R.id.icon)
icon.setImageDrawable(context.getDrawable(items[position][2] as Int))
return view
}
override fun getItem(position: Int): Any {
return items[position][0] as String
}
override fun getItemId(position: Int): Long { return 0 }
override fun getCount(): Int { return items.size }
}

View File

@ -1,36 +1,24 @@
package sushi.hardcore.droidfs.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.view.marginEnd
import androidx.core.view.setPadding
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ColoredImageView
class OpenAsDialogAdapter(private val context: Context): BaseAdapter() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private val items = listOf(
listOf("image", context.getString(R.string.image), R.drawable.icon_file_image),
listOf("video", context.getString(R.string.video), R.drawable.icon_file_video),
listOf("audio", context.getString(R.string.audio), R.drawable.icon_file_audio),
listOf("text", context.getString(R.string.text), R.drawable.icon_file_text)
class OpenAsDialogAdapter(context: Context) : IconTextDialogAdapter(context) {
private val openAsItems = listOf(
listOf("image", R.string.image, R.drawable.icon_file_image),
listOf("video", R.string.video, R.drawable.icon_file_video),
listOf("audio", R.string.audio, R.drawable.icon_file_audio),
listOf("text", R.string.text, R.drawable.icon_file_text)
)
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view: View = convertView ?: inflater.inflate(R.layout.adapter_dialog_icon_text, parent, false)
val text = view.findViewById<TextView>(R.id.text)
text.text = items[position][1] as String
val icon = view.findViewById<ColoredImageView>(R.id.icon)
icon.setImageDrawable(context.getDrawable(items[position][2] as Int))
return view
init {
items = openAsItems
}
override fun getItem(position: Int): String {
return items[position][0] as String
}
override fun getItemId(position: Int): Long { return 0 }
override fun getCount(): Int { return items.size }
}

View File

@ -13,33 +13,35 @@ import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import sushi.hardcore.droidfs.widgets.NonScrollableColoredBorderListView
import java.util.*
class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferences) : BaseAdapter() {
class SavedVolumesAdapter(val context: Context, private val sharedPrefs: SharedPreferences) : BaseAdapter() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private val saved_volumes_paths: MutableList<String> = ArrayList()
private val shared_prefs_editor: Editor = shared_prefs.edit()
private lateinit var nonScrollableColoredBorderListView: NonScrollableColoredBorderListView
private val savedVolumesPaths: MutableList<String> = ArrayList()
private val sharedPrefsEditor: Editor = sharedPrefs.edit()
init {
val saved_volumes_paths_set = shared_prefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set<String>
for (volume_path in saved_volumes_paths_set) {
saved_volumes_paths.add(volume_path)
val savedVolumesPathsSet = sharedPrefs.getStringSet(ConstValues.saved_volumes_key, HashSet()) as Set<String>
for (volume_path in savedVolumesPathsSet) {
savedVolumesPaths.add(volume_path)
}
}
private fun update_shared_prefs() {
val saved_volumes_paths_set = saved_volumes_paths.toSet()
shared_prefs_editor.remove(ConstValues.saved_volumes_key)
shared_prefs_editor.putStringSet(ConstValues.saved_volumes_key, saved_volumes_paths_set)
shared_prefs_editor.apply()
private fun updateSharedPrefs() {
val savedVolumesPathsSet = savedVolumesPaths.toSet()
sharedPrefsEditor.remove(ConstValues.saved_volumes_key)
sharedPrefsEditor.putStringSet(ConstValues.saved_volumes_key, savedVolumesPathsSet)
sharedPrefsEditor.apply()
}
override fun getCount(): Int {
return saved_volumes_paths.size
return savedVolumesPaths.size
}
override fun getItem(position: Int): String {
return saved_volumes_paths[position]
return savedVolumesPaths[position]
}
override fun getItemId(position: Int): Long {
@ -47,32 +49,35 @@ class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferen
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
if (!::nonScrollableColoredBorderListView.isInitialized){
nonScrollableColoredBorderListView = parent as NonScrollableColoredBorderListView
}
val view: View = convertView ?: inflater.inflate(R.layout.adapter_saved_volume, parent, false)
val volume_name_textview = view.findViewById<TextView>(R.id.volume_name_textview)
val volumeNameTextview = view.findViewById<TextView>(R.id.volume_name_textview)
val currentVolume = getItem(position)
volume_name_textview.text = currentVolume
val delete_imageview = view.findViewById<ImageView>(R.id.delete_imageview)
delete_imageview.setOnClickListener {
val volume_path = saved_volumes_paths[position]
volumeNameTextview.text = currentVolume
val deleteImageview = view.findViewById<ImageView>(R.id.delete_imageview)
deleteImageview.setOnClickListener {
val volumePath = savedVolumesPaths[position]
val dialog = ColoredAlertDialogBuilder(context)
dialog.setTitle(R.string.warning)
if (shared_prefs.getString(volume_path, null) != null){
if (sharedPrefs.getString(volumePath, null) != null){
dialog.setMessage(context.getString(R.string.delete_hash_or_all))
dialog.setPositiveButton(context.getString(R.string.delete_all)) { _, _ ->
saved_volumes_paths.removeAt(position)
shared_prefs_editor.remove(volume_path)
update_shared_prefs()
savedVolumesPaths.removeAt(position)
sharedPrefsEditor.remove(volumePath)
updateSharedPrefs()
refresh(parent)
}
dialog.setNegativeButton(context.getString(R.string.delete_hash)) { _, _ ->
shared_prefs_editor.remove(volume_path)
shared_prefs_editor.apply()
sharedPrefsEditor.remove(volumePath)
sharedPrefsEditor.apply()
}
} else {
dialog.setMessage(context.getString(R.string.ask_delete_volume_path))
dialog.setPositiveButton(R.string.ok) {_, _ ->
saved_volumes_paths.removeAt(position)
update_shared_prefs()
savedVolumesPaths.removeAt(position)
updateSharedPrefs()
refresh(parent)
}
dialog.setNegativeButton(R.string.cancel, null)
@ -86,13 +91,15 @@ class SavedVolumesAdapter(val context: Context, val shared_prefs: SharedPreferen
notifyDataSetChanged()
if (count == 0){
WidgetUtil.hide(parent)
} else {
nonScrollableColoredBorderListView.layoutParams.height = nonScrollableColoredBorderListView.computeHeight()
}
}
fun addVolumePath(volume_path: String) {
if (!saved_volumes_paths.contains(volume_path)) {
saved_volumes_paths.add(volume_path)
update_shared_prefs()
if (!savedVolumesPaths.contains(volume_path)) {
savedVolumesPaths.add(volume_path)
updateSharedPrefs()
}
}
}

View File

@ -23,16 +23,16 @@ import sushi.hardcore.droidfs.ConstValues.Companion.isText
import sushi.hardcore.droidfs.ConstValues.Companion.isVideo
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
import sushi.hardcore.droidfs.adapters.ExplorerElementAdapter
import sushi.hardcore.droidfs.adapters.OpenAsDialogAdapter
import sushi.hardcore.droidfs.file_viewers.AudioPlayer
import sushi.hardcore.droidfs.file_viewers.ImageViewer
import sushi.hardcore.droidfs.file_viewers.TextEditor
import sushi.hardcore.droidfs.file_viewers.VideoPlayer
import sushi.hardcore.droidfs.provider.RestrictedFileProvider
import sushi.hardcore.droidfs.util.ExternalProvider
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.GocryptfsVolume
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
open class BaseExplorerActivity : BaseActivity() {
@ -49,6 +49,7 @@ open class BaseExplorerActivity : BaseActivity() {
}
protected lateinit var explorerElements: MutableList<ExplorerElement>
protected lateinit var explorerAdapter: ExplorerElementAdapter
private var isCreating = true
private var usf_open = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -100,7 +101,7 @@ open class BaseExplorerActivity : BaseActivity() {
explorerAdapter.onItemClick(position)
if (explorerAdapter.selectedItems.isEmpty()) {
if (!wasSelecting) {
val fullPath = explorerElements[position].getFullPath()
val fullPath = explorerElements[position].fullPath
when {
explorerElements[position].isDirectory -> {
setCurrentPath(fullPath)
@ -171,7 +172,15 @@ open class BaseExplorerActivity : BaseActivity() {
explorerAdapter.setExplorerElements(explorerElements)
currentDirectoryPath = path
current_path_text.text = getString(R.string.location, currentDirectoryPath)
total_size_text.text = getString(R.string.total_size, PathUtils.formatSize(explorerAdapter.currentDirectoryTotalSize))
Thread{
var totalSize: Long = 0
for (e in gocryptfsVolume.recursiveMapFiles(currentDirectoryPath)){
if (e.isRegularFile){
totalSize += e.size
}
}
total_size_text.text = getString(R.string.total_size, PathUtils.formatSize(totalSize))
}.start()
}
private fun askCloseVolume() {
@ -222,8 +231,7 @@ open class BaseExplorerActivity : BaseActivity() {
}
}
fun onClickAddFolder(view: View?) {
findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true)
protected fun openDialogCreateFolder() {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialogBuilder(this)
@ -245,7 +253,7 @@ open class BaseExplorerActivity : BaseActivity() {
dialog.show()
}
fun rename(old_name: String, new_name: String){
protected fun rename(old_name: String, new_name: String){
if (new_name.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else {
@ -262,7 +270,7 @@ open class BaseExplorerActivity : BaseActivity() {
}
}
fun handleMenuItems(menu: Menu){
protected fun handleMenuItems(menu: Menu){
menu.findItem(R.id.rename).isVisible = false
if (usf_open){
menu.findItem(R.id.external_open)?.isVisible = false
@ -350,6 +358,10 @@ open class BaseExplorerActivity : BaseActivity() {
override fun onResume() {
super.onResume()
ExternalProvider.removeFiles(this)
if (isCreating){
isCreating = false
} else {
ExternalProvider.removeFiles(this)
}
}
}

View File

@ -10,11 +10,10 @@ import android.view.WindowManager
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.github.clans.fab.FloatingActionButton
import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.activity_explorer.*
import sushi.hardcore.droidfs.CameraActivity
import sushi.hardcore.droidfs.OpenActivity
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter
import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
@ -59,43 +58,71 @@ class ExplorerActivity : BaseExplorerActivity() {
}
}
fun onClickCreateFile(view: View) {
findViewById<FloatingActionMenu>(R.id.fam_explorer).close(true)
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView)
.setTitle(getString(R.string.enter_file_name))
.setPositiveButton(R.string.ok) { _, _ ->
val fileName = dialogEditText.text.toString()
createNewFile(fileName)
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
val fileName = dialogEditText.text.toString()
dialog.dismiss()
createNewFile(fileName)
true
fun onClickFAB(view: View) {
if (modeSelectLocation){
openDialogCreateFolder()
} else {
val adapter = IconTextDialogAdapter(this)
adapter.items = listOf(
listOf("importFromOtherVolumes", R.string.import_from_other_volume, R.drawable.icon_transfert),
listOf("importFiles", R.string.import_files, R.drawable.icon_encrypt),
listOf("createFile", R.string.new_file, R.drawable.icon_file_unknown),
listOf("createFolder", R.string.mkdir, R.drawable.icon_folder),
listOf("takePhoto", R.string.take_photo, R.drawable.icon_camera)
)
ColoredAlertDialogBuilder(this)
.setSingleChoiceItems(adapter, -1){ thisDialog, which ->
when (adapter.getItem(which)){
"importFromOtherVolumes" -> {
val intent = Intent(this, OpenActivity::class.java)
intent.action = "pick"
startActivityForResult(intent, PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE)
}
"importFiles" -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(intent, PICK_FILES_REQUEST_CODE)
}
"createFile" -> {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView)
.setTitle(getString(R.string.enter_file_name))
.setPositiveButton(R.string.ok) { _, _ ->
val fileName = dialogEditText.text.toString()
createNewFile(fileName)
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
val fileName = dialogEditText.text.toString()
dialog.dismiss()
createNewFile(fileName)
true
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
"createFolder" -> {
openDialogCreateFolder()
}
"takePhoto" -> {
val intent = Intent(this, CameraActivity::class.java)
intent.putExtra("path", currentDirectoryPath)
intent.putExtra("sessionID", gocryptfsVolume.sessionID)
startActivity(intent)
}
}
thisDialog.dismiss()
}
.setTitle(getString(R.string.fab_dialog_title))
.setNegativeButton(R.string.cancel, null)
.create()
.show()
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
fun onClickAddFile(view: View?) {
fam_explorer.close(true)
val i = Intent(Intent.ACTION_OPEN_DOCUMENT)
i.type = "*/*"
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
i.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(i, PICK_FILES_REQUEST_CODE)
}
fun onClickAddFileFromOtherVolume(view: View?) {
fam_explorer.close(true)
val intent = Intent(this, OpenActivity::class.java)
intent.action = "pick"
startActivityForResult(intent, PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -323,9 +350,6 @@ class ExplorerActivity : BaseExplorerActivity() {
}
modeSelectLocation = true
unselectAll()
findViewById<FloatingActionButton>(R.id.fab_add_file).visibility = View.GONE
findViewById<FloatingActionButton>(R.id.fab_import_file).visibility = View.GONE
findViewById<FloatingActionButton>(R.id.fab_import_file_from_other_volume).visibility = View.GONE
true
}
R.id.validate -> {
@ -333,11 +357,10 @@ class ExplorerActivity : BaseExplorerActivity() {
override fun doTask(activity: AppCompatActivity) {
var failedItem: String? = null
for (element in filesToCopy) {
val originalPath = element.getFullPath()
failedItem = if (element.isDirectory) {
recursiveCopyDirectory(originalPath, currentDirectoryPath)
recursiveCopyDirectory(element.fullPath, currentDirectoryPath)
} else {
if (copyFile(originalPath, PathUtils.path_join(currentDirectoryPath, element.name))) null else originalPath
if (copyFile(element.fullPath, PathUtils.path_join(currentDirectoryPath, element.name))) null else element.fullPath
}
if (failedItem != null) {
stopTask {
@ -385,7 +408,7 @@ class ExplorerActivity : BaseExplorerActivity() {
R.id.share -> {
val paths: MutableList<String> = ArrayList()
for (i in explorerAdapter.selectedItems) {
paths.add(explorerElements[i].getFullPath())
paths.add(explorerElements[i].fullPath)
}
ExternalProvider.share(this, gocryptfsVolume, paths)
unselectAll()
@ -403,9 +426,6 @@ class ExplorerActivity : BaseExplorerActivity() {
private fun cancelCopy() {
if (modeSelectLocation){
modeSelectLocation = false
findViewById<FloatingActionButton>(R.id.fab_add_file).visibility = View.VISIBLE
findViewById<FloatingActionButton>(R.id.fab_import_file).visibility = View.VISIBLE
findViewById<FloatingActionButton>(R.id.fab_import_file_from_other_volume).visibility = View.VISIBLE
filesToCopy.clear()
}
}
@ -457,15 +477,14 @@ class ExplorerActivity : BaseExplorerActivity() {
}
}
for (e in mappedElements) {
val srcPath = e.getFullPath()
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, srcPath))
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath))
if (e.isDirectory) {
if (!gocryptfsVolume.mkdir(dstPath)){
return srcPath
return e.fullPath
}
} else {
if (!copyFile(srcPath, dstPath)) {
return srcPath
if (!copyFile(e.fullPath, dstPath)) {
return e.fullPath
}
}
}
@ -506,15 +525,14 @@ class ExplorerActivity : BaseExplorerActivity() {
}
}
for (e in mappedElements) {
val srcPath = e.getFullPath()
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, srcPath))
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath))
if (e.isDirectory) {
if (!gocryptfsVolume.mkdir(dstPath)){
return srcPath
return e.fullPath
}
} else {
if (!importFileFromOtherVolume(remote_gocryptfsVolume, srcPath, dstPath)) {
return srcPath
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, dstPath)) {
return e.fullPath
}
}
}

View File

@ -4,6 +4,7 @@ import android.content.Intent
import android.net.Uri
import android.view.Menu
import android.view.MenuItem
import android.view.View
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
@ -13,6 +14,10 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
setContentView(R.layout.activity_explorer_drop)
}
fun onClickFAB(view: View) {
openDialogCreateFolder()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer_drop, menu)
handleMenuItems(menu)

View File

@ -5,6 +5,7 @@ import java.util.*
class ExplorerElement(val name: String, val elementType: Short, val size: Long, mtime: Long, private val parentPath: String) {
val mTime = Date((mtime * 1000).toString().toLong())
val fullPath: String = PathUtils.path_join(parentPath, name)
val isDirectory: Boolean
get() = elementType.toInt() == 0
@ -15,15 +16,11 @@ class ExplorerElement(val name: String, val elementType: Short, val size: Long,
val isRegularFile: Boolean
get() = elementType.toInt() == 1
fun getFullPath(): String {
return PathUtils.path_join(parentPath, name)
}
companion object {
fun sortBy(sortOrder: String, explorerElements: MutableList<ExplorerElement>) {
when (sortOrder) {
"name" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o1.name.compareTo(o2.name) })
explorerElements.sortWith(Comparator { o1, o2 -> o1.fullPath.compareTo(o2.fullPath) })
}
"size" -> {
explorerElements.sortWith(Comparator { o1, o2 -> (o1.size - o2.size).toInt() })
@ -32,7 +29,7 @@ class ExplorerElement(val name: String, val elementType: Short, val size: Long,
explorerElements.sortWith(Comparator { o1, o2 -> o1.mTime.compareTo(o2.mTime) })
}
"name_desc" -> {
explorerElements.sortWith(Comparator { o1, o2 -> o2.name.compareTo(o1.name) })
explorerElements.sortWith(Comparator { o1, o2 -> o2.fullPath.compareTo(o1.fullPath) })
}
"size_desc" -> {
explorerElements.sortWith(Comparator { o1, o2 -> (o2.size - o1.size).toInt() })

View File

@ -13,11 +13,8 @@ import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import kotlinx.android.synthetic.main.activity_image_viewer.*
import sushi.hardcore.droidfs.ConstValues
import sushi.hardcore.droidfs.R
<<<<<<< HEAD
=======
import sushi.hardcore.droidfs.util.MiscUtils
import sushi.hardcore.droidfs.explorers.ExplorerElement
>>>>>>> ccc453a... Sorted image swipe & ExplorerViewModel
import sushi.hardcore.droidfs.util.PathUtils
import java.security.MessageDigest
import kotlin.math.abs
@ -66,26 +63,18 @@ class ImageViewer: FileViewerActivity() {
sortOrder = intent.getStringExtra("sortOrder") ?: "name"
ExplorerElement.sortBy(sortOrder, mappedImages)
for ((i, e) in mappedImages.withIndex()){
if (filePath == e.getFullPath()){
if (filePath == e.fullPath){
currentMappedImageIndex = i
}
}
wasMapped = true
}
if (deltaX < 0){
if (currentMappedImageIndex == mappedImages.size-1){
currentMappedImageIndex = 0
} else {
currentMappedImageIndex += 1
}
currentMappedImageIndex = if (deltaX < 0){
MiscUtils.incrementIndex(currentMappedImageIndex, mappedImages)
} else {
if (currentMappedImageIndex == 0){
currentMappedImageIndex = mappedImages.size-1
} else {
currentMappedImageIndex -= 1
}
MiscUtils.decrementIndex(currentMappedImageIndex, mappedImages)
}
loadWholeFile(mappedImages[currentMappedImageIndex].getFullPath())?.let {
loadWholeFile(mappedImages[currentMappedImageIndex].fullPath)?.let {
glideImage = Glide.with(this).load(it)
glideImage.into(image_viewer)
}

View File

@ -84,6 +84,7 @@ class RestrictedFileProvider: ContentProvider() {
Wiper.wipe(file)
}
}
dbHelper?.close()
context.deleteDatabase(DB_NAME)
}
@ -134,28 +135,26 @@ class RestrictedFileProvider: ContentProvider() {
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
var resultCursor: MatrixCursor? = null
val temporaryFile = getFileFromUri(uri)
temporaryFile?.let{
val fileName =
dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_NAME), TemporaryFileColumns.COLUMN_UUID + "=?", arrayOf(uri.lastPathSegment), null, null, null)
val fileName = dbHelper?.readableDatabase?.query(TABLE_FILES, arrayOf(TemporaryFileColumns.COLUMN_NAME), TemporaryFileColumns.COLUMN_UUID + "=?", arrayOf(uri.lastPathSegment), null, null, null)
fileName?.let{
if (fileName.moveToNext()) {
val cursor = MatrixCursor(
resultCursor = MatrixCursor(
arrayOf(
MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.SIZE
)
)
cursor.newRow()
resultCursor!!.newRow()
.add(fileName.getString(0))
.add(temporaryFile.length())
fileName.close()
return cursor
}
fileName.close()
}
}
return null
return resultCursor
}
override fun delete(uri: Uri, givenSelection: String?, givenSelectionArgs: Array<String>?): Int {

View File

@ -166,7 +166,7 @@ class GocryptfsVolume(var sessionID: Int) {
result.addAll(explorerElements)
for (e in explorerElements){
if (e.isDirectory){
result.addAll(recursiveMapFiles(e.getFullPath()))
result.addAll(recursiveMapFiles(e.fullPath))
}
}
return result

View File

@ -0,0 +1,18 @@
package sushi.hardcore.droidfs.util
object MiscUtils {
fun incrementIndex(index: Int, list: List<Any>): Int {
var i = index+1
if (i >= list.size){
i = 0
}
return i
}
fun decrementIndex(index: Int, list: List<Any>): Int {
var i = index-1
if (i <= 0){
i = list.size-1
}
return i
}
}

View File

@ -12,12 +12,9 @@ import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R
open class ColoredBorderListView: ListView {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
val background = ContextCompat.getDrawable(context, R.drawable.listview_border) as StateListDrawable
val dcs = background.constantState as DrawableContainer.DrawableContainerState

View File

@ -1,18 +0,0 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatButton
class ColoredButton: AppCompatButton {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
private fun applyColor(){
super.setBackgroundTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context)))
}
}

View File

@ -6,12 +6,9 @@ import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatCheckBox
class ColoredCheckBox: AppCompatCheckBox {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
//constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
super.setButtonTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context)))
}

View File

@ -14,12 +14,9 @@ import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R
class ColoredEditText: AppCompatEditText {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
super.setBackgroundTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context)))
}

View File

@ -1,19 +1,18 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import com.github.clans.fab.FloatingActionButton
import androidx.core.content.ContextCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton
class ColoredFAB: FloatingActionButton {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
val themeColor = ThemeColor.getThemeColor(context)
super.setColorNormal(themeColor)
super.setColorPressed(themeColor)
backgroundTintList = ColorStateList.valueOf(themeColor)
setColorFilter(ContextCompat.getColor(context, android.R.color.white))
}
}

View File

@ -1,19 +0,0 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
import android.util.AttributeSet
import com.github.clans.fab.FloatingActionMenu
class ColoredFAM: FloatingActionMenu {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
private fun applyColor(){
val themeColor = ThemeColor.getThemeColor(context)
super.setMenuButtonColorNormal(themeColor)
super.setMenuButtonColorPressed(themeColor)
}
}

View File

@ -5,12 +5,9 @@ import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageButton
class ColoredImageButton: AppCompatImageButton {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
super.setColorFilter(ThemeColor.getThemeColor(context))
}

View File

@ -5,12 +5,9 @@ import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
class ColoredImageView : AppCompatImageView {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() }
private fun applyColor(){
super.setColorFilter(ThemeColor.getThemeColor(context))
}

View File

@ -1,21 +0,0 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatSeekBar
class ColoredSeekBar : AppCompatSeekBar {
constructor(context: Context) : super(context) {
applyColor()
}
constructor(context: Context, attrs: AttributeSet): super(context, attrs){
applyColor()
}
private fun applyColor(){
val colorFilter = PorterDuffColorFilter(ThemeColor.getThemeColor(context), PorterDuff.Mode.SRC_IN)
super.getProgressDrawable().colorFilter = colorFilter
super.getThumb().colorFilter = colorFilter
}
}

View File

@ -7,17 +7,20 @@ import android.widget.ListAdapter
class NonScrollableColoredBorderListView: ColoredBorderListView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet): super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr)
fun computeHeight(): Int {
var totalHeight = 0
for (i in 0 until adapter.count){
val item = adapter.getView(i, null, this)
item.measure(0, 0)
totalHeight += item.measuredHeight
}
return totalHeight + (dividerHeight * (adapter.count-1))
}
override fun setAdapter(adapter: ListAdapter?) {
super.setAdapter(adapter)
adapter?.let {
var totalHeight = 0
for (i in 0 until adapter.count){
val item = adapter.getView(i, null, this)
item.measure(0, 0)
totalHeight += item.measuredHeight
}
layoutParams.height = totalHeight + (dividerHeight * (adapter.count-1))
}
layoutParams.height = computeHeight()
}
}

View File

@ -0,0 +1,27 @@
package sushi.hardcore.droidfs.widgets
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatImageView
class TakePhotoButton: AppCompatImageView {
constructor(context: Context) : super(context) { init() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { init() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { init() }
lateinit var onClick: ()->Unit
private fun init(){
setOnTouchListener{ _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> onClick()
MotionEvent.ACTION_UP -> isPressed = true
}
true
}
}
fun onPhotoTaken(){
isPressed = false
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/buttonBackgroundColor"/>
<corners android:radius="4dp"/>
</shape>
</item>
</selector>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#666666"/>
<padding
android:left="16dp"
android:right="16dp"
android:top="6dp"
android:bottom="6dp"/>
<corners
android:radius="5dp"/>
</shape>

View File

@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FFFFFF"
android:pathData="M20,40h-10v4h10v4l6,-6 -6,-6v4zM28,40v4h10v-4h-10zM34,0h-20c-2.21,0 -4,1.79 -4,4v28c0,2.21 1.79,4 4,4h20c2.21,0 4,-1.79 4,-4v-28c0,-2.21 -1.79,-4 -4,-4zM23.99,12c-2.21,0 -3.99,-1.79 -3.99,-4s1.78,-4 3.99,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FFFFFF"
android:pathData="M20,40h-10v4h10v4l6,-6 -6,-6v4zM28,40v4h10v-4h-10zM24,16c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -3.99,1.79 -3.99,4c0.01,2.21 1.78,4 3.99,4zM34,0h-20c-2.21,0 -4,1.79 -4,4v28c0,2.21 1.79,4 4,4h20c2.21,0 4,-1.79 4,-4v-28c0,-2.21 -1.79,-4 -4,-4zM14,4h20v21c0,-3.33 -6.67,-5 -10,-5s-10,1.67 -10,5v-21z"/>
</vector>

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="500"
android:viewportHeight="500">
<path
android:fillColor="#FFFFFF"
android:pathData="M131.889,150.061v63.597h-27.256 c-20.079,0-36.343,16.263-36.343,36.342v181.711c0,20.078,16.264,36.34,36.343,36.34h290.734c20.078,0,36.345-16.262,36.345-36.34 V250c0-20.079-16.267-36.342-36.345-36.342h-27.254v-63.597c0-65.232-52.882-118.111-118.112-118.111 S131.889,84.828,131.889,150.061z M177.317,213.658v-63.597c0-40.157,32.525-72.685,72.683-72.685 c40.158,0,72.685,32.528,72.685,72.685v63.597H177.317z M213.658,313.599c0-20.078,16.263-36.341,36.342-36.341 s36.341,16.263,36.341,36.341c0,12.812-6.634,24.079-16.625,30.529c0,0,3.55,21.446,7.542,46.699 c0,7.538-6.087,13.625-13.629,13.625h-27.258c-7.541,0-13.627-6.087-13.627-13.625l7.542-46.699 C220.294,337.678,213.658,326.41,213.658,313.599z" />
<path
android:pathData="m131.889,118.111l0,63.597l-27.256,0c-20.079,0 -36.343,16.263 -36.343,36.342l0,181.711c0,20.078 16.264,36.34 36.343,36.34l290.734,0c20.078,0 36.345,-16.262 36.345,-36.34l0,-181.711c0,-20.079 -16.267,-36.342 -36.345,-36.342l-27.254,0l0,-63.597c0,-65.232 -52.882,-118.111 -118.112,-118.111s-118.112,52.878 -118.112,118.111zM177.317,181.708l0,-63.597c0,-40.157 32.525,-72.685 72.683,-72.685c40.158,0 72.685,32.528 72.685,72.685l0,63.597l-145.368,0zM213.658,281.649c0,-20.078 16.263,-36.341 36.342,-36.341s36.341,16.263 36.341,36.341c0,12.812 -6.634,24.079 -16.625,30.529c0,0 3.55,21.446 7.542,46.699c0,7.538 -6.087,13.625 -13.629,13.625l-27.258,0c-7.541,0 -13.627,-6.087 -13.627,-13.625l7.542,-46.699c-9.992,-6.45 -16.628,-17.718 -16.628,-30.529z"
android:fillColor="#FFFFFF"
android:fillType="evenOdd"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,2v12h3v9l7,-12L9,11l4,-9L3,2zM19,2h-2l-3.2,9h1.9l0.7,-2h3.2l0.7,2h1.9L19,2zM16.85,7.65L18,4l1.15,3.65h-2.3z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3.27,3L2,4.27l5,5V13h3v9l3.58,-6.14L17.73,20 19,18.73 3.27,3zM17,10h-4l4,-8H7v2.18l8.46,8.46L17,10z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
</vector>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<solid android:color="#CCCCCC" />
<size android:width="75dp" android:height="75dp" />
<stroke android:width="15dp" android:color="#444444"/>
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="#FFFFFF"/>
<size android:width="75dp" android:height="75dp" />
<stroke android:width="8dp" android:color="#444444"/>
</shape>
</item>
</selector>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".CameraActivity">
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cameraGesturePinch="zoom"
app:cameraPictureFormat="jpeg"
app:cameraAudio="off"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="30dp">
<ImageView
android:id="@+id/image_flash"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon_flash_auto"
android:layout_alignEnd="@id/take_photo_button"
android:layout_marginEnd="120dp"
android:layout_centerVertical="true"
android:onClick="onClickFlash"/>
<sushi.hardcore.droidfs.widgets.TakePhotoButton
android:id="@+id/take_photo_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/take_photo_button"
android:layout_centerInParent="true"/>
<ImageView
android:id="@+id/image_camera_switch"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon_camera_front"
android:layout_alignStart="@id/take_photo_button"
android:layout_marginStart="120dp"
android:layout_centerVertical="true"
android:onClick="onClickCameraSwitch"/>
</RelativeLayout>
</RelativeLayout>

View File

@ -25,7 +25,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/volume_path"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_volume_path"
@ -54,7 +54,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/old_password"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_old_password"
@ -74,7 +74,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/new_password"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_new_password"
@ -94,7 +94,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/new_password_confirmation"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_new_password_confirm"
@ -137,7 +137,7 @@
android:gravity="center"
android:text="@string/create_password_warning"/>
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"

View File

@ -25,7 +25,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/volume_path"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_volume_path"
@ -54,7 +54,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/password"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_password"
@ -74,7 +74,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/password_confirm"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_password_confirm"
@ -109,7 +109,7 @@
android:gravity="center"
android:text="@string/create_password_warning"/>
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -37,53 +36,14 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<sushi.hardcore.droidfs.widgets.ColoredFAM
android:id="@+id/fam_explorer"
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_margin="15dp"
app:menu_labels_style="@style/fab_label">
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:id="@+id/fab_import_file_from_other_volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_size="mini"
app:fab_label="@string/import_from_other_volume"
android:src="@drawable/icon_transfert"
android:onClick="onClickAddFileFromOtherVolume"/>
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:id="@+id/fab_import_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_size="mini"
app:fab_label="@string/import_files"
android:src="@drawable/icon_encrypt"
android:onClick="onClickAddFile"/>
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:id="@+id/fab_add_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_size="mini"
app:fab_label="@string/new_file"
android:src="@drawable/icon_file_unknown"
android:onClick="onClickCreateFile"/>
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:id="@+id/fab_add_folder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_size="mini"
app:fab_label="@string/mkdir"
android:src="@drawable/icon_folder"
android:onClick="onClickAddFolder"
tools:ignore="OnClick" />
</sushi.hardcore.droidfs.widgets.ColoredFAM>
android:src="@drawable/icon_add"
android:onClick="onClickFAB"/>
</RelativeLayout>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -35,25 +34,14 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<sushi.hardcore.droidfs.widgets.ColoredFAM
android:id="@+id/fam_explorer"
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_margin="15dp"
app:menu_labels_style="@style/fab_label">
<sushi.hardcore.droidfs.widgets.ColoredFAB
android:id="@+id/fab_add_folder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_size="mini"
app:fab_label="@string/mkdir"
android:src="@drawable/icon_folder"
android:onClick="onClickAddFolder"/>
</sushi.hardcore.droidfs.widgets.ColoredFAM>
android:src="@drawable/icon_add"
android:onClick="onClickFAB"/>
</RelativeLayout>

View File

@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/main_activity_button_height"
android:layout_marginHorizontal="@dimen/main_activity_button_hor_margin"
@ -37,7 +37,7 @@
android:text="@string/open_volume"
style="@style/button"/>
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/main_activity_button_height"
android:layout_marginHorizontal="@dimen/main_activity_button_hor_margin"
@ -46,7 +46,7 @@
android:text="@string/create_volume"
style="@style/button"/>
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/main_activity_button_height"
android:layout_marginHorizontal="@dimen/main_activity_button_hor_margin"

View File

@ -25,7 +25,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/volume_path"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_volume_path"
@ -54,7 +54,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/password"
android:textSize="@dimen/edit_text_label_size" />
android:textSize="@dimen/edit_text_label_text_size" />
<sushi.hardcore.droidfs.widgets.ColoredEditText
android:id="@+id/edit_password"
@ -96,7 +96,7 @@
android:gravity="center"
android:text="@string/open_activity_warning"/>
<Button
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="@dimen/action_activity_button_height"
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"

View File

@ -8,7 +8,8 @@
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="20dp"/>
android:layout_marginStart="20dp"
android:layout_marginEnd="5dp"/>
<TextView
android:id="@+id/text"

View File

@ -14,7 +14,7 @@
android:singleLine="true"
android:layout_weight="1"
android:layout_marginStart="50dp"
android:textSize="15dp"/>
android:textSize="15sp"/>
<TextView
android:id="@+id/total_size_text"
@ -23,6 +23,7 @@
android:layout_weight="1"
android:textAlignment="viewEnd"
android:layout_marginEnd="50dp"
android:textSize="15dp"/>
android:textSize="15sp"
android:text="@string/default_total_size"/>
</LinearLayout>

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="main_activity_button_height">70dp</dimen>
<dimen name="main_activity_button_height">60dp</dimen>
<dimen name="main_activity_button_hor_margin">50dp</dimen>
<dimen name="main_activity_button_ver_margin">15dp</dimen>
<dimen name="main_activity_button_ver_margin">20dp</dimen>
<dimen name="create_activity_label_width">100dp</dimen>
<dimen name="edit_text_label_size">15sp</dimen>
<dimen name="edit_text_label_text_size">15sp</dimen>
<dimen name="open_activity_label_width">90dp</dimen>
<dimen name="change_password_activity_label_width">100dp</dimen>
<dimen name="action_activity_button_horizontal_margin">60dp</dimen>
<dimen name="action_activity_button_height">60dp</dimen>
<dimen name="action_activity_button_height">50dp</dimen>
<dimen name="action_activity_listview_margin_horizontal">50dp</dimen>
<dimen name="action_activity_listview_margin_top">20dp</dimen>
<dimen name="warning_msg_padding">20dp</dimen>

View File

@ -6,8 +6,8 @@
<string name="password">Password:</string>
<string name="password_confirm">Password (confirmation):</string>
<string name="volume_path">Volume Path:</string>
<string name="import_files">Import/Encrypt Files</string>
<string name="mkdir">Create Folder</string>
<string name="import_files">Import/Encrypt files</string>
<string name="mkdir">Create folder</string>
<string name="dir_empty">Directory Empty</string>
<string name="warning">Warning !</string>
<string name="ask_close_volume">Are you sure you want to close this volume ?</string>
@ -17,7 +17,7 @@
<string name="error">Error</string>
<string name="error_filename_empty">Please enter a name</string>
<string name="error_mkdir">Folder creation failed.</string>
<string name="success_import">Import Successful !</string>
<string name="success_import">Import successful !</string>
<string name="success_import_msg">The selected files have been successfully imported.</string>
<string name="import_failed">Import of %1$s failed.</string>
<string name="export_failed">Export of %1$s failed.</string>
@ -119,7 +119,7 @@
<string name="discard">Discard</string>
<string name="word_wrap">Word Wrap</string>
<string name="outofmemoryerror_msg">OutOfMemoryError: This file is too large to be loaded in memory.</string>
<string name="new_file">Create New File</string>
<string name="new_file">Create new file</string>
<string name="enter_file_name">File name:</string>
<string name="file_creation_failed">Failed to create the file.</string>
<string name="loading">Loading…</string>
@ -139,4 +139,9 @@
<string name="copy_failed">Copy of %1$s failed.</string>
<string name="copy_success_msg">The selected items have been successfully copied.</string>
<string name="copy_success">Copy successful !</string>
<string name="fab_dialog_title">Add</string>
<string name="take_photo">Take photo</string>
<string name="picture_save_success">Picture saved to %1$s</string>
<string name="picture_save_failed">Failed to save this picture.</string>
<string name="default_total_size">N//A</string>
</resources>

View File

@ -4,14 +4,11 @@
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:colorBackground">@color/backgroundColor</item>
<item name="android:textColor">@color/textColor</item>
<item name="checkboxStyle">@style/checkbox</item>
<!--<item name="checkboxStyle">@style/checkbox</item>-->
<!--<item name="actionOverflowMenuStyle">@style/menu_overflow</item>-->
</style>
<style name="fab_label">
<item name="android:background">@drawable/fab_label_background</item>
</style>
<style name="button" parent="Widget.AppCompat.Button">
<item name="android:backgroundTint">@color/buttonBackgroundColor</item>
<item name="android:background">@drawable/button_background</item>
</style>
<style name="listViewNoDivider">
<item name="android:divider">#000000</item>