Changes For Folder Feature

This commit is contained in:
Binondi
2025-06-01 21:37:13 +05:30
parent ab737511a7
commit f8575da2a9
17 changed files with 559 additions and 293 deletions

View File

@@ -20,19 +20,21 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
} }
debug { debug {
isMinifyEnabled = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
@@ -43,6 +45,21 @@ android {
buildFeatures{ buildFeatures{
viewBinding = true viewBinding = true
} }
packaging {
resources {
excludes += listOf(
"META-INF/DEPENDENCIES",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/license.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt",
"META-INF/notice.txt",
"META-INF/ASL2.0"
)
}
}
} }
dependencies { dependencies {
@@ -64,4 +81,5 @@ dependencies {
implementation(libs.photoview) implementation(libs.photoview)
implementation(libs.androidx.viewpager) implementation(libs.androidx.viewpager)
implementation(libs.zoomage) implementation(libs.zoomage)
implementation(libs.lottie)
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
package devs.org.calculator.activities package devs.org.calculator.activities
import android.app.AlertDialog import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
@@ -9,29 +9,33 @@ import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.Settings
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R import devs.org.calculator.R
import devs.org.calculator.adapters.FileAdapter import devs.org.calculator.adapters.FileAdapter
import devs.org.calculator.adapters.FolderAdapter import devs.org.calculator.adapters.FolderAdapter
import devs.org.calculator.callbacks.DialogActionsCallback import devs.org.calculator.callbacks.FileProcessCallback
import devs.org.calculator.databinding.ActivityHiddenBinding import devs.org.calculator.databinding.ActivityHiddenBinding
import devs.org.calculator.databinding.ProccessingDialogBinding
import devs.org.calculator.utils.DialogUtil import devs.org.calculator.utils.DialogUtil
import devs.org.calculator.utils.FileManager import devs.org.calculator.utils.FileManager
import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR
import devs.org.calculator.utils.FolderManager import devs.org.calculator.utils.FolderManager
import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
class HiddenActivity : AppCompatActivity() { class HiddenActivity : AppCompatActivity() {
@@ -45,19 +49,49 @@ class HiddenActivity : AppCompatActivity() {
private val fileManager = FileManager(this, this) private val fileManager = FileManager(this, this)
private val folderManager = FolderManager(this) private val folderManager = FolderManager(this)
private val dialogUtil = DialogUtil(this) private val dialogUtil = DialogUtil(this)
private var customDialog: androidx.appcompat.app.AlertDialog? = null
private val STORAGE_PERMISSION_CODE = 101 private val STORAGE_PERMISSION_CODE = 101
private val PICK_FILE_REQUEST_CODE = 102
private var currentFolder: File? = null private var currentFolder: File? = null
private var folderAdapter: FolderAdapter? = null private var folderAdapter: FolderAdapter? = null
val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR) val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
private var dialogShowTime: Long = 0
private val MINIMUM_DIALOG_DURATION = 1700L
private fun showCustomDialog(i: Int) {
val dialogView = ProccessingDialogBinding.inflate(layoutInflater)
customDialog = MaterialAlertDialogBuilder(this)
.setView(dialogView.root)
.setCancelable(false)
.create()
dialogView.title.text = "Hiding $i files"
customDialog?.show()
dialogShowTime = System.currentTimeMillis()
}
private fun dismissCustomDialog() {
val currentTime = System.currentTimeMillis()
val elapsedTime = currentTime - dialogShowTime
if (elapsedTime < MINIMUM_DIALOG_DURATION) {
val remainingTime = MINIMUM_DIALOG_DURATION - elapsedTime
Handler(Looper.getMainLooper()).postDelayed({
customDialog?.dismiss()
customDialog = null
}, remainingTime)
} else {
customDialog?.dismiss()
customDialog = null
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
binding = ActivityHiddenBinding.inflate(layoutInflater) binding = ActivityHiddenBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
//initialized animations for fabs //initialized animations for fabs
fabOpen = AnimationUtils.loadAnimation(this, R.anim.fab_open) fabOpen = AnimationUtils.loadAnimation(this, R.anim.fab_open)
fabClose = AnimationUtils.loadAnimation(this, R.anim.fab_close) fabClose = AnimationUtils.loadAnimation(this, R.anim.fab_close)
@@ -70,6 +104,7 @@ class HiddenActivity : AppCompatActivity() {
binding.addAudio.visibility = View.GONE binding.addAudio.visibility = View.GONE
binding.addDocument.visibility = View.GONE binding.addDocument.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE binding.addFolder.visibility = View.VISIBLE
binding.deleteSelected.visibility = View.GONE
binding.fabExpend.setOnClickListener { binding.fabExpend.setOnClickListener {
if (isFabOpen) { if (isFabOpen) {
@@ -84,6 +119,13 @@ class HiddenActivity : AppCompatActivity() {
binding.addImage.setOnClickListener { openFilePicker("image/*") } binding.addImage.setOnClickListener { openFilePicker("image/*") }
binding.addVideo.setOnClickListener { openFilePicker("video/*") } binding.addVideo.setOnClickListener { openFilePicker("video/*") }
binding.addAudio.setOnClickListener { openFilePicker("audio/*") } binding.addAudio.setOnClickListener { openFilePicker("audio/*") }
binding.back.setOnClickListener {
if (currentFolder != null) {
pressBack()
} else {
super.onBackPressed()
}
}
binding.addDocument.setOnClickListener { openFilePicker("*/*") } binding.addDocument.setOnClickListener { openFilePicker("*/*") }
binding.addFolder.setOnClickListener { binding.addFolder.setOnClickListener {
dialogUtil.createInputDialog( dialogUtil.createInputDialog(
@@ -101,50 +143,104 @@ class HiddenActivity : AppCompatActivity() {
fileManager.askPermission(this) fileManager.askPermission(this)
listFoldersInHiddenDirectory() listFoldersInHiddenDirectory()
setupDeleteButton()
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()) {
showCustomDialog(uriList.size)
lifecycleScope.launch {
if (currentFolder != null){
FileManager(this@HiddenActivity, this@HiddenActivity)
.processMultipleFiles(uriList, currentFolder!!,
object : FileProcessCallback {
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
Toast.makeText(this@HiddenActivity, "${copiedFiles.size} ${getString(R.string.documents_hidden_successfully)}", Toast.LENGTH_SHORT).show()
openFolder(currentFolder!!)
dismissCustomDialog()
}
override fun onFileProcessFailed() {
Toast.makeText(this@HiddenActivity,
getString(R.string.failed_to_hide_files), Toast.LENGTH_SHORT).show()
dismissCustomDialog()
}
})
}else{
Toast.makeText(
this@HiddenActivity,
getString(R.string.there_was_a_problem_in_the_folder),
Toast.LENGTH_SHORT
).show()
dismissCustomDialog()
}
}
} else {
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
}
}
}
askPermissiom()
}
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
)
}
} }
private fun openFilePicker(mimeType: String) { private fun openFilePicker(mimeType: String) {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = mimeType type = mimeType
addCategory(Intent.CATEGORY_OPENABLE) 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)
} }
startActivityForResult(intent, PICK_FILE_REQUEST_CODE) pickImageLauncher.launch(intent)
}
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() { private fun listFoldersInHiddenDirectory() {
@@ -163,7 +259,16 @@ class HiddenActivity : AppCompatActivity() {
openFolder(clickedFolder) openFolder(clickedFolder)
}, },
onFolderLongClick = { folder -> onFolderLongClick = { folder ->
// go to selection mode // Enter selection mode
binding.fabExpend.visibility = View.GONE
binding.addFolder.visibility = View.GONE
binding.deleteSelected.visibility = View.VISIBLE
},
onSelectionModeChanged = { isSelectionMode ->
if (!isSelectionMode) {
binding.deleteSelected.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE
}
} }
) )
binding.recyclerView.adapter = folderAdapter binding.recyclerView.adapter = folderAdapter
@@ -191,6 +296,7 @@ class HiddenActivity : AppCompatActivity() {
// Read files in the clicked folder and update RecyclerView // Read files in the clicked folder and update RecyclerView
val files = folderManager.getFilesInFolder(folder) val files = folderManager.getFilesInFolder(folder)
Log.d("HiddenActivity", "Found ${files.size} files in ${folder.name}") Log.d("HiddenActivity", "Found ${files.size} files in ${folder.name}")
binding.folderName.text = folder.name
if (files.isNotEmpty()) { if (files.isNotEmpty()) {
binding.recyclerView.layoutManager = GridLayoutManager(this, 3) binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
@@ -253,7 +359,7 @@ class HiddenActivity : AppCompatActivity() {
binding.addVideo.visibility = View.VISIBLE binding.addVideo.visibility = View.VISIBLE
binding.addAudio.visibility = View.VISIBLE binding.addAudio.visibility = View.VISIBLE
binding.addDocument.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 binding.addFolder.visibility = View.VISIBLE
isFabOpen = true isFabOpen = true
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
@@ -279,31 +385,6 @@ class HiddenActivity : AppCompatActivity() {
binding.fabExpend.setImageResource(R.drawable.ic_add) 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? { private fun getFileNameFromUri(uri: Uri): String? {
var name: String? = null var name: String? = null
@@ -318,88 +399,49 @@ class HiddenActivity : AppCompatActivity() {
return name return name
} }
private fun showFileOptionsDialog(file: File) {
val options = arrayOf("Delete", "Rename", "Share") private fun setupDeleteButton() {
AlertDialog.Builder(this) binding.deleteSelected.setOnClickListener {
.setTitle("Choose an action for ${file.name}") val selectedFolders = folderAdapter?.getSelectedItems() ?: emptyList()
.setItems(options) { dialog, which -> if (selectedFolders.isNotEmpty()) {
when (which) { dialogUtil.showMaterialDialog(
0 -> deleteFile(file) getString(R.string.delete_items),
1 -> renameFile(file) getString(R.string.are_you_sure_you_want_to_delete_selected_items),
2 -> shareFile(file) getString(R.string.delete),
getString(R.string.cancel),
object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() {
var allDeleted = true
selectedFolders.forEach { folder ->
if (!folderManager.deleteFolder(folder)) {
allDeleted = false
} }
} }
.create() if (allDeleted) {
.show() Toast.makeText(this@HiddenActivity, getString(R.string.folder_deleted_successfully), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@HiddenActivity, getString(R.string.some_items_could_not_be_deleted), Toast.LENGTH_SHORT).show()
}
folderAdapter?.clearSelection()
binding.deleteSelected.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE
listFoldersInHiddenDirectory()
} }
private fun deleteFile(file: File) { override fun onNegativeButtonClicked() {
Log.d("HiddenActivity", "Deleting file: ${file.name}") // Do nothing
if (file.exists()) { }
if (file.delete()) {
Log.d("HiddenActivity", "File deleted successfully") override fun onNaturalButtonClicked() {
// Refresh the file list in the RecyclerView // Do nothing
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) { private fun pressBack(){
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 currentFolder = null
if (isFabOpen) { if (isFabOpen) {
closeFabs() closeFabs()
@@ -407,6 +449,7 @@ class HiddenActivity : AppCompatActivity() {
if (folderAdapter != null) { if (folderAdapter != null) {
binding.recyclerView.adapter = folderAdapter binding.recyclerView.adapter = folderAdapter
} }
binding.folderName.text = getString(R.string.hidden_space)
listFoldersInHiddenDirectory() listFoldersInHiddenDirectory()
binding.fabExpend.visibility = View.GONE binding.fabExpend.visibility = View.GONE
binding.addImage.visibility = View.GONE binding.addImage.visibility = View.GONE
@@ -414,6 +457,12 @@ class HiddenActivity : AppCompatActivity() {
binding.addAudio.visibility = View.GONE binding.addAudio.visibility = View.GONE
binding.addDocument.visibility = View.GONE binding.addDocument.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE binding.addFolder.visibility = View.VISIBLE
}
@Deprecated("This method has been deprecated in favor of using the\n {@link OnBackPressedDispatcher} via {@link #getOnBackPressedDispatcher()}.\n The OnBackPressedDispatcher controls how back button events are dispatched\n to one or more {@link OnBackPressedCallback} objects.")
override fun onBackPressed() {
if (currentFolder != null) {
pressBack()
} else { } else {
super.onBackPressed() super.onBackPressed()
} }

View File

@@ -16,6 +16,7 @@ import androidx.lifecycle.LifecycleOwner
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 com.google.android.material.dialog.MaterialAlertDialogBuilder
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.utils.FileManager import devs.org.calculator.utils.FileManager
@@ -215,18 +216,20 @@ class FileAdapter(
private fun showFileOptionsDialog(file: File) { private fun showFileOptionsDialog(file: File) {
val options = arrayOf( val options = arrayOf(
context.getString(R.string.delete), context.getString(R.string.un_hide),
context.getString(R.string.rename), context.getString(R.string.rename),
context.getString(R.string.delete),
context.getString(R.string.share) context.getString(R.string.share)
) )
AlertDialog.Builder(context) MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.file_options)) .setTitle(context.getString(R.string.file_options))
.setItems(options) { dialog, which -> .setItems(options) { dialog, which ->
when (which) { when (which) {
0 -> deleteFile(file) 0 -> unHideFile(file)
1 -> renameFile(file) 1 -> renameFile(file)
2 -> shareFile(file) 2 -> deleteFile(file)
3 -> shareFile(file)
} }
dialog.dismiss() dialog.dismiss()
} }
@@ -234,6 +237,20 @@ class FileAdapter(
.show() .show()
} }
private fun unHideFile(file: File) {
FileManager(context, lifecycleOwner).unHideFile(
file = file,
onSuccess = {
fileOperationCallback?.onFileDeleted(file)
},
onError = { errorMessage ->
Toast.makeText(context, "Failed to unhide: $errorMessage", Toast.LENGTH_SHORT).show()
}
)
}
private fun deleteFile(file: File) { private fun deleteFile(file: File) {
if (file.delete()) { if (file.delete()) {
fileOperationCallback?.onFileDeleted(file) fileOperationCallback?.onFileDeleted(file)
@@ -249,7 +266,7 @@ class FileAdapter(
selectAll() selectAll()
} }
AlertDialog.Builder(context) MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.rename_file)) .setTitle(context.getString(R.string.rename_file))
.setView(inputEditText) .setView(inputEditText)
.setPositiveButton(context.getString(R.string.rename)) { dialog, _ -> .setPositiveButton(context.getString(R.string.rename)) { dialog, _ ->

View File

@@ -3,6 +3,7 @@ package devs.org.calculator.adapters
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.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
@@ -12,21 +13,55 @@ import java.io.File
class FolderAdapter( class FolderAdapter(
private val onFolderClick: (File) -> Unit, private val onFolderClick: (File) -> Unit,
private val onFolderLongClick: (File) -> Unit private val onFolderLongClick: (File) -> Unit,
private val onSelectionModeChanged: (Boolean) -> Unit
) : ListAdapter<File, FolderAdapter.FolderViewHolder>(FolderDiffCallback()) { ) : ListAdapter<File, FolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val selectedItems = mutableSetOf<Int>()
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName) private var isSelectionMode = false
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit) { inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
val selectedView: ImageView = itemView.findViewById(R.id.selected)
val selectedLayer: View = itemView.findViewById(R.id.selectedLayer)
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) {
folderNameTextView.text = folder.name folderNameTextView.text = folder.name
itemView.setOnClickListener { onFolderClick(folder) } selectedView.visibility = if (isSelected) View.VISIBLE else View.GONE
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
itemView.setOnClickListener {
if (isSelectionMode) {
toggleSelection(adapterPosition)
} else {
onFolderClick(folder)
}
}
itemView.setOnLongClickListener { itemView.setOnLongClickListener {
if (!isSelectionMode) {
isSelectionMode = true
onSelectionModeChanged(true)
onFolderLongClick(folder) onFolderLongClick(folder)
toggleSelection(adapterPosition)
}
true true
} }
} }
private fun toggleSelection(position: Int) {
if (selectedItems.contains(position)) {
selectedItems.remove(position)
if (selectedItems.isEmpty()) {
isSelectionMode = false
onSelectionModeChanged(false)
}
} else {
selectedItems.add(position)
}
notifyItemChanged(position)
}
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
@@ -36,7 +71,20 @@ class FolderAdapter(
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) { override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
val folder = getItem(position) val folder = getItem(position)
holder.bind(folder, onFolderClick, onFolderLongClick) holder.bind(folder, onFolderClick, onFolderLongClick, selectedItems.contains(position))
}
fun getSelectedItems(): List<File> {
return selectedItems.mapNotNull { position ->
if (position < itemCount) getItem(position) else null
}
}
fun clearSelection() {
selectedItems.clear()
isSelectionMode = false
onSelectionModeChanged(false)
notifyDataSetChanged()
} }
private class FolderDiffCallback : DiffUtil.ItemCallback<File>() { private class FolderDiffCallback : DiffUtil.ItemCallback<File>() {
@@ -45,9 +93,7 @@ class FolderAdapter(
} }
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean { override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
return oldItem.name == newItem.name && return oldItem.name == newItem.name
oldItem.lastModified() == newItem.lastModified() &&
oldItem.length() == newItem.length()
} }
} }
} }

View File

@@ -25,6 +25,7 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import android.Manifest import android.Manifest
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import devs.org.calculator.R
class FileManager(private val context: Context, private val lifecycleOwner: LifecycleOwner) { class FileManager(private val context: Context, private val lifecycleOwner: LifecycleOwner) {
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest> private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
@@ -72,14 +73,12 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList() return typeDir.listFiles()?.filterNotNull()?.filter { it.name != ".nomedia" } ?: emptyList()
} }
private fun copyFileToHiddenDir(uri: Uri, type: FileType, currentDir: File? = null): File? { private fun copyFileToHiddenDir(uri: Uri, folderName: File, currentDir: File? = null): File? {
return try { return try {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
// Get the target directory // Get the target directory (i am using the current opened folder as target folder)
val targetDir = currentDir ?: File(Environment.getExternalStorageDirectory(), "$HIDDEN_DIR/${type.dirName}") val targetDir = folderName
targetDir.mkdirs()
File(targetDir, ".nomedia").createNewFile()
// Create target file // Create target file
val mimeType = contentResolver.getType(uri) val mimeType = contentResolver.getType(uri)
@@ -154,6 +153,64 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
} }
} }
fun unHideFile(file: File, onSuccess: (() -> Unit)? = null, onError: ((String) -> Unit)? = null) {
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
try {
// Create target directory (Downloads)
val targetDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
targetDir.mkdirs()
// Create target file with same name or timestamp
val targetFile = File(targetDir, file.name)
// If file with same name exists, add timestamp
val finalTargetFile = if (targetFile.exists()) {
val nameWithoutExt = file.nameWithoutExtension
val extension = file.extension
File(targetDir, "${nameWithoutExt}_${System.currentTimeMillis()}.${extension}")
} else {
targetFile
}
// Copy file content
file.copyTo(finalTargetFile, overwrite = false)
// Verify copy success
if (finalTargetFile.exists() && finalTargetFile.length() > 0) {
// Delete original hidden file
if (file.delete()) {
// Trigger media scan for the new file
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
mediaScanIntent.data = Uri.fromFile(finalTargetFile)
context.sendBroadcast(mediaScanIntent)
withContext(Dispatchers.Main) {
Toast.makeText(context, context.getString(R.string.file_unhidden_successfully), Toast.LENGTH_SHORT).show()
onSuccess?.invoke() // Call success callback
}
} else {
withContext(Dispatchers.Main) {
Toast.makeText(context, "File copied but failed to remove from hidden folder", Toast.LENGTH_SHORT).show()
onError?.invoke("Failed to remove from hidden folder")
}
}
} else {
withContext(Dispatchers.Main) {
Toast.makeText(context, "Failed to copy file", Toast.LENGTH_SHORT).show()
onError?.invoke("Failed to copy file")
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "Error unhiding file: ${e.message}", Toast.LENGTH_LONG).show()
onError?.invoke(e.message ?: "Unknown error")
}
e.printStackTrace()
}
}
}
suspend fun deletePhotoFromExternalStorage(photoUri: Uri) { suspend fun deletePhotoFromExternalStorage(photoUri: Uri) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
@@ -284,7 +341,7 @@ 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: File,
callback: FileProcessCallback, callback: FileProcessCallback,
currentDir: File? = null currentDir: File? = null
) { ) {

View File

@@ -24,13 +24,11 @@ class FolderManager(private val context: Context) {
fun deleteFolder(folder: File): Boolean { fun deleteFolder(folder: File): Boolean {
return try { return try {
if (folder.exists() && folder.isDirectory) { if (folder.exists() && folder.isDirectory) {
// Delete all files in the folder first
folder.listFiles()?.forEach { file -> folder.listFiles()?.forEach { file ->
if (file.isFile) { if (file.isFile) {
file.delete() file.delete()
} }
} }
// Then delete the folder itself
folder.delete() folder.delete()
} else { } else {
false false

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillAlpha="0.15" android:fillColor="#00363853" android:pathData="M17.88,18.6C17.88,19.926 16.806,21 15.48,21C14.155,21 13.08,19.926 13.08,18.6C13.08,17.274 14.155,16.2 15.48,16.2C16.806,16.2 17.88,17.274 17.88,18.6Z"/>
<path android:fillAlpha="0.15" android:fillColor="#00363853" android:pathData="M9.48,12C9.48,13.325 8.405,14.4 7.08,14.4C5.755,14.4 4.68,13.325 4.68,12C4.68,10.675 5.755,9.6 7.08,9.6C8.405,9.6 9.48,10.675 9.48,12Z"/>
<path android:fillAlpha="0.15" android:fillColor="#00363853" android:pathData="M17.88,5.4C17.88,6.725 16.806,7.8 15.48,7.8C14.155,7.8 13.08,6.725 13.08,5.4C13.08,4.075 14.155,3 15.48,3C16.806,3 17.88,4.075 17.88,5.4Z"/>
<path android:fillColor="#00000000" android:pathData="M18.48,18.537H21M4.68,12L3,12.044M4.68,12C4.68,13.325 5.755,14.4 7.08,14.4C8.405,14.4 9.48,13.325 9.48,12C9.48,10.675 8.405,9.6 7.08,9.6C5.755,9.6 4.68,10.675 4.68,12ZM10.169,12.044H21M12.801,5.551L3,5.551M21,5.551H18.48M3,18.537H12.801M17.88,18.6C17.88,19.926 16.806,21 15.48,21C14.155,21 13.08,19.926 13.08,18.6C13.08,17.274 14.155,16.2 15.48,16.2C16.806,16.2 17.88,17.274 17.88,18.6ZM17.88,5.4C17.88,6.725 16.806,7.8 15.48,7.8C14.155,7.8 13.08,6.725 13.08,5.4C13.08,4.075 14.155,3 15.48,3C16.806,3 17.88,4.075 17.88,5.4Z" android:strokeColor="@color/textColor" android:strokeLineCap="round" android:strokeWidth="1.5"/>
</vector>

View 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="M512,512m-448,0a448,448 0,1 0,896 0,448 448,0 1,0 -896,0Z"
android:fillColor="#00ffffff"/>
<path
android:pathData="M738.1,311.5L448,601.6l-119.5,-119.5 -59.7,59.7 179.2,179.2 349.9,-349.9z"
android:fillColor="@color/textColor"/>
</vector>

View File

@@ -14,6 +14,8 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="8dp" android:padding="8dp"
android:id="@+id/toolBar"
android:layout_marginTop="25dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@@ -29,20 +31,29 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Folder Name" android:text="@string/hidden_space"
android:textSize="18sp" android:textSize="18sp"
android:layout_weight="1"
android:id="@+id/folderName"/> android:id="@+id/folderName"/>
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_setting"
android:scaleType="fitCenter"
android:background="#00000000"
android:padding="9dp"
android:id="@+id/settings"/>
</LinearLayout> </LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginTop="?attr/actionBarSize"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toBottomOf="@+id/toolBar">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
@@ -112,8 +123,8 @@
app:fabCustomSize="51dp" app:fabCustomSize="51dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/addDocument" app:layout_constraintBottom_toTopOf="@+id/addDocument"
app:layout_constraintEnd_toEndOf="@+id/fabExpend" app:layout_constraintEnd_toEndOf="@+id/addDocument"
app:layout_constraintStart_toStartOf="@+id/fabExpend" /> app:layout_constraintStart_toStartOf="@+id/addDocument" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addDocument" android:id="@+id/addDocument"
@@ -125,8 +136,8 @@
app:fabCustomSize="54dp" app:fabCustomSize="54dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/fabExpend" app:layout_constraintBottom_toTopOf="@+id/fabExpend"
app:layout_constraintEnd_toEndOf="@+id/addFolder" app:layout_constraintEnd_toEndOf="@+id/fabExpend"
app:layout_constraintStart_toStartOf="@+id/addFolder" /> app:layout_constraintStart_toStartOf="@+id/fabExpend" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addFolder" android:id="@+id/addFolder"
@@ -138,8 +149,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp" android:layout_marginBottom="20dp"
app:fabCustomSize="57dp" app:fabCustomSize="57dp" />
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabExpend" android:id="@+id/fabExpend"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -153,4 +164,16 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/deleteSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_delete"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp"
app:fabCustomSize="57dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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">
<devs.org.calculator.views.SquareImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
</com.google.android.material.card.MaterialCardView>

View File

@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dp" android:layout_margin="4dp"
@@ -8,16 +13,30 @@
app:cardElevation="2dp"> app:cardElevation="2dp">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:id="@+id/selectedLayer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha="0.2"
android:background="?attr/colorControlNormal"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="8dp" android:padding="8dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@@ -40,9 +59,15 @@
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<ImageView
android:id="@+id/selected"
android:layout_width="35dp"
android:layout_height="35dp"
android:padding="3dp"
android:visibility="gone"
android:src="@drawable/selected"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:paddingVertical="15dp"
android:paddingHorizontal="10dp"
android:gravity="center_horizontal"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hiding_files"
android:padding="8dp"
android:textSize="20sp"/>
<com.airbnb.lottie.LottieAnimationView
android:layout_width="match_parent"
android:layout_height="200dp"
app:lottie_fileName="hiding_files.json"
app:lottie_autoPlay="true"
android:scaleX="1.2"
android:scaleY="1.2"
android:scaleType="centerCrop"
app:lottie_loop="true"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
android:layout_marginBottom="15dp"
android:indeterminate="true"/>
</LinearLayout>

View File

@@ -8,7 +8,11 @@
android:layout_margin="4dp" android:layout_margin="4dp"
app:cardCornerRadius="8dp"> app:cardCornerRadius="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.jsibbold.zoomage.ZoomageView <com.jsibbold.zoomage.ZoomageView
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -25,10 +29,12 @@
/> />
<VideoView <VideoView
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone" android:visibility="gone"
android:layout_gravity="center"
android:id="@+id/videoView"/> android:id="@+id/videoView"/>
</LinearLayout>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/audioBg" android:id="@+id/audioBg"

View File

@@ -10,13 +10,9 @@
<string name="rename_file">Rename File</string> <string name="rename_file">Rename File</string>
<string name="share_file">Share File</string> <string name="share_file">Share File</string>
<string name="add_document">Add Document</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="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">Files hidden successfully</string>
<string name="failed_to_hide_unhide_photo">Failed to hide/unhide photo</string> <string name="failed_to_hide_files">Failed to hide files</string>
<string name="images_hidden_successfully"> Images hidden successfully</string>
<string name="failed_to_hide_images">Failed to hide images</string>
<string name="storage_permissions_granted">Storage permissions granted</string> <string name="storage_permissions_granted">Storage permissions granted</string>
<string name="storage_permissions_denied">Storage permissions denied</string> <string name="storage_permissions_denied">Storage permissions denied</string>
<string name="preview_images">Preview Images</string> <string name="preview_images">Preview Images</string>
@@ -47,37 +43,22 @@
<string name="answer_cannot_be_empty">Answer cannot be empty!</string> <string name="answer_cannot_be_empty">Answer cannot be empty!</string>
<string name="password_successfully_reset">Password successfully reset.</string> <string name="password_successfully_reset">Password successfully reset.</string>
<string name="invalid_answer">Invalid answer!</string> <string name="invalid_answer">Invalid answer!</string>
<string name="videos_hidden_successfully"> Videos hidden successfully</string>
<string name="failed_to_hide_videos">Failed to hide videos</string>
<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="delete">Delete</string>
<string name="create">Create</string> <string name="create">Create</string>
<string name="delete_folder">Delete Folder</string>
<string name="rename">Rename</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>
<string name="unknown_file">Unknown File</string>
<string name="details"> DETAILS</string>
<string name="audio_hidded_successfully">Audios hidden successfully</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="enter_123456">Enter 123456</string> <string name="enter_123456">Enter 123456</string>
<string name="create_folder">Create Folder</string> <string name="now_enter_button">Now Enter \'=\' button</string>
<string name="enter_folder_name">Enter folder name</string> <string name="delete_items">Delete Folder</string>
<string name="folder_already_exists">Folder already exists</string> <string name="are_you_sure_you_want_to_delete_selected_items">Are you sure you want to delete selected Folders?</string>
<string name="folder_options">Folder Options</string> <string name="folder_deleted_successfully">Folder deleted successfully</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> <string name="some_items_could_not_be_deleted">Some items could not be deleted</string>
<string name="hidden_space">Hidden Space</string>
<string name="there_was_a_problem_in_the_folder">There was a problem in the Folder</string>
<string name="file_unhidden_successfully">File Will Now Show In Gallery</string>
</resources> </resources>

View File

@@ -9,6 +9,7 @@ junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.2.1"
espressoCore = "3.6.1" espressoCore = "3.6.1"
appcompat = "1.7.0" appcompat = "1.7.0"
lottie = "6.2.0"
material = "1.12.0" material = "1.12.0"
activity = "1.9.3" activity = "1.9.3"
constraintlayout = "2.2.0" constraintlayout = "2.2.0"
@@ -28,6 +29,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }