Changes For Folder Feature
This commit is contained in:
6
.idea/AndroidProjectSystem.xml
generated
Normal file
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,52 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="devs.org.calculator"
|
package="devs.org.calculator">
|
||||||
>
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="29" />
|
android:maxSdkVersion="29" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_MEDIA_IMAGES"
|
||||||
tools:ignore="SelectedPhotoAccess" />
|
tools:ignore="SelectedPhotoAccess" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_MEDIA_VIDEO"
|
||||||
tools:ignore="SelectedPhotoAccess" />
|
tools:ignore="SelectedPhotoAccess" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".CalculatorApp"
|
android:name=".CalculatorApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Calculator"
|
android:theme="@style/Theme.Calculator"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.DocumentsActivity"
|
android:name=".activities.HiddenActivity"
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".activities.AudioGalleryActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".activities.VideoGalleryActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".activities.ImageGalleryActivity"
|
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.SetupPasswordActivity"
|
android:name=".activities.SetupPasswordActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true" >
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
@@ -58,7 +52,7 @@
|
|||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.PreviewActivity"
|
android:name=".activities.PreviewActivity"
|
||||||
android:configChanges="orientation|screenSize"/>
|
android:configChanges="orientation|screenSize" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
@@ -69,9 +63,6 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package devs.org.calculator.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import devs.org.calculator.R
|
|
||||||
import devs.org.calculator.callbacks.FileProcessCallback
|
|
||||||
import devs.org.calculator.utils.FileManager
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class AudioGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
|
||||||
override val fileType = FileManager.FileType.AUDIO
|
|
||||||
private lateinit var pickAudioLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setupFabButton()
|
|
||||||
|
|
||||||
pickAudioLauncher =
|
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
val clipData = result.data?.clipData
|
|
||||||
val uriList = mutableListOf<Uri>()
|
|
||||||
|
|
||||||
if (clipData != null) {
|
|
||||||
for (i in 0 until clipData.itemCount) {
|
|
||||||
val uri = clipData.getItemAt(i).uri
|
|
||||||
uriList.add(uri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.data?.data?.let { uriList.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriList.isNotEmpty()) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
FileManager(
|
|
||||||
this@AudioGalleryActivity,
|
|
||||||
this@AudioGalleryActivity
|
|
||||||
).processMultipleFiles(uriList, fileType, this@AudioGalleryActivity)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
|
||||||
Toast.makeText(
|
|
||||||
this@AudioGalleryActivity,
|
|
||||||
"${copiedFiles.size} ${getString(R.string.audio_hidded_successfully)} ",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFileProcessFailed() {
|
|
||||||
Toast.makeText(this@AudioGalleryActivity, "Failed to hide Audios", Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFabButton() {
|
|
||||||
binding.fabAdd.setOnClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
type = "audio/*"
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
pickAudioLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openPreview() {
|
|
||||||
// Not implemented audio preview
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package devs.org.calculator.activities
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.view.View
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.IntentSenderRequest
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
|
||||||
import devs.org.calculator.R
|
|
||||||
import devs.org.calculator.adapters.FileAdapter
|
|
||||||
import devs.org.calculator.databinding.ActivityGalleryBinding
|
|
||||||
import devs.org.calculator.utils.FileManager
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class BaseGalleryActivity : AppCompatActivity() {
|
|
||||||
protected lateinit var binding: ActivityGalleryBinding
|
|
||||||
private lateinit var fileManager: FileManager
|
|
||||||
private lateinit var adapter: FileAdapter
|
|
||||||
private lateinit var files: List<File>
|
|
||||||
|
|
||||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
|
||||||
private val storagePermissionLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.RequestMultiplePermissions()
|
|
||||||
) { permissions ->
|
|
||||||
val granted = permissions.values.all { it }
|
|
||||||
if (granted || Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) {
|
|
||||||
loadFiles()
|
|
||||||
} else {
|
|
||||||
showPermissionDeniedDialog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract val fileType: FileManager.FileType
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setupIntentSenderLauncher()
|
|
||||||
binding = ActivityGalleryBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
fileManager = FileManager(this, this)
|
|
||||||
|
|
||||||
when(fileType){
|
|
||||||
FileManager.FileType.IMAGE -> {
|
|
||||||
val image = getString(R.string.add_image)
|
|
||||||
binding.fabAdd.text = image
|
|
||||||
binding.noItemsTxt.text = "${getString(R.string.no_items_available_add_one_by_clicking_on_the_plus_button)} '$image' button"
|
|
||||||
}
|
|
||||||
FileManager.FileType.AUDIO -> {
|
|
||||||
val text = getString(R.string.add_audio)
|
|
||||||
binding.fabAdd.text = text
|
|
||||||
binding.noItemsTxt.text = "${getString(R.string.no_items_available_add_one_by_clicking_on_the_plus_button)} '$text' button"
|
|
||||||
|
|
||||||
}
|
|
||||||
FileManager.FileType.VIDEO -> {
|
|
||||||
val text = getString(R.string.add_video)
|
|
||||||
binding.fabAdd.text = text
|
|
||||||
binding.noItemsTxt.text = "${getString(R.string.no_items_available_add_one_by_clicking_on_the_plus_button)} '$text' button"
|
|
||||||
}
|
|
||||||
FileManager.FileType.DOCUMENT -> {
|
|
||||||
val text = getString(R.string.add_files)
|
|
||||||
binding.fabAdd.text = text
|
|
||||||
binding.noItemsTxt.text = "${getString(R.string.no_items_available_add_one_by_clicking_on_the_plus_button)} '$text' button"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
|
||||||
if (scrollY > oldScrollY && binding.fabAdd.isExtended) {
|
|
||||||
|
|
||||||
binding.fabAdd.shrink()
|
|
||||||
} else if (scrollY < oldScrollY && !binding.fabAdd.isExtended) {
|
|
||||||
|
|
||||||
binding.fabAdd.extend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setupRecyclerView()
|
|
||||||
checkPermissionsAndLoadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupIntentSenderLauncher() {
|
|
||||||
intentSenderLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartIntentSenderForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
|
||||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
|
||||||
adapter = FileAdapter(fileType, this, this)
|
|
||||||
binding.recyclerView.adapter = adapter
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkPermissionsAndLoadFiles() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
if (!Environment.isExternalStorageManager()) {
|
|
||||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
|
||||||
.addCategory("android.intent.category.DEFAULT")
|
|
||||||
.setData(Uri.parse("package:${applicationContext.packageName}"))
|
|
||||||
startActivityForResult(intent, 2296)
|
|
||||||
} else {
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val permissions = arrayOf(
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
)
|
|
||||||
if (permissions.any { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED }) {
|
|
||||||
storagePermissionLauncher.launch(permissions)
|
|
||||||
} else {
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun loadFiles() {
|
|
||||||
files = fileManager.getFilesInHiddenDir(fileType)
|
|
||||||
adapter.submitList(files)
|
|
||||||
if (files.isEmpty()){
|
|
||||||
binding.recyclerView.visibility = View.GONE
|
|
||||||
binding.loading.visibility = View.GONE
|
|
||||||
binding.noItems.visibility = View.VISIBLE
|
|
||||||
}else{
|
|
||||||
binding.recyclerView.visibility = View.VISIBLE
|
|
||||||
binding.loading.visibility = View.GONE
|
|
||||||
binding.noItems.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun openPreview()
|
|
||||||
|
|
||||||
private fun showPermissionDeniedDialog() {
|
|
||||||
// permission denied
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("This method has been deprecated in favor of using the Activity Result API\n which brings increased type safety via an {@link ActivityResultContract} and the prebuilt\n contracts for common intents available in\n {@link androidx.activity.result.contract.ActivityResultContracts}, provides hooks for\n testing, and allow receiving results in separate, testable classes independent from your\n activity. Use\n {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}\n with the appropriate {@link ActivityResultContract} and handling the result in the\n {@link ActivityResultCallback#onActivityResult(Object) callback}.")
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
if (requestCode == 2296 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
if (Environment.isExternalStorageManager()) {
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package devs.org.calculator.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import devs.org.calculator.R
|
|
||||||
import devs.org.calculator.utils.FileManager
|
|
||||||
import devs.org.calculator.callbacks.FileProcessCallback
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class DocumentsActivity : BaseGalleryActivity(), FileProcessCallback {
|
|
||||||
override val fileType = FileManager.FileType.DOCUMENT
|
|
||||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setupFabButton()
|
|
||||||
|
|
||||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
val clipData = result.data?.clipData
|
|
||||||
val uriList = mutableListOf<Uri>()
|
|
||||||
|
|
||||||
if (clipData != null) {
|
|
||||||
for (i in 0 until clipData.itemCount) {
|
|
||||||
val uri = clipData.getItemAt(i).uri
|
|
||||||
uriList.add(uri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriList.isNotEmpty()) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
FileManager(this@DocumentsActivity, this@DocumentsActivity).processMultipleFiles(uriList, fileType,this@DocumentsActivity )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
|
||||||
Toast.makeText(this@DocumentsActivity,"${copiedFiles.size} ${getString(R.string.documents_hidden_successfully )}"
|
|
||||||
, Toast.LENGTH_SHORT).show()
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFileProcessFailed() {
|
|
||||||
Toast.makeText(this@DocumentsActivity,
|
|
||||||
getString(R.string.failed_to_hide_documents), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFabButton() {
|
|
||||||
binding.fabAdd.setOnClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
type = "*/*"
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
pickLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openPreview() {
|
|
||||||
//Not implemented document preview
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,421 @@
|
|||||||
|
package devs.org.calculator.activities
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import devs.org.calculator.R
|
||||||
|
import devs.org.calculator.adapters.FileAdapter
|
||||||
|
import devs.org.calculator.adapters.FolderAdapter
|
||||||
|
import devs.org.calculator.callbacks.DialogActionsCallback
|
||||||
|
import devs.org.calculator.databinding.ActivityHiddenBinding
|
||||||
|
import devs.org.calculator.utils.DialogUtil
|
||||||
|
import devs.org.calculator.utils.FileManager
|
||||||
|
import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR
|
||||||
|
import devs.org.calculator.utils.FolderManager
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
class HiddenActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private var isFabOpen = false
|
||||||
|
private lateinit var fabOpen: Animation
|
||||||
|
private lateinit var fabClose: Animation
|
||||||
|
private lateinit var rotateOpen: Animation
|
||||||
|
private lateinit var rotateClose: Animation
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityHiddenBinding
|
||||||
|
private val fileManager = FileManager(this, this)
|
||||||
|
private val folderManager = FolderManager(this)
|
||||||
|
private val dialogUtil = DialogUtil(this)
|
||||||
|
|
||||||
|
private val STORAGE_PERMISSION_CODE = 101
|
||||||
|
private val PICK_FILE_REQUEST_CODE = 102
|
||||||
|
private var currentFolder: File? = null
|
||||||
|
private var folderAdapter: FolderAdapter? = null
|
||||||
|
val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
binding = ActivityHiddenBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
//initialized animations for fabs
|
||||||
|
fabOpen = AnimationUtils.loadAnimation(this, R.anim.fab_open)
|
||||||
|
fabClose = AnimationUtils.loadAnimation(this, R.anim.fab_close)
|
||||||
|
rotateOpen = AnimationUtils.loadAnimation(this, R.anim.rotate_open)
|
||||||
|
rotateClose = AnimationUtils.loadAnimation(this, R.anim.rotate_close)
|
||||||
|
|
||||||
|
binding.fabExpend.visibility = View.GONE
|
||||||
|
binding.addImage.visibility = View.GONE
|
||||||
|
binding.addVideo.visibility = View.GONE
|
||||||
|
binding.addAudio.visibility = View.GONE
|
||||||
|
binding.addDocument.visibility = View.GONE
|
||||||
|
binding.addFolder.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
binding.fabExpend.setOnClickListener {
|
||||||
|
if (isFabOpen) {
|
||||||
|
closeFabs()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
openFabs()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.addImage.setOnClickListener { openFilePicker("image/*") }
|
||||||
|
binding.addVideo.setOnClickListener { openFilePicker("video/*") }
|
||||||
|
binding.addAudio.setOnClickListener { openFilePicker("audio/*") }
|
||||||
|
binding.addDocument.setOnClickListener { openFilePicker("*/*") }
|
||||||
|
binding.addFolder.setOnClickListener {
|
||||||
|
dialogUtil.createInputDialog(
|
||||||
|
title = "Enter Folder Name To Create",
|
||||||
|
hint = "",
|
||||||
|
callback = object : DialogUtil.InputDialogCallback {
|
||||||
|
override fun onPositiveButtonClicked(input: String) {
|
||||||
|
fileManager.askPermission(this@HiddenActivity)
|
||||||
|
folderManager.createFolder( hiddenDir,input )
|
||||||
|
listFoldersInHiddenDirectory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileManager.askPermission(this)
|
||||||
|
listFoldersInHiddenDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openFilePicker(mimeType: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
type = mimeType
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
}
|
||||||
|
startActivityForResult(intent, PICK_FILE_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
if (requestCode == STORAGE_PERMISSION_CODE) {
|
||||||
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d("HiddenActivity", "READ/WRITE_EXTERNAL_STORAGE permission granted via onRequestPermissionsResult")
|
||||||
|
listFoldersInHiddenDirectory()
|
||||||
|
} else {
|
||||||
|
Log.d("HiddenActivity", "READ/WRITE_EXTERNAL_STORAGE permission denied via onRequestPermissionsResult")
|
||||||
|
// Handle denied case, maybe show a message or disable functionality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("This method has been deprecated in favor of using the Activity Result API\n which brings increased type safety via an {@link ActivityResultContract} and the prebuilt\n contracts for common intents available in\n {@link androidx.activity.result.contract.ActivityResultContracts}, provides hooks for\n testing, and allow receiving results in separate, testable classes independent from your\n activity. Use\n {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}\n with the appropriate {@link ActivityResultContract} and handling the result in the\n {@link ActivityResultCallback#onActivityResult(Object) callback}.")
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
if (requestCode == STORAGE_PERMISSION_CODE) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
if (Environment.isExternalStorageManager()) {
|
||||||
|
listFoldersInHiddenDirectory()
|
||||||
|
} else {
|
||||||
|
// Handle denied case
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
|
||||||
|
data?.data?.let { uri ->
|
||||||
|
Log.d("HiddenActivity", "Selected file URI: $uri")
|
||||||
|
copyFileToHiddenDirectory(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listFoldersInHiddenDirectory() {
|
||||||
|
if (hiddenDir.exists() && hiddenDir.isDirectory) {
|
||||||
|
val folders = folderManager.getFoldersInDirectory(hiddenDir)
|
||||||
|
|
||||||
|
if (folders.isNotEmpty()) {
|
||||||
|
binding.noItems.visibility = View.GONE
|
||||||
|
binding.recyclerView.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
// Initialize adapter only once
|
||||||
|
if (folderAdapter == null) {
|
||||||
|
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||||
|
folderAdapter = FolderAdapter(
|
||||||
|
onFolderClick = { clickedFolder ->
|
||||||
|
openFolder(clickedFolder)
|
||||||
|
},
|
||||||
|
onFolderLongClick = { folder ->
|
||||||
|
// go to selection mode
|
||||||
|
}
|
||||||
|
)
|
||||||
|
binding.recyclerView.adapter = folderAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit new list to adapter - DiffUtil will handle the comparison
|
||||||
|
folderAdapter?.submitList(folders)
|
||||||
|
} else {
|
||||||
|
binding.noItems.visibility = View.VISIBLE
|
||||||
|
binding.recyclerView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
} else if (!hiddenDir.exists()) {
|
||||||
|
fileManager.getHiddenDirectory()
|
||||||
|
} else {
|
||||||
|
Log.e("HiddenActivity", "Hidden directory is not a directory: ${hiddenDir.absolutePath}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openFolder(folder: File) {
|
||||||
|
Log.d("HiddenActivity", "Opening folder: ${folder.name}")
|
||||||
|
currentFolder = folder
|
||||||
|
binding.addFolder.visibility = View.GONE
|
||||||
|
binding.fabExpend.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
// Read files in the clicked folder and update RecyclerView
|
||||||
|
val files = folderManager.getFilesInFolder(folder)
|
||||||
|
Log.d("HiddenActivity", "Found ${files.size} files in ${folder.name}")
|
||||||
|
|
||||||
|
if (files.isNotEmpty()) {
|
||||||
|
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||||
|
|
||||||
|
val fileAdapter = FileAdapter(this, this, folder).apply {
|
||||||
|
// Set up the callback for file operations
|
||||||
|
fileOperationCallback = object : FileAdapter.FileOperationCallback {
|
||||||
|
override fun onFileDeleted(file: File) {
|
||||||
|
// Refresh the file list
|
||||||
|
refreshCurrentFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFileRenamed(oldFile: File, newFile: File) {
|
||||||
|
// Refresh the file list
|
||||||
|
refreshCurrentFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRefreshNeeded() {
|
||||||
|
// Refresh the file list
|
||||||
|
refreshCurrentFolder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitList(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.recyclerView.adapter = fileAdapter
|
||||||
|
binding.recyclerView.visibility = View.VISIBLE
|
||||||
|
binding.noItems.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.recyclerView.visibility = View.GONE
|
||||||
|
binding.noItems.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshCurrentFolder() {
|
||||||
|
currentFolder?.let { folder ->
|
||||||
|
val files = folderManager.getFilesInFolder(folder)
|
||||||
|
(binding.recyclerView.adapter as? FileAdapter)?.submitList(files)
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
binding.recyclerView.visibility = View.GONE
|
||||||
|
binding.noItems.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.recyclerView.visibility = View.VISIBLE
|
||||||
|
binding.noItems.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openFabs() {
|
||||||
|
binding.addImage.startAnimation(fabOpen)
|
||||||
|
binding.addVideo.startAnimation(fabOpen)
|
||||||
|
binding.addAudio.startAnimation(fabOpen)
|
||||||
|
binding.addDocument.startAnimation(fabOpen)
|
||||||
|
binding.addFolder.startAnimation(fabOpen)
|
||||||
|
binding.fabExpend.startAnimation(rotateOpen)
|
||||||
|
|
||||||
|
binding.addImage.visibility = View.VISIBLE
|
||||||
|
binding.addVideo.visibility = View.VISIBLE
|
||||||
|
binding.addAudio.visibility = View.VISIBLE
|
||||||
|
binding.addDocument.visibility = View.VISIBLE
|
||||||
|
binding.addFolder.visibility = View.VISIBLE // Keep this visible if in folder list, but should be GONE when showing files
|
||||||
|
|
||||||
|
isFabOpen = true
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
binding.fabExpend.setImageResource(R.drawable.wrong)
|
||||||
|
},200)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun closeFabs() {
|
||||||
|
binding.addImage.startAnimation(fabClose)
|
||||||
|
binding.addVideo.startAnimation(fabClose)
|
||||||
|
binding.addAudio.startAnimation(fabClose)
|
||||||
|
binding.addDocument.startAnimation(fabClose)
|
||||||
|
binding.addFolder.startAnimation(fabClose)
|
||||||
|
binding.fabExpend.startAnimation(rotateClose)
|
||||||
|
|
||||||
|
binding.addImage.visibility = View.INVISIBLE
|
||||||
|
binding.addVideo.visibility = View.INVISIBLE
|
||||||
|
binding.addAudio.visibility = View.INVISIBLE
|
||||||
|
binding.addDocument.visibility = View.INVISIBLE
|
||||||
|
binding.addFolder.visibility = View.INVISIBLE
|
||||||
|
|
||||||
|
isFabOpen = false
|
||||||
|
binding.fabExpend.setImageResource(R.drawable.ic_add)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyFileToHiddenDirectory(uri: Uri) {
|
||||||
|
currentFolder?.let { destinationFolder ->
|
||||||
|
try {
|
||||||
|
val inputStream: InputStream? = contentResolver.openInputStream(uri)
|
||||||
|
val fileName = getFileNameFromUri(uri) ?: "unknown_file"
|
||||||
|
val destinationFile = File(destinationFolder, fileName)
|
||||||
|
|
||||||
|
inputStream?.use { input ->
|
||||||
|
val outputStream: OutputStream = FileOutputStream(destinationFile)
|
||||||
|
outputStream.use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("HiddenActivity", "File copied to: ${destinationFile.absolutePath}")
|
||||||
|
// Refresh the file list in the RecyclerView
|
||||||
|
currentFolder?.let { openFolder(it) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("HiddenActivity", "Error copying file", e)
|
||||||
|
// TODO: Show error message to user
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
Log.e("HiddenActivity", "Current folder is null, cannot copy file")
|
||||||
|
// TODO: Show error message to user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileNameFromUri(uri: Uri): String? {
|
||||||
|
var name: String? = null
|
||||||
|
val cursor = contentResolver.query(uri, null, null, null, null)
|
||||||
|
cursor?.use { it ->
|
||||||
|
val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
|
||||||
|
if (nameIndex != -1) {
|
||||||
|
it.moveToFirst()
|
||||||
|
name = it.getString(nameIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showFileOptionsDialog(file: File) {
|
||||||
|
val options = arrayOf("Delete", "Rename", "Share")
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle("Choose an action for ${file.name}")
|
||||||
|
.setItems(options) { dialog, which ->
|
||||||
|
when (which) {
|
||||||
|
0 -> deleteFile(file)
|
||||||
|
1 -> renameFile(file)
|
||||||
|
2 -> shareFile(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteFile(file: File) {
|
||||||
|
Log.d("HiddenActivity", "Deleting file: ${file.name}")
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.delete()) {
|
||||||
|
Log.d("HiddenActivity", "File deleted successfully")
|
||||||
|
// Refresh the file list in the RecyclerView
|
||||||
|
currentFolder?.let { openFolder(it) }
|
||||||
|
} else {
|
||||||
|
Log.e("HiddenActivity", "Failed to delete file: ${file.absolutePath}")
|
||||||
|
// TODO: Show error message to user
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("HiddenActivity", "File not found for deletion: ${file.absolutePath}")
|
||||||
|
// TODO: Show error message to user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renameFile(file: File) {
|
||||||
|
Log.d("HiddenActivity", "Renaming file: ${file.name}")
|
||||||
|
val inputEditText = EditText(this)
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle("Rename ${file.name}")
|
||||||
|
.setView(inputEditText)
|
||||||
|
.setPositiveButton("Rename") { dialog, _ ->
|
||||||
|
val newName = inputEditText.text.toString().trim()
|
||||||
|
if (newName.isNotEmpty()) {
|
||||||
|
val parentDir = file.parentFile
|
||||||
|
if (parentDir != null) {
|
||||||
|
val newFile = File(parentDir, newName)
|
||||||
|
if (file.renameTo(newFile)) {
|
||||||
|
Log.d("HiddenActivity", "File renamed to: ${newFile.name}")
|
||||||
|
currentFolder?.let { openFolder(it) }
|
||||||
|
} else {
|
||||||
|
Log.e("HiddenActivity", "Failed to rename file: ${file.absolutePath} to ${newFile.absolutePath}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("HiddenActivity", "Parent directory is null for renaming: ${file.absolutePath}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("HiddenActivity", "New file name is empty")
|
||||||
|
}
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton("Cancel") { dialog, _ ->
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareFile(file: File) {
|
||||||
|
val uri: Uri? = FileProvider.getUriForFile(this, "${packageName}.fileprovider", file)
|
||||||
|
uri?.let { fileUri ->
|
||||||
|
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||||
|
type = contentResolver.getType(fileUri) ?: "*/*"
|
||||||
|
putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(shareIntent, "Share ${file.name}"))
|
||||||
|
} ?: run {
|
||||||
|
Log.e("HiddenActivity", "Could not get URI for sharing file: ${file.absolutePath}")
|
||||||
|
//Show error message to user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (currentFolder != null) {
|
||||||
|
currentFolder = null
|
||||||
|
if (isFabOpen) {
|
||||||
|
closeFabs()
|
||||||
|
}
|
||||||
|
if (folderAdapter != null) {
|
||||||
|
binding.recyclerView.adapter = folderAdapter
|
||||||
|
}
|
||||||
|
listFoldersInHiddenDirectory()
|
||||||
|
binding.fabExpend.visibility = View.GONE
|
||||||
|
binding.addImage.visibility = View.GONE
|
||||||
|
binding.addVideo.visibility = View.GONE
|
||||||
|
binding.addAudio.visibility = View.GONE
|
||||||
|
binding.addDocument.visibility = View.GONE
|
||||||
|
binding.addFolder.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,17 +20,5 @@ class HiddenVaultActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupNavigation() {
|
private fun setupNavigation() {
|
||||||
binding.btnImages.setOnClickListener {
|
|
||||||
startActivity(Intent(this, ImageGalleryActivity::class.java))
|
|
||||||
}
|
|
||||||
binding.btnVideos.setOnClickListener {
|
|
||||||
startActivity(Intent(this, VideoGalleryActivity::class.java))
|
|
||||||
}
|
|
||||||
binding.btnAudio.setOnClickListener {
|
|
||||||
startActivity(Intent(this, AudioGalleryActivity::class.java))
|
|
||||||
}
|
|
||||||
binding.btnDocs.setOnClickListener {
|
|
||||||
startActivity(Intent(this, DocumentsActivity::class.java))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package devs.org.calculator.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.IntentSenderRequest
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import devs.org.calculator.utils.FileManager
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
import android.Manifest
|
|
||||||
import devs.org.calculator.R
|
|
||||||
import devs.org.calculator.callbacks.FileProcessCallback
|
|
||||||
|
|
||||||
class ImageGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
|
||||||
override val fileType = FileManager.FileType.IMAGE
|
|
||||||
private val STORAGE_PERMISSION_CODE = 100
|
|
||||||
|
|
||||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
|
||||||
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setupIntentSenderLauncher()
|
|
||||||
setupFabButton()
|
|
||||||
|
|
||||||
intentSenderLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()){
|
|
||||||
if (it.resultCode != RESULT_OK) Toast.makeText(this,
|
|
||||||
getString(R.string.failed_to_hide_unhide_photo), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
val clipData = result.data?.clipData
|
|
||||||
val uriList = mutableListOf<Uri>()
|
|
||||||
|
|
||||||
if (clipData != null) {
|
|
||||||
for (i in 0 until clipData.itemCount) {
|
|
||||||
val uri = clipData.getItemAt(i).uri
|
|
||||||
uriList.add(uri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.data?.data?.let { uriList.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriList.isNotEmpty()) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
FileManager(this@ImageGalleryActivity, this@ImageGalleryActivity)
|
|
||||||
.processMultipleFiles(uriList, fileType,this@ImageGalleryActivity )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
askPermissiom()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
|
||||||
Toast.makeText(this@ImageGalleryActivity, "${copiedFiles.size} ${getString(R.string.images_hidden_successfully)}", Toast.LENGTH_SHORT).show()
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFileProcessFailed() {
|
|
||||||
Toast.makeText(this@ImageGalleryActivity,
|
|
||||||
getString(R.string.failed_to_hide_images), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupIntentSenderLauncher() {
|
|
||||||
intentSenderLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartIntentSenderForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun askPermissiom() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
|
|
||||||
if (!Environment.isExternalStorageManager()){
|
|
||||||
val intent = Intent().setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
checkAndRequestStoragePermission()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkAndRequestStoragePermission() {
|
|
||||||
if (ContextCompat.checkSelfPermission(
|
|
||||||
this,
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
|
||||||
) != PackageManager.PERMISSION_GRANTED ||
|
|
||||||
ContextCompat.checkSelfPermission(
|
|
||||||
this,
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
) != PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
ActivityCompat.requestPermissions(
|
|
||||||
this,
|
|
||||||
arrayOf(
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
),
|
|
||||||
STORAGE_PERMISSION_CODE
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
//storage permission granted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
|
||||||
requestCode: Int,
|
|
||||||
permissions: Array<out String>,
|
|
||||||
grantResults: IntArray
|
|
||||||
) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
if (requestCode == STORAGE_PERMISSION_CODE) {
|
|
||||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
Toast.makeText(this,
|
|
||||||
getString(R.string.storage_permissions_granted), Toast.LENGTH_SHORT).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this,
|
|
||||||
getString(R.string.storage_permissions_denied), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFabButton() {
|
|
||||||
binding.fabAdd.setOnClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
type = "image/*"
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
pickImageLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openPreview() {
|
|
||||||
val intent = Intent(this, PreviewActivity::class.java).apply {
|
|
||||||
putExtra("type", fileType)
|
|
||||||
}
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,7 @@ import devs.org.calculator.utils.PrefsUtil
|
|||||||
import net.objecthunter.exp4j.ExpressionBuilder
|
import net.objecthunter.exp4j.ExpressionBuilder
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.DialogCallback {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private var currentExpression = "0"
|
private var currentExpression = "0"
|
||||||
private var lastWasOperator = false
|
private var lastWasOperator = false
|
||||||
@@ -57,8 +57,24 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
|||||||
"\n" +
|
"\n" +
|
||||||
"For devices running Android 11 or higher, you'll need to grant the 'All Files Access' permission.",
|
"For devices running Android 11 or higher, you'll need to grant the 'All Files Access' permission.",
|
||||||
"Grant",
|
"Grant",
|
||||||
"Cancel",
|
"Later",
|
||||||
this
|
object : DialogUtil.DialogCallback {
|
||||||
|
override fun onPositiveButtonClicked() {
|
||||||
|
fileManager.askPermission(this@MainActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNegativeButtonClicked() {
|
||||||
|
Toast.makeText(this@MainActivity,
|
||||||
|
"Storage permission is required for the app to function properly",
|
||||||
|
Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNaturalButtonClicked() {
|
||||||
|
Toast.makeText(this@MainActivity,
|
||||||
|
"You can grant permission later from Settings",
|
||||||
|
Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
setupNumberButton(binding.btn0, "0")
|
setupNumberButton(binding.btn0, "0")
|
||||||
@@ -298,7 +314,7 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PrefsUtil(this).validatePassword(currentExpression)) {
|
if (PrefsUtil(this).validatePassword(currentExpression)) {
|
||||||
val intent = Intent(this, HiddenVaultActivity::class.java)
|
val intent = Intent(this, HiddenActivity::class.java)
|
||||||
intent.putExtra("password", currentExpression)
|
intent.putExtra("password", currentExpression)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
clearDisplay()
|
clearDisplay()
|
||||||
@@ -395,15 +411,18 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPositiveButtonClicked() {
|
override fun onPositiveButtonClicked() {
|
||||||
|
// Handle positive button click for both DialogUtil and DialogActionsCallback
|
||||||
fileManager.askPermission(this)
|
fileManager.askPermission(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNegativeButtonClicked() {
|
override fun onNegativeButtonClicked() {
|
||||||
|
// Handle negative button click
|
||||||
|
Toast.makeText(this, "Storage permission is required for the app to function properly", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNaturalButtonClicked() {
|
override fun onNaturalButtonClicked() {
|
||||||
|
// Handle neutral button click
|
||||||
|
Toast.makeText(this, "You can grant permission later from Settings", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import devs.org.calculator.R
|
import devs.org.calculator.R
|
||||||
import devs.org.calculator.adapters.ImagePreviewAdapter
|
import devs.org.calculator.adapters.ImagePreviewAdapter
|
||||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
|
||||||
import devs.org.calculator.databinding.ActivityPreviewBinding
|
import devs.org.calculator.databinding.ActivityPreviewBinding
|
||||||
import devs.org.calculator.utils.DialogUtil
|
import devs.org.calculator.utils.DialogUtil
|
||||||
import devs.org.calculator.utils.FileManager
|
import devs.org.calculator.utils.FileManager
|
||||||
@@ -20,6 +19,7 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
private var currentPosition: Int = 0
|
private var currentPosition: Int = 0
|
||||||
private lateinit var files: List<File>
|
private lateinit var files: List<File>
|
||||||
private lateinit var type: String
|
private lateinit var type: String
|
||||||
|
private lateinit var folder: String
|
||||||
private lateinit var filetype: FileManager.FileType
|
private lateinit var filetype: FileManager.FileType
|
||||||
private lateinit var adapter: ImagePreviewAdapter
|
private lateinit var adapter: ImagePreviewAdapter
|
||||||
private lateinit var fileManager: FileManager
|
private lateinit var fileManager: FileManager
|
||||||
@@ -34,9 +34,10 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
currentPosition = intent.getIntExtra("position", 0)
|
currentPosition = intent.getIntExtra("position", 0)
|
||||||
type = intent.getStringExtra("type").toString()
|
type = intent.getStringExtra("type").toString()
|
||||||
|
folder = intent.getStringExtra("folder").toString()
|
||||||
|
|
||||||
setupFileType()
|
setupFileType()
|
||||||
files = fileManager.getFilesInHiddenDir(filetype)
|
files = fileManager.getFilesInHiddenDirFromFolder(filetype, folder = folder)
|
||||||
|
|
||||||
setupImagePreview()
|
setupImagePreview()
|
||||||
clickListeners()
|
clickListeners()
|
||||||
@@ -72,7 +73,7 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupImagePreview() {
|
private fun setupImagePreview() {
|
||||||
adapter = ImagePreviewAdapter(this, filetype)
|
adapter = ImagePreviewAdapter(this, this)
|
||||||
adapter.images = files
|
adapter.images = files
|
||||||
binding.viewPager.adapter = adapter
|
binding.viewPager.adapter = adapter
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
getString(R.string.are_you_sure_to_delete_this_file_permanently),
|
getString(R.string.are_you_sure_to_delete_this_file_permanently),
|
||||||
getString(R.string.delete_permanently),
|
getString(R.string.delete_permanently),
|
||||||
getString(R.string.cancel),
|
getString(R.string.cancel),
|
||||||
object : DialogActionsCallback{
|
object : DialogUtil.DialogCallback {
|
||||||
override fun onPositiveButtonClicked() {
|
override fun onPositiveButtonClicked() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
FileManager(this@PreviewActivity, this@PreviewActivity).deletePhotoFromExternalStorage(fileUri)
|
FileManager(this@PreviewActivity, this@PreviewActivity).deletePhotoFromExternalStorage(fileUri)
|
||||||
@@ -119,13 +120,12 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNegativeButtonClicked() {
|
override fun onNegativeButtonClicked() {
|
||||||
|
// Handle negative button click
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNaturalButtonClicked() {
|
override fun onNaturalButtonClicked() {
|
||||||
|
// Handle neutral button click
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
getString(R.string.are_you_sure_you_want_to_un_hide_this_file),
|
getString(R.string.are_you_sure_you_want_to_un_hide_this_file),
|
||||||
getString(R.string.un_hide),
|
getString(R.string.un_hide),
|
||||||
getString(R.string.cancel),
|
getString(R.string.cancel),
|
||||||
object : DialogActionsCallback{
|
object : DialogUtil.DialogCallback {
|
||||||
override fun onPositiveButtonClicked() {
|
override fun onPositiveButtonClicked() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
FileManager(this@PreviewActivity, this@PreviewActivity).copyFileToNormalDir(fileUri)
|
FileManager(this@PreviewActivity, this@PreviewActivity).copyFileToNormalDir(fileUri)
|
||||||
@@ -148,13 +148,12 @@ class PreviewActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNegativeButtonClicked() {
|
override fun onNegativeButtonClicked() {
|
||||||
|
// Handle negative button click
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNaturalButtonClicked() {
|
override fun onNaturalButtonClicked() {
|
||||||
|
// Handle neutral button click
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
package devs.org.calculator.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import devs.org.calculator.R
|
|
||||||
import devs.org.calculator.utils.FileManager
|
|
||||||
import devs.org.calculator.callbacks.FileProcessCallback
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class VideoGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
|
||||||
override val fileType = FileManager.FileType.VIDEO
|
|
||||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setupFabButton()
|
|
||||||
|
|
||||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
val clipData = result.data?.clipData
|
|
||||||
val uriList = mutableListOf<Uri>()
|
|
||||||
|
|
||||||
if (clipData != null) {
|
|
||||||
for (i in 0 until clipData.itemCount) {
|
|
||||||
val uri = clipData.getItemAt(i).uri
|
|
||||||
uriList.add(uri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uriList.isNotEmpty()) {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
FileManager(this@VideoGalleryActivity, this@VideoGalleryActivity)
|
|
||||||
.processMultipleFiles(uriList, fileType,this@VideoGalleryActivity )
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
|
||||||
Toast.makeText(this@VideoGalleryActivity, "${copiedFiles.size} ${getString(R.string.videos_hidden_successfully)}"
|
|
||||||
, Toast.LENGTH_SHORT).show()
|
|
||||||
loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFileProcessFailed() {
|
|
||||||
Toast.makeText(this@VideoGalleryActivity,
|
|
||||||
getString(R.string.failed_to_hide_videos), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFabButton() {
|
|
||||||
binding.fabAdd.setOnClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
type = "video/*"
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
pickLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openPreview() {
|
|
||||||
val intent = Intent(this, PreviewActivity::class.java).apply {
|
|
||||||
putExtra("type", fileType)
|
|
||||||
}
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +1,123 @@
|
|||||||
package devs.org.calculator.adapters
|
package devs.org.calculator.adapters
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import devs.org.calculator.R
|
import devs.org.calculator.R
|
||||||
import devs.org.calculator.activities.PreviewActivity
|
import devs.org.calculator.activities.PreviewActivity
|
||||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
|
||||||
import devs.org.calculator.utils.DialogUtil
|
|
||||||
import devs.org.calculator.utils.FileManager
|
import devs.org.calculator.utils.FileManager
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class FileAdapter(
|
class FileAdapter(
|
||||||
private val fileType: FileManager.FileType,
|
private val context: Context,
|
||||||
var context: Context,
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
private var lifecycleOwner: LifecycleOwner
|
private val currentFolder: File
|
||||||
) :
|
) : ListAdapter<File, FileAdapter.FileViewHolder>(FileDiffCallback()) {
|
||||||
ListAdapter<File, FileAdapter.FileViewHolder>(FileDiffCallback()) {
|
|
||||||
|
|
||||||
private val selectedItems = mutableSetOf<Int>()
|
private val selectedItems = mutableSetOf<Int>()
|
||||||
private var isSelectionMode = false
|
private var isSelectionMode = false
|
||||||
private var fileName = "Unknown File"
|
|
||||||
private var fileTypes = when (fileType) {
|
|
||||||
|
|
||||||
FileManager.FileType.IMAGE -> {
|
// Callback interface for handling file operations
|
||||||
context.getString(R.string.image)
|
interface FileOperationCallback {
|
||||||
|
fun onFileDeleted(file: File)
|
||||||
|
fun onFileRenamed(oldFile: File, newFile: File)
|
||||||
|
fun onRefreshNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
FileManager.FileType.VIDEO -> {
|
var fileOperationCallback: FileOperationCallback? = null
|
||||||
context.getString(R.string.video)
|
|
||||||
}
|
|
||||||
|
|
||||||
FileManager.FileType.AUDIO -> {
|
|
||||||
context.getString(R.string.audio)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> context.getString(R.string.document)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class FileViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
inner class FileViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
private val imageView: ImageView = view.findViewById(R.id.imageView)
|
val imageView: ImageView = view.findViewById(R.id.fileIconImageView)
|
||||||
|
val fileNameTextView: TextView = view.findViewById(R.id.fileNameTextView)
|
||||||
|
val playIcon: ImageView = view.findViewById(R.id.videoPlay)
|
||||||
|
|
||||||
fun bind(file: File) {
|
fun bind(file: File) {
|
||||||
|
val fileType = FileManager(context, lifecycleOwner).getFileType(file)
|
||||||
|
setupFileDisplay(file, fileType)
|
||||||
|
setupClickListeners(file, fileType)
|
||||||
|
|
||||||
|
// Handle selection state
|
||||||
|
itemView.isSelected = selectedItems.contains(adapterPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(file: File, payloads: List<Any>) {
|
||||||
|
if (payloads.isEmpty()) {
|
||||||
|
bind(file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle partial updates based on payload
|
||||||
|
val changes = payloads.firstOrNull() as? List<String>
|
||||||
|
changes?.forEach { change ->
|
||||||
|
when (change) {
|
||||||
|
"NAME_CHANGED" -> {
|
||||||
|
fileNameTextView.text = file.name
|
||||||
|
}
|
||||||
|
"SIZE_CHANGED", "MODIFIED_DATE_CHANGED" -> {
|
||||||
|
// Could update file info if displayed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupFileDisplay(file: File, fileType: FileManager.FileType) {
|
||||||
when (fileType) {
|
when (fileType) {
|
||||||
FileManager.FileType.IMAGE -> {
|
FileManager.FileType.IMAGE -> {
|
||||||
|
loadImageThumbnail(file)
|
||||||
|
fileNameTextView.visibility = View.GONE
|
||||||
|
playIcon.visibility = View.GONE
|
||||||
|
}
|
||||||
|
FileManager.FileType.VIDEO -> {
|
||||||
|
loadVideoThumbnail(file)
|
||||||
|
fileNameTextView.visibility = View.GONE
|
||||||
|
playIcon.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
loadFileIcon(fileType)
|
||||||
|
fileNameTextView.visibility = View.VISIBLE
|
||||||
|
playIcon.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileNameTextView.text = file.name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadImageThumbnail(file: File) {
|
||||||
Glide.with(imageView)
|
Glide.with(imageView)
|
||||||
.load(file)
|
.load(file)
|
||||||
|
.thumbnail(0.1f)
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
|
.override(300, 300)
|
||||||
|
.placeholder(R.drawable.ic_file)
|
||||||
|
.error(R.drawable.ic_file)
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
FileManager.FileType.VIDEO -> {
|
private fun loadVideoThumbnail(file: File) {
|
||||||
Glide.with(imageView)
|
Glide.with(imageView)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(file)
|
.load(file)
|
||||||
|
.thumbnail(0.1f)
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
|
.override(300, 300)
|
||||||
|
.placeholder(R.drawable.ic_file)
|
||||||
|
.error(R.drawable.ic_file)
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
private fun loadFileIcon(fileType: FileManager.FileType) {
|
||||||
val resourceId = when (fileType) {
|
val resourceId = when (fileType) {
|
||||||
FileManager.FileType.AUDIO -> R.drawable.ic_audio
|
FileManager.FileType.AUDIO -> R.drawable.ic_audio
|
||||||
FileManager.FileType.DOCUMENT -> R.drawable.ic_document
|
FileManager.FileType.DOCUMENT -> R.drawable.ic_document
|
||||||
@@ -78,120 +125,217 @@ class FileAdapter(
|
|||||||
}
|
}
|
||||||
imageView.setImageResource(resourceId)
|
imageView.setImageResource(resourceId)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun setupClickListeners(file: File, fileType: FileManager.FileType) {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
|
if (isSelectionMode) {
|
||||||
|
toggleSelection(adapterPosition)
|
||||||
when(fileType){
|
return@setOnClickListener
|
||||||
FileManager.FileType.AUDIO -> {
|
|
||||||
// Create an intent to play audio using available audio players
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
|
||||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "audio/*")
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
context.startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(context,
|
|
||||||
context.getString(R.string.no_audio_player_found), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
|
openFile(file, fileType)
|
||||||
}
|
}
|
||||||
|
|
||||||
FileManager.FileType.DOCUMENT -> {
|
|
||||||
// Create an intent to open the document using available viewers or file managers
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
|
||||||
setDataAndType(FileManager.FileManager().getContentUriImage(context, file, fileType), "*/*")
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
context.startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(context,
|
|
||||||
context.getString(R.string.no_suitable_app_found_to_open_this_document), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val intent = Intent(context, PreviewActivity::class.java).apply {
|
|
||||||
putExtra("type", fileTypes)
|
|
||||||
putExtra("position", position)
|
|
||||||
}
|
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
itemView.setOnLongClickListener {
|
itemView.setOnLongClickListener {
|
||||||
|
if (!isSelectionMode) {
|
||||||
|
showFileOptionsDialog(file)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val fileUri = FileManager.FileManager().getContentUriImage(context, file, fileType)
|
private fun openFile(file: File, fileType: FileManager.FileType) {
|
||||||
if (fileUri == null) {
|
when (fileType) {
|
||||||
Toast.makeText(context, "Unable to access file: $file", Toast.LENGTH_SHORT)
|
FileManager.FileType.AUDIO -> openAudioFile(file)
|
||||||
|
FileManager.FileType.IMAGE, FileManager.FileType.VIDEO -> openInPreview(fileType)
|
||||||
|
FileManager.FileType.DOCUMENT -> openDocumentFile(file)
|
||||||
|
else -> openDocumentFile(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openAudioFile(file: File) {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${context.packageName}.fileprovider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, "audio/*")
|
||||||
|
putExtra("folder", currentFolder.toString())
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
context.startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.no_audio_player_found),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openDocumentFile(file: File) {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${context.packageName}.fileprovider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, "*/*")
|
||||||
|
putExtra("folder", currentFolder.toString())
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
context.startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.no_suitable_app_found_to_open_this_document),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openInPreview(fileType: FileManager.FileType) {
|
||||||
|
val fileTypeString = when (fileType) {
|
||||||
|
FileManager.FileType.IMAGE -> context.getString(R.string.image)
|
||||||
|
FileManager.FileType.VIDEO -> context.getString(R.string.video)
|
||||||
|
else -> "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(context, PreviewActivity::class.java).apply {
|
||||||
|
putExtra("type", fileTypeString)
|
||||||
|
putExtra("folder", currentFolder.toString())
|
||||||
|
putExtra("position", adapterPosition)
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showFileOptionsDialog(file: File) {
|
||||||
|
val options = arrayOf(
|
||||||
|
context.getString(R.string.delete),
|
||||||
|
context.getString(R.string.rename),
|
||||||
|
context.getString(R.string.share)
|
||||||
|
)
|
||||||
|
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(context.getString(R.string.file_options))
|
||||||
|
.setItems(options) { dialog, which ->
|
||||||
|
when (which) {
|
||||||
|
0 -> deleteFile(file)
|
||||||
|
1 -> renameFile(file)
|
||||||
|
2 -> shareFile(file)
|
||||||
|
}
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
|
|
||||||
}
|
}
|
||||||
fileName = FileManager.FileName(context).getFileNameFromUri(fileUri)?.toString()
|
|
||||||
?: context.getString(R.string.unknown_file)
|
|
||||||
|
|
||||||
DialogUtil(context).showMaterialDialogWithNaturalButton(
|
private fun deleteFile(file: File) {
|
||||||
context.getString(R.string.details, fileTypes),
|
if (file.delete()) {
|
||||||
"File Name: $fileName\n\nFile Path: $file\n\nYou can permanently delete or un-hide this file.",
|
fileOperationCallback?.onFileDeleted(file)
|
||||||
context.getString(R.string.delete_permanently),
|
Toast.makeText(context, "File deleted", Toast.LENGTH_SHORT).show()
|
||||||
context.getString(R.string.un_hide),
|
} else {
|
||||||
context.getString(R.string.cancel),
|
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
|
||||||
object : DialogActionsCallback {
|
}
|
||||||
override fun onPositiveButtonClicked() {
|
}
|
||||||
lifecycleOwner.lifecycleScope.launch {
|
|
||||||
FileManager(context, lifecycleOwner).deletePhotoFromExternalStorage(
|
private fun renameFile(file: File) {
|
||||||
fileUri
|
val inputEditText = EditText(context).apply {
|
||||||
|
setText(file.name)
|
||||||
|
selectAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(context.getString(R.string.rename_file))
|
||||||
|
.setView(inputEditText)
|
||||||
|
.setPositiveButton(context.getString(R.string.rename)) { dialog, _ ->
|
||||||
|
val newName = inputEditText.text.toString().trim()
|
||||||
|
if (newName.isNotEmpty() && newName != file.name) {
|
||||||
|
val parentDir = file.parentFile
|
||||||
|
if (parentDir != null) {
|
||||||
|
val newFile = File(parentDir, newName)
|
||||||
|
if (file.renameTo(newFile)) {
|
||||||
|
fileOperationCallback?.onFileRenamed(file, newFile)
|
||||||
|
Toast.makeText(context, "File renamed", Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "Failed to rename file", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(context.getString(R.string.cancel)) { dialog, _ ->
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareFile(file: File) {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${context.packageName}.fileprovider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||||
|
type = context.contentResolver.getType(uri) ?: "*/*"
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
context.startActivity(
|
||||||
|
Intent.createChooser(shareIntent, context.getString(R.string.share_file))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val currentList = currentList.toMutableList()
|
|
||||||
currentList.remove(file)
|
private fun toggleSelection(position: Int) {
|
||||||
submitList(currentList)
|
if (selectedItems.contains(position)) {
|
||||||
|
selectedItems.remove(position)
|
||||||
|
} else {
|
||||||
|
selectedItems.add(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNegativeButtonClicked() {
|
if (selectedItems.isEmpty()) {
|
||||||
FileManager(context, lifecycleOwner).copyFileToNormalDir(fileUri)
|
isSelectionMode = false
|
||||||
val currentList = currentList.toMutableList()
|
|
||||||
currentList.remove(file)
|
|
||||||
submitList(currentList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNaturalButtonClicked() {
|
notifyItemChanged(position)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
.inflate(R.layout.item_file, parent, false)
|
.inflate(R.layout.list_item_file, parent, false)
|
||||||
return FileViewHolder(view)
|
return FileViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
|
||||||
holder.bind(getItem(position))
|
val file = getItem(position)
|
||||||
|
holder.bind(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileDiffCallback : DiffUtil.ItemCallback<File>() {
|
override fun onBindViewHolder(holder: FileViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||||
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
if (payloads.isEmpty()) {
|
||||||
return oldItem.path == newItem.path
|
super.onBindViewHolder(holder, position, payloads)
|
||||||
}
|
} else {
|
||||||
|
val file = getItem(position)
|
||||||
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
holder.bind(file, payloads)
|
||||||
return oldItem == newItem
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public methods for external control
|
||||||
|
fun clearSelection() {
|
||||||
|
selectedItems.clear()
|
||||||
|
isSelectionMode = false
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSelectedItems(): List<File> {
|
||||||
|
return selectedItems.mapNotNull { position ->
|
||||||
|
if (position < itemCount) getItem(position) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package devs.org.calculator.adapters
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FileDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||||
|
// Compare by absolute path since File objects might be different instances
|
||||||
|
// but represent the same file
|
||||||
|
return oldItem.absolutePath == newItem.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||||
|
// Compare all relevant properties that might change and affect the UI
|
||||||
|
return oldItem.name == newItem.name &&
|
||||||
|
oldItem.length() == newItem.length() &&
|
||||||
|
oldItem.lastModified() == newItem.lastModified() &&
|
||||||
|
oldItem.canRead() == newItem.canRead() &&
|
||||||
|
oldItem.canWrite() == newItem.canWrite()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChangePayload(oldItem: File, newItem: File): Any? {
|
||||||
|
// Return a payload if only specific properties changed
|
||||||
|
// This allows for partial updates instead of full rebinding
|
||||||
|
val changes = mutableListOf<String>()
|
||||||
|
|
||||||
|
if (oldItem.name != newItem.name) {
|
||||||
|
changes.add("NAME_CHANGED")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldItem.length() != newItem.length()) {
|
||||||
|
changes.add("SIZE_CHANGED")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldItem.lastModified() != newItem.lastModified()) {
|
||||||
|
changes.add("MODIFIED_DATE_CHANGED")
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (changes.isNotEmpty()) changes else null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package devs.org.calculator.adapters
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import devs.org.calculator.R
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FolderAdapter(
|
||||||
|
private val onFolderClick: (File) -> Unit,
|
||||||
|
private val onFolderLongClick: (File) -> Unit
|
||||||
|
) : ListAdapter<File, FolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
|
||||||
|
|
||||||
|
class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
|
||||||
|
|
||||||
|
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit) {
|
||||||
|
folderNameTextView.text = folder.name
|
||||||
|
|
||||||
|
itemView.setOnClickListener { onFolderClick(folder) }
|
||||||
|
itemView.setOnLongClickListener {
|
||||||
|
onFolderLongClick(folder)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
|
||||||
|
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_folder, parent, false)
|
||||||
|
return FolderViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
|
||||||
|
val folder = getItem(position)
|
||||||
|
holder.bind(folder, onFolderClick, onFolderLongClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FolderDiffCallback : DiffUtil.ItemCallback<File>() {
|
||||||
|
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
|
||||||
|
return oldItem.absolutePath == newItem.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
|
||||||
|
return oldItem.name == newItem.name &&
|
||||||
|
oldItem.lastModified() == newItem.lastModified() &&
|
||||||
|
oldItem.length() == newItem.length()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.MediaController
|
import android.widget.MediaController
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.recyclerview.widget.AsyncListDiffer
|
import androidx.recyclerview.widget.AsyncListDiffer
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import devs.org.calculator.adapters.FileAdapter.FileDiffCallback
|
|
||||||
import devs.org.calculator.databinding.ViewpagerItemsBinding
|
import devs.org.calculator.databinding.ViewpagerItemsBinding
|
||||||
import devs.org.calculator.utils.FileManager
|
import devs.org.calculator.utils.FileManager
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -21,7 +21,7 @@ import devs.org.calculator.R
|
|||||||
|
|
||||||
class ImagePreviewAdapter(
|
class ImagePreviewAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private var fileType: FileManager.FileType
|
private var lifecycleOwner: LifecycleOwner
|
||||||
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
|
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
|
||||||
|
|
||||||
private val differ = AsyncListDiffer(this, FileDiffCallback())
|
private val differ = AsyncListDiffer(this, FileDiffCallback())
|
||||||
@@ -42,7 +42,8 @@ class ImagePreviewAdapter(
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||||
val imageUrl = images[position]
|
val imageUrl = images[position]
|
||||||
holder.bind(imageUrl)
|
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
|
||||||
|
holder.bind(imageUrl,fileType)
|
||||||
currentViewHolder = holder
|
currentViewHolder = holder
|
||||||
|
|
||||||
currentMediaPlayer?.let {
|
currentMediaPlayer?.let {
|
||||||
@@ -67,7 +68,7 @@ class ImagePreviewAdapter(
|
|||||||
private var seekHandler = Handler(Looper.getMainLooper())
|
private var seekHandler = Handler(Looper.getMainLooper())
|
||||||
private var seekRunnable: Runnable? = null
|
private var seekRunnable: Runnable? = null
|
||||||
|
|
||||||
fun bind(file: File) {
|
fun bind(file: File, fileType: FileManager.FileType) {
|
||||||
when (fileType) {
|
when (fileType) {
|
||||||
FileManager.FileType.VIDEO -> {
|
FileManager.FileType.VIDEO -> {
|
||||||
binding.imageView.visibility = View.GONE
|
binding.imageView.visibility = View.GONE
|
||||||
@@ -228,6 +229,7 @@ class ImagePreviewAdapter(
|
|||||||
|
|
||||||
private fun playVideoAtPosition(position: Int) {
|
private fun playVideoAtPosition(position: Int) {
|
||||||
val nextFile = images[position]
|
val nextFile = images[position]
|
||||||
|
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
|
||||||
if (fileType == FileManager.FileType.VIDEO) {
|
if (fileType == FileManager.FileType.VIDEO) {
|
||||||
val videoUri = Uri.fromFile(nextFile)
|
val videoUri = Uri.fromFile(nextFile)
|
||||||
binding.videoView.setVideoURI(videoUri)
|
binding.videoView.setVideoURI(videoUri)
|
||||||
|
|||||||
@@ -1,59 +1,57 @@
|
|||||||
package devs.org.calculator.utils
|
package devs.org.calculator.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import android.view.LayoutInflater
|
||||||
import androidx.activity.result.IntentSenderRequest
|
import android.widget.EditText
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import devs.org.calculator.callbacks.DialogActionsCallback
|
import devs.org.calculator.R
|
||||||
|
|
||||||
class DialogUtil(private val context: Context) {
|
class DialogUtil(private val context: Context) {
|
||||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
|
||||||
fun showMaterialDialogWithNaturalButton(
|
|
||||||
title: String,
|
|
||||||
message: String,
|
|
||||||
positiveButton: String,
|
|
||||||
negativeButton: String,
|
|
||||||
neutralButton: String,
|
|
||||||
callback: DialogActionsCallback
|
|
||||||
) {
|
|
||||||
MaterialAlertDialogBuilder(context)
|
|
||||||
.setTitle(title)
|
|
||||||
.setMessage(message)
|
|
||||||
.setPositiveButton(positiveButton) { dialog, _ ->
|
|
||||||
// Handle positive button click
|
|
||||||
callback.onPositiveButtonClicked()
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
.setNegativeButton(negativeButton) { dialog, _ ->
|
|
||||||
// Handle negative button click
|
|
||||||
callback.onNegativeButtonClicked()
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
.setNeutralButton(neutralButton) { dialog, _ ->
|
|
||||||
callback.onNaturalButtonClicked()
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
fun showMaterialDialog(
|
fun showMaterialDialog(
|
||||||
title: String,
|
title: String,
|
||||||
message: String,
|
message: String,
|
||||||
positiveButton: String,
|
positiveButtonText: String,
|
||||||
negativeButton: String,
|
neutralButtonText: String,
|
||||||
callback: DialogActionsCallback
|
callback: DialogCallback
|
||||||
) {
|
) {
|
||||||
MaterialAlertDialogBuilder(context)
|
MaterialAlertDialogBuilder(context)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(positiveButton) { _, _ ->
|
.setPositiveButton(positiveButtonText) { _, _ -> callback.onPositiveButtonClicked() }
|
||||||
// Handle positive button click
|
.setNegativeButton(neutralButtonText) { _, _ -> callback.onNegativeButtonClicked() }
|
||||||
callback.onPositiveButtonClicked()
|
|
||||||
}
|
|
||||||
.setNegativeButton(negativeButton) { dialog, _ ->
|
|
||||||
// Handle negative button click
|
|
||||||
callback.onNegativeButtonClicked()
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createInputDialog(
|
||||||
|
title: String,
|
||||||
|
hint: String,
|
||||||
|
callback: InputDialogCallback
|
||||||
|
) {
|
||||||
|
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_input, null)
|
||||||
|
val editText = dialogView.findViewById<EditText>(R.id.editText)
|
||||||
|
editText.hint = hint
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(context)
|
||||||
|
.setTitle(title)
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton(R.string.create) { _, _ ->
|
||||||
|
callback.onPositiveButtonClicked(editText.text.toString())
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogCallback {
|
||||||
|
fun onPositiveButtonClicked()
|
||||||
|
fun onNegativeButtonClicked()
|
||||||
|
fun onNaturalButtonClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InputDialogCallback {
|
||||||
|
fun onPositiveButtonClicked(input: String)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,31 +41,43 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
|||||||
fun getHiddenDirectory(): File {
|
fun getHiddenDirectory(): File {
|
||||||
val dir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
val dir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
dir.mkdirs()
|
val created = dir.mkdirs()
|
||||||
|
if (!created) {
|
||||||
|
throw RuntimeException("Failed to create hidden directory: ${dir.absolutePath}")
|
||||||
|
}
|
||||||
// Create .nomedia file to hide from media scanners
|
// Create .nomedia file to hide from media scanners
|
||||||
File(dir, ".nomedia").createNewFile()
|
val nomediaFile = File(dir, ".nomedia")
|
||||||
|
if (!nomediaFile.exists()) {
|
||||||
|
nomediaFile.createNewFile()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getFilesInHiddenDir(type: FileType): List<File> {
|
fun getFilesInHiddenDir(type: FileType): List<File> {
|
||||||
val hiddenDir = getHiddenDirectory()
|
val hiddenDir = getHiddenDirectory()
|
||||||
val typeDir = File(hiddenDir, type.dirName)
|
val typeDir = File(hiddenDir, type.dirName)
|
||||||
return if (typeDir.exists()) {
|
if (!typeDir.exists()) {
|
||||||
typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
typeDir.mkdirs()
|
||||||
} else {
|
File(typeDir, ".nomedia").createNewFile()
|
||||||
emptyList()
|
|
||||||
}
|
}
|
||||||
|
return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
||||||
|
}
|
||||||
|
fun getFilesInHiddenDirFromFolder(type: FileType, folder: String): List<File> {
|
||||||
|
val typeDir = File(folder)
|
||||||
|
if (!typeDir.exists()) {
|
||||||
|
typeDir.mkdirs()
|
||||||
|
File(typeDir, ".nomedia").createNewFile()
|
||||||
|
}
|
||||||
|
return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyFileToHiddenDir(uri: Uri, type: FileType): File? {
|
private fun copyFileToHiddenDir(uri: Uri, type: FileType, currentDir: File? = null): File? {
|
||||||
return try {
|
return try {
|
||||||
val contentResolver = context.contentResolver
|
val contentResolver = context.contentResolver
|
||||||
|
|
||||||
// Get the target directory
|
// Get the target directory
|
||||||
val targetDir = File(Environment.getExternalStorageDirectory(), "$HIDDEN_DIR/${type.dirName}")
|
val targetDir = currentDir ?: File(Environment.getExternalStorageDirectory(), "$HIDDEN_DIR/${type.dirName}")
|
||||||
targetDir.mkdirs()
|
targetDir.mkdirs()
|
||||||
File(targetDir, ".nomedia").createNewFile()
|
File(targetDir, ".nomedia").createNewFile()
|
||||||
|
|
||||||
@@ -258,8 +270,6 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
|||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Toast.makeText(activity, "Unable to open settings. Please grant permission manually.", Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, "Unable to open settings. Please grant permission manually.", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Toast.makeText(activity, "Permission already granted", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For Android 10 and below
|
// For Android 10 and below
|
||||||
@@ -275,13 +285,14 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
|||||||
suspend fun processMultipleFiles(
|
suspend fun processMultipleFiles(
|
||||||
uriList: List<Uri>,
|
uriList: List<Uri>,
|
||||||
fileType: FileType,
|
fileType: FileType,
|
||||||
callback: FileProcessCallback
|
callback: FileProcessCallback,
|
||||||
|
currentDir: File? = null
|
||||||
) {
|
) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val copiedFiles = mutableListOf<File>()
|
val copiedFiles = mutableListOf<File>()
|
||||||
for (uri in uriList) {
|
for (uri in uriList) {
|
||||||
try {
|
try {
|
||||||
val file = copyFileToHiddenDir(uri, fileType)
|
val file = copyFileToHiddenDir(uri, fileType, currentDir)
|
||||||
file?.let { copiedFiles.add(it) }
|
file?.let { copiedFiles.add(it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -297,12 +308,22 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getFileType(file: File): FileType {
|
||||||
|
val extension = file.extension.lowercase()
|
||||||
|
return when (extension) {
|
||||||
|
"jpg", "jpeg", "png", "gif", "bmp", "webp" -> FileType.IMAGE
|
||||||
|
"mp4", "avi", "mkv", "mov", "wmv", "flv", "webm", "3gp" -> FileType.VIDEO
|
||||||
|
"mp3", "wav", "flac", "aac", "ogg", "m4a" -> FileType.AUDIO
|
||||||
|
else -> FileType.DOCUMENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum class FileType(val dirName: String) {
|
enum class FileType(val dirName: String) {
|
||||||
IMAGE(IMAGES_DIR),
|
IMAGE(IMAGES_DIR),
|
||||||
VIDEO(VIDEOS_DIR),
|
VIDEO(VIDEOS_DIR),
|
||||||
AUDIO(AUDIO_DIR),
|
AUDIO(AUDIO_DIR),
|
||||||
DOCUMENT(DOCS_DIR)
|
DOCUMENT(DOCS_DIR),
|
||||||
|
ALL("all")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
75
app/src/main/java/devs/org/calculator/utils/FolderManager.kt
Normal file
75
app/src/main/java/devs/org/calculator/utils/FolderManager.kt
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package devs.org.calculator.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FolderManager(private val context: Context) {
|
||||||
|
companion object {
|
||||||
|
const val HIDDEN_DIR = ".CalculatorHide"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFolder(parentDir: File, folderName: String): Boolean {
|
||||||
|
val newFolder = File(parentDir, folderName)
|
||||||
|
return if (!newFolder.exists()) {
|
||||||
|
newFolder.mkdirs()
|
||||||
|
// Create .nomedia file to hide from media scanners
|
||||||
|
File(newFolder, ".nomedia").createNewFile()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteFolder(folder: File): Boolean {
|
||||||
|
return try {
|
||||||
|
if (folder.exists() && folder.isDirectory) {
|
||||||
|
// Delete all files in the folder first
|
||||||
|
folder.listFiles()?.forEach { file ->
|
||||||
|
if (file.isFile) {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Then delete the folder itself
|
||||||
|
folder.delete()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFoldersInDirectory(directory: File): List<File> {
|
||||||
|
return if (directory.exists() && directory.isDirectory) {
|
||||||
|
directory.listFiles()?.filter { it.isDirectory && it.name != ".nomedia" } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilesInFolder(folder: File): List<File> {
|
||||||
|
return if (folder.exists() && folder.isDirectory) {
|
||||||
|
folder.listFiles()?.filter { it.isFile && it.name != ".nomedia" } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun moveFileToFolder(file: File, targetFolder: File): Boolean {
|
||||||
|
return try {
|
||||||
|
if (!targetFolder.exists()) {
|
||||||
|
targetFolder.mkdirs()
|
||||||
|
File(targetFolder, ".nomedia").createNewFile()
|
||||||
|
}
|
||||||
|
val newFile = File(targetFolder, file.name)
|
||||||
|
file.copyTo(newFile, overwrite = true)
|
||||||
|
file.delete()
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
app/src/main/res/anim/fab_close.xml
Normal file
9
app/src/main/res/anim/fab_close.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<scale xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="200"
|
||||||
|
android:fromXScale="1.0"
|
||||||
|
android:fromYScale="1.0"
|
||||||
|
android:toXScale="0.0"
|
||||||
|
android:toYScale="0.0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
9
app/src/main/res/anim/fab_open.xml
Normal file
9
app/src/main/res/anim/fab_open.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<scale xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="200"
|
||||||
|
android:fromXScale="0.0"
|
||||||
|
android:fromYScale="0.0"
|
||||||
|
android:toXScale="1.0"
|
||||||
|
android:toYScale="1.0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
7
app/src/main/res/anim/rotate_close.xml
Normal file
7
app/src/main/res/anim/rotate_close.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="200"
|
||||||
|
android:fromDegrees="45"
|
||||||
|
android:toDegrees="0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
7
app/src/main/res/anim/rotate_open.xml
Normal file
7
app/src/main/res/anim/rotate_open.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="200"
|
||||||
|
android:fromDegrees="0"
|
||||||
|
android:toDegrees="45"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
9
app/src/main/res/drawable/add_image.xml
Normal file
9
app/src/main/res/drawable/add_image.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M23,4v2h-3v3h-2L18,6h-3L15,4h3L18,1h2v3h3zM14.5,11c0.828,0 1.5,-0.672 1.5,-1.5S15.328,8 14.5,8 13,8.672 13,9.5s0.672,1.5 1.5,1.5zM18,14.234l-0.513,-0.57c-0.794,-0.885 -2.18,-0.885 -2.976,0l-0.655,0.73L9,9l-3,3.333L6,6h7L13,4L6,4c-1.105,0 -2,0.895 -2,2v12c0,1.105 0.895,2 2,2h12c1.105,0 2,-0.895 2,-2v-7h-2v3.234z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/document_add.xml
Normal file
9
app/src/main/res/drawable/document_add.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="205dp"
|
||||||
|
android:height="205dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M15,12h-2v-2c0,-0.553 -0.447,-1 -1,-1s-1,0.447 -1,1v2h-2c-0.553,0 -1,0.447 -1,1s0.447,1 1,1h2v2c0,0.553 0.447,1 1,1s1,-0.447 1,-1v-2h2c0.553,0 1,-0.447 1,-1s-0.447,-1 -1,-1zM19.707,7.293l-4,-4c-0.187,-0.188 -0.441,-0.293 -0.707,-0.293h-8c-1.654,0 -3,1.346 -3,3v12c0,1.654 1.346,3 3,3h10c1.654,0 3,-1.346 3,-3v-10c0,-0.266 -0.105,-0.52 -0.293,-0.707zM17.586,8h-1.086c-0.827,0 -1.5,-0.673 -1.5,-1.5v-1.086l2.586,2.586zM17,19h-10c-0.552,0 -1,-0.448 -1,-1v-12c0,-0.552 0.448,-1 1,-1h7v1.5c0,1.379 1.121,2.5 2.5,2.5h1.5v9c0,0.552 -0.448,1 -1,1z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_add.xml
Normal file
10
app/src/main/res/drawable/ic_add.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/textColor"
|
android:fillColor="#FFFFFF"
|
||||||
android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
|
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
|
||||||
</vector>
|
</vector>
|
||||||
12
app/src/main/res/drawable/ic_back.xml
Normal file
12
app/src/main/res/drawable/ic_back.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M224,480h640a32,32 0,1 1,0 64H224a32,32 0,0 1,0 -64z"
|
||||||
|
android:fillColor="@color/textColor"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m237.2,512 l265.4,265.3a32,32 0,0 1,-45.3 45.3l-288,-288a32,32 0,0 1,0 -45.3l288,-288a32,32 0,1 1,45.3 45.3L237.2,512z"
|
||||||
|
android:fillColor="@color/textColor"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_close.xml
Normal file
10
app/src/main/res/drawable/ic_close.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_create_new_folder.xml
Normal file
10
app/src/main/res/drawable/ic_create_new_folder.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18L4,8h16v10zM14,12h-2v-2h-2v2L8,12v2h2v2h2v-2h2z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_delete.xml
Normal file
10
app/src/main/res/drawable/ic_delete.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/textColor"
|
android:fillColor="#FFFFFF"
|
||||||
android:pathData="M14,2H6C4.9,2 4,2.9 4,4v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V8L14,2zM16,18H8v-2h8V18zM16,14H8v-2h8V14zM13,9V3.5L18.5,9H13z"/>
|
android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||||
</vector>
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_folder.xml
Normal file
10
app/src/main/res/drawable/ic_folder.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorPrimary"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_folder_add.xml
Normal file
13
app/src/main/res/drawable/ic_folder_add.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M15,13h-2v2h-2v-2H9v-2h2V9h2v2h2v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/textColor"
|
android:fillColor="#FFFFFF"
|
||||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||||
</vector>
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_no_items.xml
Normal file
10
app/src/main/res/drawable/ic_no_items.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_video.xml
Normal file
10
app/src/main/res/drawable/ic_video.xml
Normal file
@@ -0,0 +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="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
|
||||||
|
</vector>
|
||||||
41
app/src/main/res/drawable/music_add.xml
Normal file
41
app/src/main/res/drawable/music_add.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M17,6.5H20M23,6.5H20M20,6.5V3.5M20,6.5V9.5"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6,16V5L14,4"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15,14V10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M12,19H13C14.105,19 15,18.105 15,17V14H12C10.895,14 10,14.895 10,16V17C10,18.105 10.895,19 12,19Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M3,21H4C5.105,21 6,20.105 6,19V16H3C1.895,16 1,16.895 1,18V19C1,20.105 1.895,21 3,21Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
<path android:fillColor="@color/textColor"
|
<path android:fillColor="?attr/colorPrimary"
|
||||||
android:pathData="M21.409,9.353C23.531,10.507 23.531,13.493 21.409,14.647L8.597,21.615C6.534,22.736 4,21.276 4,18.967L4,5.033C4,2.724 6.534,1.264 8.597,2.385L21.409,9.353Z"/>
|
android:pathData="M21.409,9.353C23.531,10.507 23.531,13.493 21.409,14.647L8.597,21.615C6.534,22.736 4,21.276 4,18.967L4,5.033C4,2.724 6.534,1.264 8.597,2.385L21.409,9.353Z"/>
|
||||||
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
9
app/src/main/res/drawable/video_add.xml
Normal file
9
app/src/main/res/drawable/video_add.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M16,4c0.552,0 1,0.448 1,1v4.2l5.213,-3.65c0.226,-0.158 0.538,-0.103 0.697,0.124 0.058,0.084 0.09,0.184 0.09,0.286v12.08c0,0.276 -0.224,0.5 -0.5,0.5 -0.103,0 -0.203,-0.032 -0.287,-0.09L17,14.8V19c0,0.552 -0.448,1 -1,1H2c-0.552,0 -1,-0.448 -1,-1V5c0,-0.552 0.448,-1 1,-1h14zM8,8v3H5v2h2.999L8,16h2l-0.001,-3H13v-2h-3V8H8z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/wrong.xml
Normal file
9
app/src/main/res/drawable/wrong.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="256"
|
||||||
|
android:viewportHeight="256">
|
||||||
|
<path
|
||||||
|
android:pathData="M142,128l49,-49a9.9,9.9 0,0 0,-14 -14L128,114 79,65A9.9,9.9 0,0 0,65 79l49,49L65,177a9.9,9.9 0,0 0,14 14l49,-49 49,49a9.9,9.9 0,0 0,14 -14Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".activities.AudioGalleryActivity">
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
tools:context=".activities.DocumentsActivity">
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -3,58 +3,143 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
android:padding="8dp" />
|
android:padding="8dp" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/loading"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:visibility="visible"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical">
|
|
||||||
<ProgressBar
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/noItems"
|
android:id="@+id/noItems"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:orientation="vertical">
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_no_items" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/noItemsTxt"
|
android:id="@+id/noItemsTxt"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="16sp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="25dp"
|
android:padding="10dp"
|
||||||
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button"/>
|
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
<LinearLayout
|
||||||
|
android:id="@+id/actionModeBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selectedCount"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnDelete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_delete"
|
||||||
|
android:tint="@android:color/white" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnClose"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:tint="@android:color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabCreateFolder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/create_folder"
|
||||||
|
app:srcCompat="@drawable/ic_create_new_folder" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fabAdd"
|
android:id="@+id/fabAdd"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_marginEnd="25dp"
|
android:layout_margin="16dp"
|
||||||
android:layout_marginBottom="35dp"
|
android:contentDescription="@string/add_files"
|
||||||
android:contentDescription="Compose"
|
app:srcCompat="@drawable/ic_add" />
|
||||||
app:iconPadding="8dp"
|
|
||||||
app:icon="@android:drawable/ic_input_add"
|
|
||||||
android:text="Add file"/>
|
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabAddImage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/add_image"
|
||||||
|
app:srcCompat="@drawable/ic_image" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabAddVideo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/add_video"
|
||||||
|
app:srcCompat="@drawable/ic_video" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabAddAudio"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/add_audio"
|
||||||
|
app:srcCompat="@drawable/ic_audio" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabAddDocument"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/add_document"
|
||||||
|
app:srcCompat="@drawable/ic_document" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
156
app/src/main/res/layout/activity_hidden.xml
Normal file
156
app/src/main/res/layout/activity_hidden.xml
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".activities.HiddenActivity">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:padding="7dp"
|
||||||
|
android:id="@+id/back"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Folder Name"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:id="@+id/folderName"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/noItems"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_no_items" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/noItemsTxt"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addImage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/add_image"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:fabCustomSize="48dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/addVideo"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/addVideo"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/addVideo" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addVideo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:src="@drawable/video_add"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:fabCustomSize="49dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/addAudio"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/addAudio"
|
||||||
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/addAudio" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addAudio"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:src="@drawable/music_add"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
app:fabCustomSize="51dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/addDocument"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/fabExpend"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/fabExpend" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addDocument"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/document_add"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
app:fabCustomSize="54dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/fabExpend"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/addFolder"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/addFolder" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/addFolder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_folder_add"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
app:fabCustomSize="57dp"
|
||||||
|
/>
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabExpend"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:src="@drawable/ic_add"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/add_image"
|
||||||
|
app:fabCustomSize="60dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".activities.ImageGalleryActivity">
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".activities.VideoGalleryActivity">
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
22
app/src/main/res/layout/dialog_input.xml
Normal file
22
app/src/main/res/layout/dialog_input.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/enter_folder_name">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/editText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
48
app/src/main/res/layout/item_folder.xml
Normal file
48
app/src/main/res/layout/item_folder.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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="wrap_content"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/folderIcon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_folder" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/folderName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
60
app/src/main/res/layout/list_item_file.xml
Normal file
60
app/src/main/res/layout/list_item_file.xml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/cardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/fileIconImageView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/add_image" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/videoPlay"
|
||||||
|
android:layout_width="43dp"
|
||||||
|
android:layout_height="43dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/play"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/fileNameTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:text="File Name" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
15
app/src/main/res/layout/list_item_folder.xml
Normal file
15
app/src/main/res/layout/list_item_folder.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/folderNameTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||||
|
android:text="Folder Name" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -5,6 +5,12 @@
|
|||||||
<string name="add_audio">Add Audio</string>
|
<string name="add_audio">Add Audio</string>
|
||||||
<string name="add_video">Add Video</string>
|
<string name="add_video">Add Video</string>
|
||||||
<string name="add_files">Add Files</string>
|
<string name="add_files">Add Files</string>
|
||||||
|
<string name="share">Share</string>
|
||||||
|
<string name="file_options">File Options</string>
|
||||||
|
<string name="rename_file">Rename File</string>
|
||||||
|
<string name="share_file">Share File</string>
|
||||||
|
<string name="add_document">Add Document</string>
|
||||||
|
<string name="failed_to_hide_audio">Failed to hide Audios</string>
|
||||||
<string name="failed_to_hide_documents">Failed to hide Documents</string>
|
<string name="failed_to_hide_documents">Failed to hide Documents</string>
|
||||||
<string name="no_files_selected">No files selected</string>
|
<string name="no_files_selected">No files selected</string>
|
||||||
<string name="documents_hidden_successfully"> Documents hidden successfully</string>
|
<string name="documents_hidden_successfully"> Documents hidden successfully</string>
|
||||||
@@ -46,6 +52,11 @@
|
|||||||
<string name="image">IMAGE</string>
|
<string name="image">IMAGE</string>
|
||||||
<string name="video">VIDEO</string>
|
<string name="video">VIDEO</string>
|
||||||
<string name="audio">AUDIO</string>
|
<string name="audio">AUDIO</string>
|
||||||
|
<string name="delete">Delete</string>
|
||||||
|
<string name="create">Create</string>
|
||||||
|
<string name="delete_folder">Delete Folder</string>
|
||||||
|
<string name="rename">Rename</string>
|
||||||
|
<string name="cannot_delete_folder">Cannot Delete Folder</string>
|
||||||
<string name="document">DOCUMENT</string>
|
<string name="document">DOCUMENT</string>
|
||||||
<string name="no_audio_player_found">No audio player found!</string>
|
<string name="no_audio_player_found">No audio player found!</string>
|
||||||
<string name="no_suitable_app_found_to_open_this_document">No suitable app found to open this document!</string>
|
<string name="no_suitable_app_found_to_open_this_document">No suitable app found to open this document!</string>
|
||||||
@@ -55,4 +66,18 @@
|
|||||||
<string name="no_items_available_add_one_by_clicking_on_the_plus_button">No Items Available, Add one by clicking on the</string>
|
<string name="no_items_available_add_one_by_clicking_on_the_plus_button">No Items Available, Add one by clicking on the</string>
|
||||||
<string name="now_enter_button">Now Enter \'=\' button</string>
|
<string name="now_enter_button">Now Enter \'=\' button</string>
|
||||||
<string name="enter_123456">Enter 123456</string>
|
<string name="enter_123456">Enter 123456</string>
|
||||||
|
<string name="create_folder">Create Folder</string>
|
||||||
|
<string name="enter_folder_name">Enter folder name</string>
|
||||||
|
<string name="folder_already_exists">Folder already exists</string>
|
||||||
|
<string name="folder_options">Folder Options</string>
|
||||||
|
<string name="rename_folder">Rename Folder</string>
|
||||||
|
<string name="enter_new_folder_name">Enter new folder name</string>
|
||||||
|
<string name="failed_to_create_folder">Failed to create folder</string>
|
||||||
|
<string name="are_you_sure_you_want_to_delete_this_folder">Are you sure you want to delete this folder?</string>
|
||||||
|
<string name="yes">Yes</string>
|
||||||
|
<string name="error_loading_files">Error loading files</string>
|
||||||
|
<string name="delete_items">Delete Items</string>
|
||||||
|
<string name="are_you_sure_you_want_to_delete_selected_items">Are you sure you want to delete selected items?</string>
|
||||||
|
<string name="items_deleted_successfully">Items deleted successfully</string>
|
||||||
|
<string name="some_items_could_not_be_deleted">Some items could not be deleted</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user