Changes For Folder Feature

This commit is contained in:
Binondi
2025-06-03 00:52:41 +05:30
parent 88dcc844c8
commit 4069ddc200
44 changed files with 2608 additions and 1086 deletions

View File

@@ -32,6 +32,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.Calculator"
tools:targetApi="31">
<activity
android:name=".activities.ViewFolderActivity"
android:exported="false" />
<activity
android:name=".activities.SettingsActivity"
android:exported="false" />
@@ -50,9 +53,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.HiddenVaultActivity"
android:exported="true" />
<activity
android:name=".activities.PreviewActivity"
android:configChanges="orientation|screenSize" />

View File

@@ -1,12 +1,23 @@
package devs.org.calculator
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.google.android.material.color.DynamicColors
class CalculatorApp : Application() {
override fun onCreate() {
super.onCreate()
// Apply dynamic colors to enable Material You theming
DynamicColors.applyToActivitiesIfAvailable(this)
// Initialize theme settings
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
// Apply saved theme mode
val themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
AppCompatDelegate.setDefaultNightMode(themeMode)
// Apply dynamic colors only if dynamic theme is enabled
if (prefs.getBoolean("dynamic_theme", true)) {
DynamicColors.applyToActivitiesIfAvailable(this)
}
}
}

View File

@@ -1,484 +1,530 @@
package devs.org.calculator.activities
import android.Manifest
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.provider.Settings
import android.util.Log
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.view.WindowManager
import android.widget.EditText
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R
import devs.org.calculator.adapters.FileAdapter
import devs.org.calculator.adapters.FolderAdapter
import devs.org.calculator.callbacks.FileProcessCallback
import devs.org.calculator.adapters.ListFolderAdapter
import devs.org.calculator.databinding.ActivityHiddenBinding
import devs.org.calculator.databinding.ProccessingDialogBinding
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 kotlinx.coroutines.launch
import devs.org.calculator.utils.PrefsUtil
import java.io.File
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 var customDialog: androidx.appcompat.app.AlertDialog? = null
private val STORAGE_PERMISSION_CODE = 101
private lateinit var fileManager: FileManager
private lateinit var folderManager: FolderManager
private lateinit var dialogUtil: DialogUtil
private var currentFolder: File? = null
private var folderAdapter: FolderAdapter? = null
val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
private var listFolderAdapter: ListFolderAdapter? = null
private 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 val mainHandler = Handler(Looper.getMainLooper())
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
}
companion object {
private const val TAG = "HiddenActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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
// Initialize managers
fileManager = FileManager(this, this)
folderManager = FolderManager(this)
dialogUtil = DialogUtil(this)
setupInitialUIState()
setupClickListeners()
setupBackPressedHandler()
// Initialize permissions and load data
fileManager.askPermission(this)
// Set initial orientation icon based on saved preference
refreshCurrentView()
}
private fun setupInitialUIState() {
binding.addFolder.visibility = View.VISIBLE
binding.deleteSelected.visibility = View.GONE
binding.delete.visibility = View.GONE
binding.menuButton.visibility = View.GONE
}
binding.fabExpend.setOnClickListener {
if (isFabOpen) {
closeFabs()
private fun setupClickListeners() {
} else {
openFabs()
}
}
binding.settings.setOnClickListener {
startActivity(Intent(this, SettingsActivity::class.java))
}
binding.addImage.setOnClickListener { openFilePicker("image/*") }
binding.addVideo.setOnClickListener { openFilePicker("video/*") }
binding.addAudio.setOnClickListener { openFilePicker("audio/*") }
binding.back.setOnClickListener {
if (currentFolder != null) {
pressBack()
handleBackPress()
}
binding.addFolder.setOnClickListener {
createNewFolder()
}
binding.deleteSelected.setOnClickListener {
deleteSelectedItems()
}
binding.delete.setOnClickListener {
deleteSelectedItems()
}
binding.edit.setOnClickListener {
editSelectedFolder()
}
binding.folderOrientation.setOnClickListener {
// Switch between grid mode and list mode
val currentIsList = PrefsUtil(this).getBoolean("isList", false)
val newIsList = !currentIsList
if (newIsList) {
// Switch to list view
showListUI()
PrefsUtil(this).setBoolean("isList", true)
binding.folderOrientation.setImageResource(R.drawable.ic_grid)
} else {
super.onBackPressed()
// Switch to grid view
showGridUI()
PrefsUtil(this).setBoolean("isList", false)
binding.folderOrientation.setImageResource(R.drawable.ic_list)
}
}
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)
private fun showGridUI() {
listFoldersInHiddenDirectory()
}
setupDeleteButton()
private fun showListUI() {
listFoldersInHiddenDirectoryListStyle()
}
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val clipData = result.data?.clipData
val uriList = mutableListOf<Uri>()
private fun listFoldersInHiddenDirectoryListStyle() {
try {
if (!hiddenDir.exists()) {
fileManager.getHiddenDirectory()
}
if (clipData != null) {
for (i in 0 until clipData.itemCount) {
val uri = clipData.getItemAt(i).uri
uriList.add(uri)
}
if (hiddenDir.exists() && hiddenDir.isDirectory) {
val folders = folderManager.getFoldersInDirectory(hiddenDir)
if (folders.isNotEmpty()) {
showFolderListStyle(folders)
} else {
result.data?.data?.let { uriList.add(it) }
showEmptyState()
}
} else {
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
showEmptyState()
}
} catch (e: Exception) {
Log.e(TAG, "Error listing folders: ${e.message}")
showEmptyState()
}
}
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()
}
private fun setupBackPressedHandler() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
handleBackPress()
}
})
}
override fun onFileProcessFailed() {
Toast.makeText(this@HiddenActivity,
getString(R.string.failed_to_hide_files), Toast.LENGTH_SHORT).show()
dismissCustomDialog()
}
})
}else{
private fun createNewFolder() {
dialogUtil.createInputDialog(
title = "Enter Folder Name To Create",
hint = "",
callback = object : DialogUtil.InputDialogCallback {
override fun onPositiveButtonClicked(input: String) {
if (input.trim().isNotEmpty()) {
try {
folderManager.createFolder(hiddenDir, input.trim())
refreshCurrentView()
} catch (e: Exception) {
Log.e(TAG, "Error creating folder: ${e.message}")
Toast.makeText(
this@HiddenActivity,
getString(R.string.there_was_a_problem_in_the_folder),
"Failed to create 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()
}
override fun onResume() {
super.onResume()
setupFlagSecure()
}
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 setupFlagSecure() {
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
if (prefs.getBoolean("screenshot_restriction", true)) {
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}
}
private fun openFilePicker(mimeType: String) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = mimeType
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)
}
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 ->
// 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
}
// Submit new list to adapter - DiffUtil will handle the comparison
folderAdapter?.submitList(folders)
} else {
binding.noItems.visibility = View.VISIBLE
binding.recyclerView.visibility = View.GONE
try {
if (!hiddenDir.exists()) {
fileManager.getHiddenDirectory()
}
} else if (!hiddenDir.exists()) {
fileManager.getHiddenDirectory()
} else {
Log.e("HiddenActivity", "Hidden directory is not a directory: ${hiddenDir.absolutePath}")
if (hiddenDir.exists() && hiddenDir.isDirectory) {
val folders = folderManager.getFoldersInDirectory(hiddenDir)
if (folders.isNotEmpty()) {
showFolderList(folders)
} else {
showEmptyState()
}
} else {
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
showEmptyState()
}
} catch (e: Exception) {
Log.e(TAG, "Error listing folders: ${e.message}")
showEmptyState()
}
}
private fun openFolder(folder: File) {
Log.d("HiddenActivity", "Opening folder: ${folder.name}")
currentFolder = folder
binding.addFolder.visibility = View.GONE
binding.fabExpend.visibility = View.VISIBLE
private fun showFolderList(folders: List<File>) {
binding.noItems.visibility = View.GONE
binding.recyclerView.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}")
binding.folderName.text = folder.name
// Clear the existing adapter to avoid conflicts
listFolderAdapter = null
if (files.isNotEmpty()) {
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
binding.recyclerView.layoutManager = GridLayoutManager(this, 2)
folderAdapter = FolderAdapter(
onFolderClick = { clickedFolder ->
startActivity(Intent(this,ViewFolderActivity::class.java).putExtra("folder",clickedFolder.toString()))
},
onFolderLongClick = {
enterFolderSelectionMode()
},
onSelectionModeChanged = { isSelectionMode ->
handleFolderSelectionModeChange(isSelectionMode)
},
onSelectionCountChanged = { selectedCount ->
updateEditButtonVisibility()
}
)
binding.recyclerView.adapter = folderAdapter
folderAdapter?.submitList(folders)
val fileAdapter = FileAdapter(this, this, folder).apply {
fileOperationCallback = object : FileAdapter.FileOperationCallback {
override fun onFileDeleted(file: File) {
// Refresh the file list
refreshCurrentFolder()
// Ensure proper icon state for folder view
if (folderAdapter?.isInSelectionMode() != true) {
showFolderViewIcons()
}
}
private fun showFolderListStyle(folders: List<File>) {
binding.noItems.visibility = View.GONE
binding.recyclerView.visibility = View.VISIBLE
// Clear the existing adapter to avoid conflicts
folderAdapter = null
binding.recyclerView.layoutManager = GridLayoutManager(this, 1)
listFolderAdapter = ListFolderAdapter(
onFolderClick = { clickedFolder ->
startActivity(Intent(this,ViewFolderActivity::class.java).putExtra("folder",clickedFolder.toString()))
},
onFolderLongClick = {
enterFolderSelectionMode()
},
onSelectionModeChanged = { isSelectionMode ->
handleFolderSelectionModeChange(isSelectionMode)
},
onSelectionCountChanged = { selectedCount ->
updateEditButtonVisibility()
}
)
binding.recyclerView.adapter = listFolderAdapter
listFolderAdapter?.submitList(folders)
// Ensure proper icon state for folder view
if (listFolderAdapter?.isInSelectionMode() != true) {
showFolderViewIcons()
}
}
private fun updateEditButtonVisibility() {
val selectedCount = when {
folderAdapter != null -> folderAdapter?.getSelectedItems()?.size ?: 0
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems()?.size ?: 0
else -> 0
}
binding.edit.visibility = if (selectedCount == 1) View.VISIBLE else View.GONE
}
private fun showEmptyState() {
binding.noItems.visibility = View.VISIBLE
binding.recyclerView.visibility = View.GONE
}
private fun enterFolderSelectionMode() {
showFolderSelectionIcons()
}
private fun refreshCurrentView() {
val isList = PrefsUtil(this).getBoolean("isList", false)
if (isList) {
binding.folderOrientation.setImageResource(R.drawable.ic_grid)
listFoldersInHiddenDirectoryListStyle()
} else {
binding.folderOrientation.setImageResource(R.drawable.ic_list)
listFoldersInHiddenDirectory()
}
}
private fun deleteSelectedItems() {
deleteSelectedFolders()
}
private fun deleteSelectedFolders() {
val selectedFolders = when {
folderAdapter != null -> folderAdapter?.getSelectedItems() ?: emptyList()
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems() ?: emptyList()
else -> emptyList()
}
if (selectedFolders.isNotEmpty()) {
dialogUtil.showMaterialDialog(
getString(R.string.delete_items),
getString(R.string.are_you_sure_you_want_to_delete_selected_items),
getString(R.string.delete),
getString(R.string.cancel),
object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() {
performFolderDeletion(selectedFolders)
}
override fun onFileRenamed(oldFile: File, newFile: File) {
// Refresh the file list
refreshCurrentFolder()
override fun onNegativeButtonClicked() {
// Do nothing
}
override fun onRefreshNeeded() {
// Refresh the file list
refreshCurrentFolder()
}
override fun onSelectionModeChanged(
isSelectionMode: Boolean,
selectedCount: Int
) {
}
override fun onSelectionCountChanged(selectedCount: Int) {
override fun onNaturalButtonClicked() {
// Do nothing
}
}
)
}
}
submitList(files)
private fun performFolderDeletion(selectedFolders: List<File>) {
var allDeleted = true
selectedFolders.forEach { folder ->
if (!folderManager.deleteFolder(folder)) {
allDeleted = false
Log.e(TAG, "Failed to delete folder: ${folder.name}")
}
}
binding.recyclerView.adapter = fileAdapter
binding.recyclerView.visibility = View.VISIBLE
binding.noItems.visibility = View.GONE
val message = if (allDeleted) {
getString(R.string.folder_deleted_successfully)
} else {
binding.recyclerView.visibility = View.GONE
binding.noItems.visibility = View.VISIBLE
getString(R.string.some_items_could_not_be_deleted)
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Clear selection from both adapters
folderAdapter?.clearSelection()
listFolderAdapter?.clearSelection()
// This will trigger the selection mode change callback and show proper icons
exitFolderSelectionMode()
// Refresh the current view based on orientation
refreshCurrentView()
}
private fun refreshCurrentFolder() {
currentFolder?.let { folder ->
val files = folderManager.getFilesInFolder(folder)
(binding.recyclerView.adapter as? FileAdapter)?.submitList(files)
private fun handleBackPress() {
if (files.isEmpty()) {
binding.recyclerView.visibility = View.GONE
binding.noItems.visibility = View.VISIBLE
} else {
binding.recyclerView.visibility = View.VISIBLE
binding.noItems.visibility = View.GONE
}
// Check if folder adapters are in selection mode
if (folderAdapter?.onBackPressed() == true || listFolderAdapter?.onBackPressed() == true) {
return
}
}
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
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 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 setupDeleteButton() {
binding.deleteSelected.setOnClickListener {
val selectedFolders = folderAdapter?.getSelectedItems() ?: emptyList()
if (selectedFolders.isNotEmpty()) {
dialogUtil.showMaterialDialog(
getString(R.string.delete_items),
getString(R.string.are_you_sure_you_want_to_delete_selected_items),
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
}
}
if (allDeleted) {
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()
}
override fun onNegativeButtonClicked() {
// Do nothing
}
override fun onNaturalButtonClicked() {
// Do nothing
}
}
)
}
}
}
private fun pressBack(){
currentFolder = null
if (isFabOpen) {
closeFabs()
}
if (folderAdapter != null) {
binding.recyclerView.adapter = folderAdapter
}
binding.folderName.text = getString(R.string.hidden_space)
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
}
@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() {
// Handle navigation back
if (currentFolder != null) {
pressBack()
navigateBackToFolders()
} else {
super.onBackPressed()
finish()
}
}
private fun navigateBackToFolders() {
currentFolder = null
// Clean up file adapter
refreshCurrentView()
binding.folderName.text = getString(R.string.hidden_space)
// Set proper icons for folder view
showFolderViewIcons()
}
override fun onDestroy() {
super.onDestroy()
// Remove any pending callbacks
mainHandler.removeCallbacksAndMessages(null)
}
//visibility related code
private fun showFolderViewIcons() {
binding.folderOrientation.visibility = View.VISIBLE
binding.settings.visibility = View.VISIBLE
binding.delete.visibility = View.GONE
binding.deleteSelected.visibility = View.GONE
binding.menuButton.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE
binding.edit.visibility = View.GONE
// Ensure FABs are properly managed
if (currentFolder == null) {
binding.addFolder.visibility = View.VISIBLE
}
}
private fun showFolderSelectionIcons() {
binding.folderOrientation.visibility = View.GONE
binding.settings.visibility = View.GONE
binding.delete.visibility = View.VISIBLE
binding.deleteSelected.visibility = View.VISIBLE
binding.menuButton.visibility = View.GONE
binding.addFolder.visibility = View.GONE
// Update edit button visibility based on current selection count
updateEditButtonVisibility()
}
private fun exitFolderSelectionMode() {
showFolderViewIcons()
}
private fun handleFolderSelectionModeChange(isSelectionMode: Boolean) {
if (!isSelectionMode) {
exitFolderSelectionMode()
} else {
enterFolderSelectionMode()
}
// Always update edit button visibility when selection mode changes
updateEditButtonVisibility()
}
private fun editSelectedFolder() {
val selectedFolders = when {
folderAdapter != null -> folderAdapter?.getSelectedItems() ?: emptyList()
listFolderAdapter != null -> listFolderAdapter?.getSelectedItems() ?: emptyList()
else -> emptyList()
}
if (selectedFolders.size != 1) {
Toast.makeText(this, "Please select exactly one folder to edit", Toast.LENGTH_SHORT).show()
return
}
val folder = selectedFolders[0]
showEditFolderDialog(folder)
}
private fun showEditFolderDialog(folder: File) {
val inputEditText = EditText(this).apply {
setText(folder.name)
selectAll()
}
MaterialAlertDialogBuilder(this)
.setTitle("Rename Folder")
.setView(inputEditText)
.setPositiveButton("Rename") { dialog, _ ->
val newName = inputEditText.text.toString().trim()
if (newName.isNotEmpty() && newName != folder.name) {
if (isValidFolderName(newName)) {
renameFolder(folder, newName)
} else {
Toast.makeText(this, "Invalid folder name", Toast.LENGTH_SHORT).show()
}
}
dialog.dismiss()
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.cancel()
}
.show()
}
private fun isValidFolderName(folderName: String): Boolean {
val forbiddenChars = charArrayOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
return folderName.isNotBlank() &&
folderName.none { it in forbiddenChars } &&
!folderName.startsWith(".") &&
folderName.length <= 255
}
private fun renameFolder(oldFolder: File, newName: String) {
val parentDir = oldFolder.parentFile
if (parentDir != null) {
val newFolder = File(parentDir, newName)
if (newFolder.exists()) {
Toast.makeText(this, "Folder with this name already exists", Toast.LENGTH_SHORT).show()
return
}
if (oldFolder.renameTo(newFolder)) {
// Clear selection from both adapters
folderAdapter?.clearSelection()
listFolderAdapter?.clearSelection()
// Exit selection mode
exitFolderSelectionMode()
refreshCurrentView()
} else {
Toast.makeText(this, "Failed to rename folder", Toast.LENGTH_SHORT).show()
}
}
}
}

View File

@@ -1,24 +0,0 @@
package devs.org.calculator.activities
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import devs.org.calculator.databinding.ActivityHiddenVaultBinding
import devs.org.calculator.utils.FileManager
class HiddenVaultActivity : AppCompatActivity() {
private lateinit var binding: ActivityHiddenVaultBinding
private lateinit var fileManager: FileManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHiddenVaultBinding.inflate(layoutInflater)
setContentView(binding.root)
fileManager = FileManager(this, this)
setupNavigation()
}
private fun setupNavigation() {
}
}

View File

@@ -2,6 +2,7 @@ package devs.org.calculator.activities
import android.net.Uri
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
@@ -30,6 +31,7 @@ class PreviewActivity : AppCompatActivity() {
binding = ActivityPreviewBinding.inflate(layoutInflater)
setContentView(binding.root)
fileManager = FileManager(this, this)
currentPosition = intent.getIntExtra("position", 0)
@@ -55,6 +57,21 @@ class PreviewActivity : AppCompatActivity() {
}
override fun onResume() {
super.onResume()
setupFlagSecure()
}
private fun setupFlagSecure() {
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
if (prefs.getBoolean("screenshot_restriction", true)) {
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}
}
private fun setupFileType() {
when (type) {
"IMAGE" -> {

View File

@@ -1,20 +1,154 @@
package devs.org.calculator.activities
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import devs.org.calculator.R
import devs.org.calculator.databinding.ActivitySettingsBinding
class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding
private lateinit var prefs: SharedPreferences
private val DEV_GITHUB_URL = "https://github.com/binondi"
private val GITHUB_URL = "$DEV_GITHUB_URL/calculator-hide-files"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
setupUI()
loadSettings()
setupListeners()
}
private fun setupUI() {
binding.back.setOnClickListener {
onBackPressed()
}
}
private fun loadSettings() {
binding.dynamicThemeSwitch.isChecked = prefs.getBoolean("dynamic_theme", true)
val themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
binding.themeModeSwitch.isChecked = themeMode != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
when (themeMode) {
AppCompatDelegate.MODE_NIGHT_YES -> binding.darkThemeRadio.isChecked = true
AppCompatDelegate.MODE_NIGHT_NO -> binding.lightThemeRadio.isChecked = true
else -> binding.systemThemeRadio.isChecked = true
}
binding.screenshotRestrictionSwitch.isChecked = prefs.getBoolean("screenshot_restriction", true)
binding.showFileNames.isChecked = prefs.getBoolean("showFileName", true)
updateThemeModeVisibility()
}
private fun setupListeners() {
binding.githubButton.setOnClickListener {
openUrl(GITHUB_URL)
}
binding.devGithubButton.setOnClickListener {
openUrl(DEV_GITHUB_URL)
}
binding.dynamicThemeSwitch.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("dynamic_theme", isChecked).apply()
if (!isChecked) {
showThemeModeDialog()
}
}
binding.themeModeSwitch.setOnCheckedChangeListener { _, isChecked ->
binding.themeRadioGroup.visibility = if (isChecked) View.VISIBLE else View.GONE
if (!isChecked) {
binding.systemThemeRadio.isChecked = true
applyThemeMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
}
binding.themeRadioGroup.setOnCheckedChangeListener { _, checkedId ->
val themeMode = when (checkedId) {
R.id.lightThemeRadio -> AppCompatDelegate.MODE_NIGHT_NO
R.id.darkThemeRadio -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
applyThemeMode(themeMode)
}
binding.screenshotRestrictionSwitch.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("screenshot_restriction", isChecked).apply()
if (isChecked) {
enableScreenshotRestriction()
} else {
disableScreenshotRestriction()
}
}
binding.showFileNames.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("showFileName", isChecked).apply()
}
}
private fun updateThemeModeVisibility() {
binding.themeRadioGroup.visibility = if (binding.themeModeSwitch.isChecked) View.VISIBLE else View.GONE
}
private fun showThemeModeDialog() {
MaterialAlertDialogBuilder(this)
.setTitle("Theme Mode")
.setMessage("Would you like to set a specific theme mode?")
.setPositiveButton("Yes") { _, _ ->
binding.themeModeSwitch.isChecked = true
}
.setNegativeButton("No") { _, _ ->
binding.systemThemeRadio.isChecked = true
applyThemeMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
.show()
}
private fun applyThemeMode(themeMode: Int) {
prefs.edit().putInt("theme_mode", themeMode).apply()
AppCompatDelegate.setDefaultNightMode(themeMode)
}
private fun enableScreenshotRestriction() {
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}
private fun disableScreenshotRestriction() {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
private fun openUrl(url: String) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} catch (e: Exception) {
Snackbar.make(binding.root, "Could not open URL", Snackbar.LENGTH_SHORT).show()
}
}
}

View File

@@ -0,0 +1,596 @@
package devs.org.calculator.activities
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R
import devs.org.calculator.adapters.FileAdapter
import devs.org.calculator.callbacks.FileProcessCallback
import devs.org.calculator.databinding.ActivityViewFolderBinding
import devs.org.calculator.databinding.ProccessingDialogBinding
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 devs.org.calculator.utils.PrefsUtil
import kotlinx.coroutines.launch
import java.io.File
class ViewFolderActivity : 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: ActivityViewFolderBinding
private val mainHandler = Handler(Looper.getMainLooper())
private lateinit var fileManager: FileManager
private lateinit var folderManager: FolderManager
private lateinit var dialogUtil: DialogUtil
private var fileAdapter: FileAdapter? = null
private var currentFolder: File? = null
private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
private lateinit var prefs: SharedPreferences
private var customDialog: androidx.appcompat.app.AlertDialog? = null
private var dialogShowTime: Long = 0
private val MINIMUM_DIALOG_DURATION = 1700L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityViewFolderBinding.inflate(layoutInflater)
setContentView(binding.root)
setupAnimations()
initialize()
setupClickListeners()
closeFabs()
val folder = intent.getStringExtra("folder").toString()
currentFolder = File(folder)
if (currentFolder != null){
openFolder(currentFolder!!)
}else {
showEmptyState()
}
setupActivityResultLaunchers()
}
private fun initialize() {
fileManager = FileManager(this, this)
folderManager = FolderManager(this)
dialogUtil = DialogUtil(this)
prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
}
private fun setupActivityResultLaunchers() {
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
handleFilePickerResult(result.data)
}
}
}
private fun handleFilePickerResult(data: Intent?) {
val clipData = 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 {
data?.data?.let { uriList.add(it) }
}
if (uriList.isNotEmpty()) {
processSelectedFiles(uriList)
} else {
Toast.makeText(this, getString(R.string.no_files_selected), Toast.LENGTH_SHORT).show()
}
}
private fun showCustomDialog(count: Int) {
val dialogView = ProccessingDialogBinding.inflate(layoutInflater)
customDialog = MaterialAlertDialogBuilder(this)
.setView(dialogView.root)
.setCancelable(false)
.create()
dialogView.title.text = "Hiding $count 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
mainHandler.postDelayed({
customDialog?.dismiss()
customDialog = null
}, remainingTime)
} else {
customDialog?.dismiss()
customDialog = null
}
}
private fun processSelectedFiles(uriList: List<Uri>) {
val targetFolder = currentFolder ?: hiddenDir
showCustomDialog(uriList.size)
lifecycleScope.launch {
try {
fileManager.processMultipleFiles(uriList, targetFolder,
object : FileProcessCallback {
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
mainHandler.post {
refreshCurrentView()
dismissCustomDialog()
}
}
override fun onFileProcessFailed() {
mainHandler.post {
Toast.makeText(
this@ViewFolderActivity,
getString(R.string.failed_to_hide_files),
Toast.LENGTH_SHORT
).show()
dismissCustomDialog()
}
}
})
} catch (e: Exception) {
mainHandler.post {
Toast.makeText(
this@ViewFolderActivity,
getString(R.string.there_was_a_problem_in_the_folder),
Toast.LENGTH_SHORT
).show()
dismissCustomDialog()
}
}
}
}
private fun refreshCurrentView() {
if (currentFolder != null) {
refreshCurrentFolder()
}
}
override fun onResume() {
super.onResume()
refreshCurrentFolder()
}
private fun openFolder(folder: File) {
val files = folderManager.getFilesInFolder(folder)
binding.folderName.text = folder.name
if (files.isNotEmpty()) {
showFileList(files, folder)
} else {
showEmptyState()
}
}
private fun showEmptyState() {
binding.noItems.visibility = View.VISIBLE
binding.recyclerView.visibility = View.GONE
}
private fun showFileList(files: List<File>, folder: File) {
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
// Clean up previous adapter
fileAdapter?.cleanup()
fileAdapter = FileAdapter(this, this, folder, prefs.getBoolean("showFileName", true),
onFolderLongClick = { isSelected ->
handleFileSelectionModeChange(isSelected, 0)
}).apply {
setFileOperationCallback(object : FileAdapter.FileOperationCallback {
override fun onFileDeleted(file: File) {
refreshCurrentFolder()
}
override fun onFileRenamed(oldFile: File, newFile: File) {
refreshCurrentFolder()
}
override fun onRefreshNeeded() {
refreshCurrentFolder()
}
override fun onSelectionModeChanged(isSelectionMode: Boolean, selectedCount: Int) {
handleFileSelectionModeChange(isSelectionMode, selectedCount)
}
override fun onSelectionCountChanged(selectedCount: Int) {
updateSelectionCountDisplay(selectedCount)
}
})
submitList(files)
}
binding.recyclerView.adapter = fileAdapter
binding.recyclerView.visibility = View.VISIBLE
binding.noItems.visibility = View.GONE
// Setup menu button click listener
binding.menuButton.setOnClickListener {
fileAdapter?.let { adapter ->
showFileOptionsMenu(adapter.getSelectedItems())
}
}
// Set initial UI state
showFileViewIcons()
}
@SuppressLint("MissingSuperCall")
override fun onBackPressed() {
handleBackPress()
}
private fun handleBackPress() {
if (fileAdapter?.onBackPressed() == true) {
return
}
super.onBackPressed()
}
private fun handleFileSelectionModeChange(isSelectionMode: Boolean, selectedCount: Int) {
if (isSelectionMode) {
showFileSelectionIcons()
} else {
showFileViewIcons()
}
}
private fun updateSelectionCountDisplay(selectedCount: Int) {
// Update any UI elements that show selection count
if (selectedCount > 0) {
showFileSelectionIcons()
} else {
showFileViewIcons()
}
}
private fun showFileOptionsMenu(selectedFiles: List<File>) {
if (selectedFiles.isEmpty()) return
val options = arrayOf(
getString(R.string.un_hide),
getString(R.string.delete),
getString(R.string.copy_to_another_folder),
getString(R.string.move_to_another_folder)
)
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.file_options))
.setItems(options) { _, which ->
when (which) {
0 -> unhideSelectedFiles(selectedFiles)
1 -> deleteSelectedFiles(selectedFiles)
2 -> copyToAnotherFolder(selectedFiles)
3 -> moveToAnotherFolder(selectedFiles)
}
}
.show()
}
private fun moveToAnotherFolder(selectedFiles: List<File>) {
showFolderSelectionDialog { destinationFolder ->
moveFilesToFolder(selectedFiles, destinationFolder)
}
}
private fun unhideSelectedFiles(selectedFiles: List<File>) {
dialogUtil.showMaterialDialog(
getString(R.string.un_hide_files),
getString(R.string.are_you_sure_you_want_to_un_hide_selected_files),
getString(R.string.un_hide),
getString(R.string.cancel),
object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() {
performFileUnhiding(selectedFiles)
}
override fun onNegativeButtonClicked() {
// Do nothing
}
override fun onNaturalButtonClicked() {
// Do nothing
}
}
)
}
private fun deleteSelectedFiles(selectedFiles: List<File>) {
dialogUtil.showMaterialDialog(
getString(R.string.delete_items),
getString(R.string.are_you_sure_you_want_to_delete_selected_items),
getString(R.string.delete),
getString(R.string.cancel),
object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() {
performFileDeletion(selectedFiles)
}
override fun onNegativeButtonClicked() {
// Do nothing
}
override fun onNaturalButtonClicked() {
// Do nothing
}
}
)
}
private fun refreshCurrentFolder() {
currentFolder?.let { folder ->
val files = folderManager.getFilesInFolder(folder)
fileAdapter?.submitList(files)
if (files.isEmpty()) {
showEmptyState()
} else {
binding.recyclerView.visibility = View.VISIBLE
binding.noItems.visibility = View.GONE
fileAdapter?.let { adapter ->
if (adapter.isInSelectionMode()) {
showFileSelectionIcons()
} else {
showFileViewIcons()
}
}
}
}
}
private fun setupClickListeners() {
binding.fabExpend.setOnClickListener {
if (isFabOpen) closeFabs()
else openFabs()
}
binding.back.setOnClickListener {
finish()
}
binding.addImage.setOnClickListener { openFilePicker("image/*") }
binding.addVideo.setOnClickListener { openFilePicker("video/*") }
binding.addAudio.setOnClickListener { openFilePicker("audio/*") }
binding.addDocument.setOnClickListener { openFilePicker("*/*") }
}
private fun setupAnimations() {
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)
}
private fun openFilePicker(mimeType: String) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = mimeType
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)
}
private fun openFabs() {
if (!isFabOpen) {
binding.addImage.startAnimation(fabOpen)
binding.addVideo.startAnimation(fabOpen)
binding.addAudio.startAnimation(fabOpen)
binding.addDocument.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
isFabOpen = true
mainHandler.postDelayed({
binding.fabExpend.setImageResource(R.drawable.wrong)
}, 200)
}
}
private fun closeFabs() {
if (isFabOpen) {
binding.addImage.startAnimation(fabClose)
binding.addVideo.startAnimation(fabClose)
binding.addAudio.startAnimation(fabClose)
binding.addDocument.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
isFabOpen = false
binding.fabExpend.setImageResource(R.drawable.ic_add)
}
}
private fun showFileViewIcons() {
binding.menuButton.visibility = View.GONE
binding.fabExpend.visibility = View.VISIBLE
binding.addImage.visibility = View.INVISIBLE
binding.addVideo.visibility = View.INVISIBLE
binding.addAudio.visibility = View.INVISIBLE
binding.addDocument.visibility = View.INVISIBLE
isFabOpen = false
binding.fabExpend.setImageResource(R.drawable.ic_add)
}
private fun showFileSelectionIcons() {
binding.menuButton.visibility = View.VISIBLE
binding.fabExpend.visibility = View.GONE
binding.addImage.visibility = View.INVISIBLE
binding.addVideo.visibility = View.INVISIBLE
binding.addAudio.visibility = View.INVISIBLE
binding.addDocument.visibility = View.INVISIBLE
isFabOpen = false
}
private fun performFileUnhiding(selectedFiles: List<File>) {
lifecycleScope.launch {
var allUnhidden = true
selectedFiles.forEach { file ->
try {
val fileUri = Uri.fromFile(file)
val result = fileManager.copyFileToNormalDir(fileUri)
if (result == null) {
allUnhidden = false
} else {
// Delete the hidden file after successful copy
file.delete()
}
} catch (e: Exception) {
allUnhidden = false
}
}
mainHandler.post {
val message = if (allUnhidden) {
getString(R.string.files_unhidden_successfully)
} else {
getString(R.string.some_files_could_not_be_unhidden)
}
Toast.makeText(this@ViewFolderActivity, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode()
refreshCurrentFolder()
}
}
}
private fun performFileDeletion(selectedFiles: List<File>) {
var allDeleted = true
selectedFiles.forEach { file ->
if (!file.delete()) {
allDeleted = false
}
}
val message = if (allDeleted) {
getString(R.string.files_deleted_successfully)
} else {
getString(R.string.some_items_could_not_be_deleted)
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode()
refreshCurrentFolder()
}
private fun copyToAnotherFolder(selectedFiles: List<File>) {
showFolderSelectionDialog { destinationFolder ->
copyFilesToFolder(selectedFiles, destinationFolder)
}
}
private fun copyFilesToFolder(selectedFiles: List<File>, destinationFolder: File) {
var allCopied = true
selectedFiles.forEach { file ->
try {
val newFile = File(destinationFolder, file.name)
file.copyTo(newFile, overwrite = true)
} catch (e: Exception) {
allCopied = false
}
}
val message = if (allCopied) "Files copied successfully" else "Some files could not be copied"
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode()
refreshCurrentFolder()
}
private fun moveFilesToFolder(selectedFiles: List<File>, destinationFolder: File) {
var allMoved = true
selectedFiles.forEach { file ->
try {
val newFile = File(destinationFolder, file.name)
file.copyTo(newFile, overwrite = true)
file.delete()
} catch (e: Exception) {
allMoved = false
}
}
val message = if (allMoved) "Files moved successfully" else "Some files could not be moved"
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode()
refreshCurrentFolder()
}
private fun showFolderSelectionDialog(onFolderSelected: (File) -> Unit) {
val folders = folderManager.getFoldersInDirectory(hiddenDir)
.filter { it != currentFolder } // Exclude current folder
if (folders.isEmpty()) {
Toast.makeText(this, getString(R.string.no_folders_available), Toast.LENGTH_SHORT).show()
return
}
val folderNames = folders.map { it.name }.toTypedArray()
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.select_destination_folder))
.setItems(folderNames) { _, which ->
onFolderSelected(folders[which])
}
.show()
}
}

View File

@@ -1,9 +1,10 @@
package devs.org.calculator.adapters
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -16,21 +17,37 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R
import devs.org.calculator.activities.PreviewActivity
import devs.org.calculator.utils.FileManager
import java.io.File
import java.lang.ref.WeakReference
import java.util.concurrent.Executors
class FileAdapter(
private val context: Context,
private val lifecycleOwner: LifecycleOwner,
private val currentFolder: File
private val currentFolder: File,
private val showFileName: Boolean,
private val onFolderLongClick: (Boolean) -> Unit
) : ListAdapter<File, FileAdapter.FileViewHolder>(FileDiffCallback()) {
private val selectedItems = mutableSetOf<Int>()
private var isSelectionMode = false
// Use WeakReference to prevent memory leaks
private var fileOperationCallback: WeakReference<FileOperationCallback>? = null
// Background executor for file operations
private val fileExecutor = Executors.newSingleThreadExecutor()
private val mainHandler = Handler(Looper.getMainLooper())
companion object {
private const val TAG = "FileAdapter"
}
// Callback interface for handling file operations and selection changes
interface FileOperationCallback {
fun onFileDeleted(file: File)
@@ -40,23 +57,29 @@ class FileAdapter(
fun onSelectionCountChanged(selectedCount: Int)
}
var fileOperationCallback: FileOperationCallback? = null
fun setFileOperationCallback(callback: FileOperationCallback?) {
fileOperationCallback = callback?.let { WeakReference(it) }
}
inner class FileViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val imageView: ImageView = view.findViewById(R.id.fileIconImageView)
val fileNameTextView: TextView = view.findViewById(R.id.fileNameTextView)
val playIcon: ImageView = view.findViewById(R.id.videoPlay)
val selectionOverlay: View? = view.findViewById(R.id.selectedLayer) // Optional overlay for selection
val checkIcon: ImageView? = view.findViewById(R.id.selected) // Optional check icon
val selectedLayer: View = view.findViewById(R.id.selectedLayer)
val selected: ImageView = view.findViewById(R.id.selected)
fun bind(file: File) {
val fileType = FileManager(context, lifecycleOwner).getFileType(file)
setupFileDisplay(file, fileType)
setupClickListeners(file, fileType)
fileNameTextView.visibility = if (showFileName) View.VISIBLE else View.GONE
// Handle selection state
val isSelected = selectedItems.contains(adapterPosition)
updateSelectionUI(isSelected)
// Update selection state
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
val isSelected = selectedItems.contains(position)
updateSelectionUI(isSelected)
}
}
fun bind(file: File, payloads: List<Any>) {
@@ -76,101 +99,88 @@ class FileAdapter(
// Could update file info if displayed
}
"SELECTION_CHANGED" -> {
val isSelected = selectedItems.contains(adapterPosition)
updateSelectionUI(isSelected)
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
val isSelected = selectedItems.contains(position)
updateSelectionUI(isSelected)
// Notify activity about selection change
notifySelectionModeChange()
}
}
}
}
}
private fun updateSelectionUI(isSelected: Boolean) {
// Update visual selection state
itemView.isSelected = isSelected
// If you have a selection overlay, show/hide it
selectionOverlay?.visibility = if (isSelected) View.VISIBLE else View.GONE
// If you have a check icon, show/hide it
checkIcon?.visibility = if (isSelected) View.VISIBLE else View.GONE
// You can also change the background or add other visual indicators
itemView.alpha = if (isSelectionMode && !isSelected) 0.7f else 1.0f
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
selected.visibility = if (isSelected) View.VISIBLE else View.GONE
}
private fun setupFileDisplay(file: File, fileType: FileManager.FileType) {
fileNameTextView.text = file.name
when (fileType) {
FileManager.FileType.IMAGE -> {
loadImageThumbnail(file)
fileNameTextView.visibility = View.GONE
playIcon.visibility = View.GONE
Glide.with(context)
.load(file)
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.error(R.drawable.ic_document)
.placeholder(R.drawable.ic_document)
.into(imageView)
}
FileManager.FileType.VIDEO -> {
loadVideoThumbnail(file)
fileNameTextView.visibility = View.GONE
playIcon.visibility = View.VISIBLE
Glide.with(context)
.load(file)
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.error(R.drawable.ic_document)
.placeholder(R.drawable.ic_document)
.into(imageView)
}
FileManager.FileType.AUDIO -> {
playIcon.visibility = View.GONE
imageView.setImageResource(R.drawable.ic_audio)
}
else -> {
loadFileIcon(fileType)
fileNameTextView.visibility = View.VISIBLE
playIcon.visibility = View.GONE
imageView.setImageResource(R.drawable.ic_document)
}
}
fileNameTextView.text = file.name
}
private fun loadImageThumbnail(file: File) {
Glide.with(imageView)
.load(file)
.thumbnail(0.1f)
.centerCrop()
.override(300, 300)
.placeholder(R.drawable.ic_file)
.error(R.drawable.ic_file)
.into(imageView)
}
private fun loadVideoThumbnail(file: File) {
Glide.with(imageView)
.asBitmap()
.load(file)
.thumbnail(0.1f)
.centerCrop()
.override(300, 300)
.placeholder(R.drawable.ic_file)
.error(R.drawable.ic_file)
.into(imageView)
}
private fun loadFileIcon(fileType: FileManager.FileType) {
val resourceId = when (fileType) {
FileManager.FileType.AUDIO -> R.drawable.ic_audio
FileManager.FileType.DOCUMENT -> R.drawable.ic_document
else -> R.drawable.ic_file
}
imageView.setImageResource(resourceId)
}
private fun setupClickListeners(file: File, fileType: FileManager.FileType) {
itemView.setOnClickListener {
val position = adapterPosition
if (position == RecyclerView.NO_POSITION) return@setOnClickListener
if (isSelectionMode) {
toggleSelection(adapterPosition)
return@setOnClickListener
toggleSelection(position)
} else {
openFile(file, fileType)
}
openFile(file, fileType)
}
itemView.setOnLongClickListener {
val position = adapterPosition
if (position == RecyclerView.NO_POSITION) return@setOnLongClickListener false
if (!isSelectionMode) {
showFileOptionsDialog(file)
true
} else {
toggleSelection(adapterPosition)
true
enterSelectionMode()
toggleSelection(position)
}
true
}
}
private fun openFile(file: File, fileType: FileManager.FileType) {
if (!file.exists()) {
Toast.makeText(context, "File no longer exists", Toast.LENGTH_SHORT).show()
return
}
when (fileType) {
FileManager.FileType.AUDIO -> openAudioFile(file)
FileManager.FileType.IMAGE, FileManager.FileType.VIDEO -> openInPreview(fileType)
@@ -180,19 +190,20 @@ class FileAdapter(
}
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 {
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)
}
context.startActivity(intent)
} catch (e: Exception) {
Log.e(TAG, "Failed to open audio file: ${e.message}")
Toast.makeText(
context,
context.getString(R.string.no_audio_player_found),
@@ -202,19 +213,20 @@ class FileAdapter(
}
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 {
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)
}
context.startActivity(intent)
} catch (e: Exception) {
Log.e(TAG, "Failed to open document file: ${e.message}")
Toast.makeText(
context,
context.getString(R.string.no_suitable_app_found_to_open_this_document),
@@ -239,23 +251,41 @@ class FileAdapter(
}
private fun showFileOptionsDialog(file: File) {
val options = arrayOf(
context.getString(R.string.un_hide),
context.getString(R.string.select_multiple),
context.getString(R.string.rename),
context.getString(R.string.delete),
context.getString(R.string.share)
)
val options = if (isSelectionMode) {
arrayOf(
context.getString(R.string.un_hide),
context.getString(R.string.delete),
context.getString(R.string.copy_to_another_folder),
context.getString(R.string.move_to_another_folder)
)
} else {
arrayOf(
context.getString(R.string.un_hide),
context.getString(R.string.select_multiple),
context.getString(R.string.rename),
context.getString(R.string.delete),
context.getString(R.string.share)
)
}
MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.file_options))
.setItems(options) { dialog, which ->
when (which) {
0 -> unHideFile(file)
1 -> enableSelectMultipleFiles()
2 -> renameFile(file)
3 -> deleteFile(file)
4 -> shareFile(file)
if (isSelectionMode) {
when (which) {
0 -> unHideFile(file)
1 -> deleteFile(file)
2 -> copyToAnotherFolder(file)
3 -> moveToAnotherFolder(file)
}
} else {
when (which) {
0 -> unHideFile(file)
1 -> enableSelectMultipleFiles()
2 -> renameFile(file)
3 -> deleteFile(file)
4 -> shareFile(file)
}
}
dialog.dismiss()
}
@@ -264,18 +294,19 @@ class FileAdapter(
}
private fun enableSelectMultipleFiles() {
// Enable multiple selection mode and select current item
val position = adapterPosition
if (position == RecyclerView.NO_POSITION) return
enterSelectionMode()
selectedItems.add(adapterPosition)
notifyItemChanged(adapterPosition, listOf("SELECTION_CHANGED"))
fileOperationCallback?.onSelectionCountChanged(selectedItems.size)
selectedItems.add(position)
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
}
private fun unHideFile(file: File) {
FileManager(context, lifecycleOwner).unHideFile(
file = file,
onSuccess = {
fileOperationCallback?.onFileDeleted(file)
fileOperationCallback?.get()?.onFileDeleted(file)
},
onError = { errorMessage ->
Toast.makeText(context, "Failed to unhide: $errorMessage", Toast.LENGTH_SHORT).show()
@@ -284,11 +315,34 @@ class FileAdapter(
}
private fun deleteFile(file: File) {
if (file.delete()) {
fileOperationCallback?.onFileDeleted(file)
Toast.makeText(context, "File deleted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
// Show confirmation dialog first
MaterialAlertDialogBuilder(context)
.setTitle("Delete File")
.setMessage("Are you sure you want to delete ${file.name}?")
.setPositiveButton("Delete") { _, _ ->
deleteFileAsync(file)
}
.setNegativeButton("Cancel", null)
.show()
}
private fun deleteFileAsync(file: File) {
fileExecutor.execute {
val success = try {
file.delete()
} catch (e: Exception) {
Log.e(TAG, "Failed to delete file: ${e.message}")
false
}
mainHandler.post {
if (success) {
fileOperationCallback?.get()?.onFileDeleted(file)
Toast.makeText(context, "File deleted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
}
}
}
}
@@ -304,15 +358,10 @@ class FileAdapter(
.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()
}
if (isValidFileName(newName)) {
renameFileAsync(file, newName)
} else {
Toast.makeText(context, "Invalid file name", Toast.LENGTH_SHORT).show()
}
}
dialog.dismiss()
@@ -324,37 +373,87 @@ class FileAdapter(
.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)
private fun isValidFileName(fileName: String): Boolean {
val forbiddenChars = charArrayOf('/', '\\', ':', '*', '?', '"', '<', '>', '|')
return fileName.isNotBlank() &&
fileName.none { it in forbiddenChars } &&
!fileName.startsWith(".") &&
fileName.length <= 255
}
private fun renameFileAsync(file: File, newName: String) {
fileExecutor.execute {
val parentDir = file.parentFile
if (parentDir != null) {
val newFile = File(parentDir, newName)
if (newFile.exists()) {
mainHandler.post {
Toast.makeText(context, "File with this name already exists", Toast.LENGTH_SHORT).show()
}
return@execute
}
val success = try {
file.renameTo(newFile)
} catch (e: Exception) {
Log.e(TAG, "Failed to rename file: ${e.message}")
false
}
mainHandler.post {
if (success) {
fileOperationCallback?.get()?.onFileRenamed(file, newFile)
Toast.makeText(context, "File renamed", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Failed to rename file", Toast.LENGTH_SHORT).show()
}
}
}
}
context.startActivity(
Intent.createChooser(shareIntent, context.getString(R.string.share_file))
)
}
private fun shareFile(file: File) {
try {
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))
)
} catch (e: Exception) {
Log.e(TAG, "Failed to share file: ${e.message}")
Toast.makeText(context, "Failed to share file", Toast.LENGTH_SHORT).show()
}
}
private fun copyToAnotherFolder(file: File) {
// This will be handled by the activity
fileOperationCallback?.get()?.onRefreshNeeded()
}
private fun moveToAnotherFolder(file: File) {
// This will be handled by the activity
fileOperationCallback?.get()?.onRefreshNeeded()
}
private fun toggleSelection(position: Int) {
if (selectedItems.contains(position)) {
selectedItems.remove(position)
if (selectedItems.isEmpty()) {
exitSelectionMode()
}
} else {
selectedItems.add(position)
}
// Exit selection mode if no items are selected
if (selectedItems.isEmpty()) {
exitSelectionMode()
} else {
fileOperationCallback?.onSelectionCountChanged(selectedItems.size)
}
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
onSelectionCountChanged(selectedItems.size)
notifyItemChanged(position)
}
}
@@ -365,29 +464,30 @@ class FileAdapter(
}
override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
val file = getItem(position)
holder.bind(file)
if (position < itemCount) {
val file = getItem(position)
holder.bind(file)
}
}
override fun onBindViewHolder(holder: FileViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val file = getItem(position)
holder.bind(file, payloads)
if (position < itemCount) {
val file = getItem(position)
holder.bind(file, payloads)
}
}
}
// Public methods for external control
/**
* Enter selection mode
* Enter selection mode and notify callback immediately
*/
fun enterSelectionMode() {
if (!isSelectionMode) {
isSelectionMode = true
fileOperationCallback?.onSelectionModeChanged(true, selectedItems.size)
notifyDataSetChanged() // Refresh all items to show selection UI
notifySelectionModeChange()
}
}
@@ -398,8 +498,8 @@ class FileAdapter(
if (isSelectionMode) {
isSelectionMode = false
selectedItems.clear()
fileOperationCallback?.onSelectionModeChanged(false, 0)
notifyDataSetChanged() // Refresh all items to hide selection UI
notifySelectionModeChange()
notifyDataSetChanged()
}
}
@@ -410,11 +510,11 @@ class FileAdapter(
if (selectedItems.isNotEmpty()) {
val previouslySelected = selectedItems.toSet()
selectedItems.clear()
fileOperationCallback?.onSelectionCountChanged(0)
// Only update previously selected items
fileOperationCallback?.get()?.onSelectionCountChanged(0)
previouslySelected.forEach { position ->
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
if (position < itemCount) {
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
}
}
}
}
@@ -435,16 +535,33 @@ class FileAdapter(
selectedItems.add(i)
}
fileOperationCallback?.onSelectionCountChanged(selectedItems.size)
// Notify callback about selection change
fileOperationCallback?.get()?.onSelectionCountChanged(selectedItems.size)
// Update UI for changed items
val allPositions = (0 until itemCount).toSet()
val changedPositions = allPositions - previouslySelected + previouslySelected - allPositions
changedPositions.forEach { position ->
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
// Update UI for changed items efficiently
updateSelectionItems(selectedItems.toSet(), previouslySelected)
}
/**
* Efficiently update selection UI for changed items only
*/
private fun updateSelectionItems(newSelections: Set<Int>, oldSelections: Set<Int>) {
val changedItems = (oldSelections - newSelections) + (newSelections - oldSelections)
changedItems.forEach { position ->
if (position < itemCount) {
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
}
}
}
/**
* Centralized method to notify selection mode changes
*/
private fun notifySelectionModeChange() {
fileOperationCallback?.get()?.onSelectionModeChanged(isSelectionMode, selectedItems.size)
onFolderLongClick(isSelectionMode)
}
/**
* Get selected files
*/
@@ -465,34 +582,67 @@ class FileAdapter(
fun isInSelectionMode(): Boolean = isSelectionMode
/**
* Delete selected files
* Delete selected files with proper error handling and background processing
*/
fun deleteSelectedFiles() {
val selectedFiles = getSelectedItems()
var deletedCount = 0
var failedCount = 0
if (selectedFiles.isEmpty()) return
selectedFiles.forEach { file ->
if (file.delete()) {
deletedCount++
fileOperationCallback?.onFileDeleted(file)
} else {
failedCount++
// Show confirmation dialog
MaterialAlertDialogBuilder(context)
.setTitle("Delete Files")
.setMessage("Are you sure you want to delete ${selectedFiles.size} file(s)?")
.setPositiveButton("Delete") { _, _ ->
deleteFilesAsync(selectedFiles)
}
}
.setNegativeButton("Cancel", null)
.show()
}
exitSelectionMode()
private fun deleteFilesAsync(selectedFiles: List<File>) {
fileExecutor.execute {
var deletedCount = 0
var failedCount = 0
val failedFiles = mutableListOf<String>()
// Show result message
when {
deletedCount > 0 && failedCount == 0 -> {
Toast.makeText(context, "Deleted $deletedCount file(s)", Toast.LENGTH_SHORT).show()
selectedFiles.forEach { file ->
try {
if (file.delete()) {
deletedCount++
mainHandler.post {
fileOperationCallback?.get()?.onFileDeleted(file)
}
} else {
failedCount++
failedFiles.add(file.name)
}
} catch (e: Exception) {
failedCount++
failedFiles.add(file.name)
Log.e(TAG, "Failed to delete ${file.name}: ${e.message}")
}
}
deletedCount > 0 && failedCount > 0 -> {
Toast.makeText(context, "Deleted $deletedCount file(s), failed to delete $failedCount", Toast.LENGTH_LONG).show()
}
failedCount > 0 -> {
Toast.makeText(context, "Failed to delete $failedCount file(s)", Toast.LENGTH_SHORT).show()
mainHandler.post {
// Exit selection mode first
exitSelectionMode()
// Show detailed result message
when {
deletedCount > 0 && failedCount == 0 -> {
Toast.makeText(context, "Deleted $deletedCount file(s)", Toast.LENGTH_SHORT).show()
}
deletedCount > 0 && failedCount > 0 -> {
Toast.makeText(context,
"Deleted $deletedCount file(s), failed to delete $failedCount",
Toast.LENGTH_LONG).show()
}
failedCount > 0 -> {
Toast.makeText(context,
"Failed to delete $failedCount file(s)",
Toast.LENGTH_SHORT).show()
}
}
}
}
}
@@ -504,39 +654,54 @@ class FileAdapter(
val selectedFiles = getSelectedItems()
if (selectedFiles.isEmpty()) return
if (selectedFiles.size == 1) {
// Share single file
val file = selectedFiles.first()
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))
)
} else {
// Share multiple files
val uris = selectedFiles.map { file ->
FileProvider.getUriForFile(
try {
if (selectedFiles.size == 1) {
// Share single file
val file = selectedFiles.first()
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))
)
} else {
// Share multiple files
val uris = selectedFiles.mapNotNull { file ->
try {
FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
} catch (e: Exception) {
Log.e(TAG, "Failed to get URI for file ${file.name}: ${e.message}")
null
}
}
if (uris.isNotEmpty()) {
val shareIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
type = "*/*"
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(shareIntent, "Share ${selectedFiles.size} files")
)
} else {
Toast.makeText(context, "No files could be shared", Toast.LENGTH_SHORT).show()
}
}
val shareIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
type = "*/*"
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
Intent.createChooser(shareIntent, "Share ${selectedFiles.size} files")
)
} catch (e: Exception) {
Log.e(TAG, "Failed to share files: ${e.message}")
Toast.makeText(context, "Failed to share files", Toast.LENGTH_SHORT).show()
}
exitSelectionMode()
@@ -554,4 +719,48 @@ class FileAdapter(
false
}
}
/**
* Force refresh of all selection states
* Call this if you notice selection UI issues
*/
fun refreshSelectionStates() {
if (isSelectionMode) {
selectedItems.forEach { position ->
if (position < itemCount) {
notifyItemChanged(position, listOf("SELECTION_CHANGED"))
}
}
// Ensure callback is notified
notifySelectionModeChange()
}
}
/**
* Clean up resources to prevent memory leaks
*/
fun cleanup() {
try {
if (!fileExecutor.isShutdown) {
fileExecutor.shutdown()
}
} catch (e: Exception) {
Log.e(TAG, "Error shutting down executor: ${e.message}")
}
fileOperationCallback?.clear()
fileOperationCallback = null
}
/**
* Call this from the activity's onDestroy()
*/
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
cleanup()
}
private fun onSelectionCountChanged(count: Int) {
fileOperationCallback?.get()?.onSelectionCountChanged(count)
}
}

View File

@@ -14,7 +14,8 @@ import java.io.File
class FolderAdapter(
private val onFolderClick: (File) -> Unit,
private val onFolderLongClick: (File) -> Unit,
private val onSelectionModeChanged: (Boolean) -> Unit
private val onSelectionModeChanged: (Boolean) -> Unit,
private val onSelectionCountChanged: (Int) -> Unit
) : ListAdapter<File, FolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
private val selectedItems = mutableSetOf<Int>()
@@ -41,8 +42,7 @@ class FolderAdapter(
itemView.setOnLongClickListener {
if (!isSelectionMode) {
isSelectionMode = true
onSelectionModeChanged(true)
enterSelectionMode()
onFolderLongClick(folder)
toggleSelection(adapterPosition)
}
@@ -54,12 +54,12 @@ class FolderAdapter(
if (selectedItems.contains(position)) {
selectedItems.remove(position)
if (selectedItems.isEmpty()) {
isSelectionMode = false
onSelectionModeChanged(false)
exitSelectionMode()
}
} else {
selectedItems.add(position)
}
onSelectionCountChanged(selectedItems.size)
notifyItemChanged(position)
}
}
@@ -81,10 +81,36 @@ class FolderAdapter(
}
fun clearSelection() {
val wasInSelectionMode = isSelectionMode
selectedItems.clear()
if (wasInSelectionMode) {
exitSelectionMode()
}
onSelectionCountChanged(0)
notifyDataSetChanged()
}
fun onBackPressed(): Boolean {
return if (isInSelectionMode()) {
clearSelection()
true
} else {
false
}
}
fun isInSelectionMode(): Boolean {
return isSelectionMode
}
private fun enterSelectionMode() {
isSelectionMode = true
onSelectionModeChanged(true)
}
private fun exitSelectionMode() {
isSelectionMode = false
onSelectionModeChanged(false)
notifyDataSetChanged()
}
private class FolderDiffCallback : DiffUtil.ItemCallback<File>() {

View File

@@ -0,0 +1,124 @@
package devs.org.calculator.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
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 ListFolderAdapter(
private val onFolderClick: (File) -> Unit,
private val onFolderLongClick: (File) -> Unit,
private val onSelectionModeChanged: (Boolean) -> Unit,
private val onSelectionCountChanged: (Int) -> Unit
) : ListAdapter<File, ListFolderAdapter.FolderViewHolder>(FolderDiffCallback()) {
private val selectedItems = mutableSetOf<Int>()
private var isSelectionMode = false
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
val selectedLayer: View = itemView.findViewById(R.id.selectedLayer)
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) {
folderNameTextView.text = folder.name
selectedLayer.visibility = if (isSelected) View.VISIBLE else View.GONE
itemView.setOnClickListener {
if (isSelectionMode) {
toggleSelection(adapterPosition)
} else {
onFolderClick(folder)
}
}
itemView.setOnLongClickListener {
if (!isSelectionMode) {
enterSelectionMode()
onFolderLongClick(folder)
toggleSelection(adapterPosition)
}
true
}
}
private fun toggleSelection(position: Int) {
if (selectedItems.contains(position)) {
selectedItems.remove(position)
if (selectedItems.isEmpty()) {
exitSelectionMode()
}
} else {
selectedItems.add(position)
}
onSelectionCountChanged(selectedItems.size)
notifyItemChanged(position)
}
}
private fun enterSelectionMode() {
isSelectionMode = true
onSelectionModeChanged(true)
}
private fun exitSelectionMode() {
isSelectionMode = false
onSelectionModeChanged(false)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_folder_list_style, parent, false)
return FolderViewHolder(view)
}
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
val folder = getItem(position)
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() {
val wasInSelectionMode = isSelectionMode
selectedItems.clear()
if (wasInSelectionMode) {
exitSelectionMode()
}
onSelectionCountChanged(0)
notifyDataSetChanged()
}
fun onBackPressed(): Boolean {
return if (isInSelectionMode()) {
clearSelection()
true
} else {
false
}
}
fun isInSelectionMode(): Boolean {
return isSelectionMode
}
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
}
}
}

View File

@@ -18,6 +18,15 @@ class PrefsUtil(context: Context) {
.apply()
}
fun setBoolean(key:String, value: Boolean){
return prefs.edit().putBoolean(key,value).apply()
}
fun getBoolean(key: String, defValue: Boolean = false): Boolean{
return prefs.getBoolean(key,defValue)
}
fun resetPassword(){
prefs.edit()
.remove("password")

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:bottomLeftRadius="15dp" android:bottomRightRadius="15dp"/>
<solid android:color="?attr/cardForegroundColor"/>
</shape>

View File

@@ -5,8 +5,8 @@
android:viewportHeight="1024">
<path
android:pathData="M224,480h640a32,32 0,1 1,0 64H224a32,32 0,0 1,0 -64z"
android:fillColor="@color/textColor"/>
android:fillColor="@color/svgTintColor"/>
<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"/>
android:fillColor="@color/svgTintColor"/>
</vector>

View File

@@ -5,6 +5,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:fillColor="?attr/colorPrimary"
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>

View 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:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
android:fillColor="?attr/colorPrimary"/>
</vector>

View File

@@ -0,0 +1,37 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="800dp"
android:height="800dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M2.909,3.5L11.283,3.5C12.441,3.5 13.551,3.96 14.369,4.778L14.722,5.131C15.54,5.949 16.65,6.409 17.807,6.409L23.273,6.409C24.076,6.409 24.762,6.693 25.33,7.261C25.898,7.829 26.182,8.515 26.182,9.318L26.182,25.318C26.182,26.122 25.898,26.807 25.33,27.375C24.762,27.943 24.076,28.227 23.273,28.227L2.909,28.227C2.106,28.227 1.42,27.943 0.852,27.375C0.284,26.807 0,26.122 0,25.318L0,6.409C0,5.606 0.284,4.92 0.852,4.352C1.42,3.784 2.106,3.5 2.909,3.5Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
android:startX="16"
android:startY="3.5"
android:endX="16"
android:endY="19.854"
android:type="linear">
<item android:offset="0" android:color="#FFFBA200"/>
<item android:offset="1" android:color="#FFFF7300"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M5.818,25.318L5.818,12.227C5.818,11.424 6.102,10.738 6.67,10.17C7.238,9.602 7.924,9.318 8.727,9.318L29.091,9.318C29.894,9.318 30.58,9.602 31.148,10.17C31.716,10.738 32,11.424 32,12.227L32,25.318C32,26.122 31.716,26.807 31.148,27.375C30.58,27.943 29.894,28.227 29.091,28.227L2.909,28.227C3.712,28.227 4.398,27.943 4.966,27.375C5.534,26.807 5.818,26.122 5.818,25.318Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
android:startX="17.454"
android:startY="9.318"
android:endX="17.454"
android:endY="28.227"
android:type="linear">
<item android:offset="0" android:color="#FFFAC227"/>
<item android:offset="1" android:color="#FFFAA627"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -0,0 +1,10 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="20"
android:viewportWidth="20"
android:width="24dp">
<path android:fillColor="@color/black" android:fillType="evenOdd" android:pathData="M10,0C15.523,0 20,4.59 20,10.253C20,14.782 17.138,18.624 13.167,19.981C12.66,20.082 12.48,19.762 12.48,19.489C12.48,19.151 12.492,18.047 12.492,16.675C12.492,15.719 12.172,15.095 11.813,14.777C14.04,14.523 16.38,13.656 16.38,9.718C16.38,8.598 15.992,7.684 15.35,6.966C15.454,6.707 15.797,5.664 15.252,4.252C15.252,4.252 14.414,3.977 12.505,5.303C11.706,5.076 10.85,4.962 10,4.958C9.15,4.962 8.295,5.076 7.497,5.303C5.586,3.977 4.746,4.252 4.746,4.252C4.203,5.664 4.546,6.707 4.649,6.966C4.01,7.684 3.619,8.598 3.619,9.718C3.619,13.646 5.954,14.526 8.175,14.785C7.889,15.041 7.63,15.493 7.54,16.156C6.97,16.418 5.522,16.871 4.63,15.304C4.63,15.304 4.101,14.319 3.097,14.247C3.097,14.247 2.122,14.234 3.029,14.87C3.029,14.87 3.684,15.185 4.139,16.37C4.139,16.37 4.726,18.2 7.508,17.58C7.513,18.437 7.522,19.245 7.522,19.489C7.522,19.76 7.338,20.077 6.839,19.982C2.865,18.627 0,14.783 0,10.253C0,4.59 4.478,0 10,0" android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="169dp"
android:height="169dp"
android:viewportWidth="35.28"
android:viewportHeight="35.28">
<path
android:pathData="M27.64,3.64L23.64,3.64C21.431,3.64 19.64,5.431 19.64,7.64L19.64,11.64C19.64,13.849 21.431,15.64 23.64,15.64L27.64,15.64C29.849,15.64 31.64,13.849 31.64,11.64L31.64,7.64C31.64,5.431 29.849,3.64 27.64,3.64L27.64,3.64ZM27.64,19.64L23.64,19.64C21.431,19.64 19.64,21.431 19.64,23.64L19.64,27.64C19.64,29.849 21.431,31.64 23.64,31.64L27.64,31.64C29.849,31.64 31.64,29.849 31.64,27.64L31.64,23.64C31.64,21.431 29.849,19.64 27.64,19.64L27.64,19.64ZM11.64,19.64L7.64,19.64C5.431,19.64 3.64,21.431 3.64,23.64L3.64,27.64C3.64,29.849 5.431,31.64 7.64,31.64L11.64,31.64C13.849,31.64 15.64,29.849 15.64,27.64L15.64,23.64C15.64,21.431 13.849,19.64 11.64,19.64L11.64,19.64ZM11.64,3.64L7.64,3.64C5.431,3.64 3.64,5.431 3.64,7.64L3.64,11.64C3.64,13.849 5.431,15.64 7.64,15.64L11.64,15.64C13.849,15.64 15.64,13.849 15.64,11.64L15.64,7.64C15.64,5.431 13.849,3.64 11.64,3.64L11.64,3.64Z"
android:strokeWidth="1"
android:fillColor="?attr/colorPrimary"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M4,13L2,13L2,11L4,11ZM14,11L6,11v2h8ZM4,7L2,7L2,9L4,9ZM14,7L6,7L6,9h8ZM4,3L2,3L2,5L4,5ZM14,3L6,3L6,5h8Z"
android:fillColor="?attr/colorPrimary"/>
</vector>

View File

@@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp">
<path android:fillColor="?attr/colorPrimary"
android:pathData="M12,12H12.01M12,6H12.01M12,18H12.01M13,12C13,12.552 12.552,13 12,13C11.448,13 11,12.552 11,12C11,11.448 11.448,11 12,11C12.552,11 13,11.448 13,12ZM13,18C13,18.552 12.552,19 12,19C11.448,19 11,18.552 11,18C11,17.448 11.448,17 12,17C12.552,17 13,17.448 13,18ZM13,6C13,6.552 12.552,7 12,7C11.448,7 11,6.552 11,6C11,5.448 11.448,5 12,5C12.552,5 13,5.448 13,6Z" android:strokeColor="?attr/colorPrimary" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</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="48"
android:viewportHeight="48">
<path
android:fillColor="?attr/colorPrimary"
android:pathData="M34.6,23.3 L18.1,13.2c-0.6,-0.4 -1.1,-0.1 -1.1,0.6V34.2c0,0.7 0.5,1 1.1,0.6L34.6,24.7A0.8,0.8 0,0 0,34.6 23.3Z"/>
<path
android:fillColor="?attr/colorPrimary"
android:pathData="M24,2A22,22 0,1 0,46 24,21.9 21.9,0 0,0 24,2ZM24,42A18,18 0,1 1,42 24,18.1 18.1,0 0,1 24,42Z"/>
</vector>

View File

@@ -1,11 +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="1" android:fillColor="?attr/colorPrimary" 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="1" android:fillColor="?attr/colorPrimary" 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:fillAlpha="1" android:fillColor="?attr/colorPrimary" 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"/>
<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="?attr/colorPrimary" android:strokeLineCap="round" android:strokeWidth="1.5"/>
</vector>

View 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:pathData="M4,13.743l-1,0.579a1,1 0,0 0,-0.366 1.366l1.488,2.578a1,1 0,0 0,1.366 0.366L6.5,18.05a1.987,1.987 0,0 1,1.986 0l0.02,0.011a1.989,1.989 0,0 1,1 1.724V21a1,1 0,0 0,1 1h3a1,1 0,0 0,1 -1V19.782a1.985,1.985 0,0 1,0.995 -1.721l0.021,-0.012a1.987,1.987 0,0 1,1.986 0l1.008,0.582a1,1 0,0 0,1.366 -0.366l1.488,-2.578A1,1 0,0 0,21 14.322l-1,-0.579a1.994,1.994 0,0 1,-1 -1.733v-0.021a1.991,1.991 0,0 1,1 -1.732l1,-0.579a1,1 0,0 0,0.366 -1.366L19.876,5.734a1,1 0,0 0,-1.366 -0.366L17.5,5.95a1.987,1.987 0,0 1,-1.986 0L15.5,5.94a1.989,1.989 0,0 1,-1 -1.724V3a1,1 0,0 0,-1 -1h-3a1,1 0,0 0,-1 1V4.294A1.856,1.856 0,0 1,8.57 5.9l-0.153,0.088a1.855,1.855 0,0 1,-1.853 0L5.49,5.368a1,1 0,0 0,-1.366 0.366L2.636,8.312A1,1 0,0 0,3 9.678l1,0.579A1.994,1.994 0,0 1,5 11.99v0.021A1.991,1.991 0,0 1,4 13.743ZM12,9a3,3 0,1 1,-3 3A3,3 0,0 1,12 9Z"
android:fillColor="?attr/colorPrimary"/>
</vector>

View File

@@ -1,8 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="512" android:viewportWidth="512" android:width="24dp">
<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"/>
<path android:fillColor="?attr/colorPrimary" android:pathData="M464.7,221.5L86.1,7.3C52.5,-11.7 25,7.5 25,50v412c0,42.5 27.5,61.7 61.1,42.7l378.6,-214.1C498.2,271.5 498.2,240.5 464.7,221.5z"/>
</vector>

View File

@@ -8,5 +8,5 @@
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"/>
android:fillColor="@color/black"/>
</vector>

View File

@@ -12,7 +12,7 @@
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change Password"
android:text="@string/change_password"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="Enter Old Password"
android:hint="@string/enter_old_password"
app:endIconMode="password_toggle"
app:layout_constraintTop_toBottomOf="@id/tvTitle">
@@ -42,7 +42,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="Enter New Password"
android:hint="@string/enter_new_password"
app:endIconMode="password_toggle"
app:layout_constraintTop_toBottomOf="@id/tilOldPassword">
@@ -61,7 +61,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:padding="12dp"
android:text="Change Password"
android:text="@string/change_password"
app:layout_constraintTop_toBottomOf="@id/tilNewPassword" />
<com.google.android.material.button.MaterialButton
@@ -70,7 +70,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Forgot Password?"
android:text="@string/forgot_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnChangePassword" />

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,145 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:layout_marginTop="50dp"
android:padding="8dp" />
<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>
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/add_files"
app:srcCompat="@drawable/ic_add" />
<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>

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="bottom|end"
android:layout_marginEnd="25dp"
android:layout_marginBottom="35dp"
android:src="@android:drawable/ic_input_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -41,10 +41,48 @@
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_setting"
android:src="@drawable/ic_edit"
android:scaleType="fitCenter"
android:padding="9dp"
android:visibility="gone"
android:background="#00000000"
android:id="@+id/edit"/>
<ImageButton
android:id="@+id/delete"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#00000000"
android:padding="9dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
android:visibility="gone" />
<ImageButton
android:id="@+id/menuButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_more"
android:scaleType="fitCenter"
android:padding="9dp"
android:visibility="gone"
android:background="#00000000"/>
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_list"
android:scaleType="fitCenter"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/folderOrientation"/>
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_settings"
android:scaleType="fitCenter"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/settings"/>
</LinearLayout>
@@ -88,90 +126,28 @@
</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/addDocument"
app:layout_constraintStart_toStartOf="@+id/addDocument" />
<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/fabExpend"
app:layout_constraintStart_toStartOf="@+id/fabExpend" />
<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"
android:backgroundTint="?attr/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
app:tint="@color/white"
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" />
<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:tint="@color/white"
android:backgroundTint="?attr/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"

View File

@@ -1,121 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:minHeight="300dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal|bottom"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnImages"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toTopOf="@id/btnAudio"
app:layout_constraintEnd_toStartOf="@id/btnVideos"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Images"
android:textSize="18sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnVideos"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toTopOf="@id/btnDocs"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btnImages"
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Videos"
android:textSize="18sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal|top"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnAudio"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnDocs"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnImages">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Audio"
android:textSize="18sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/btnDocs"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btnAudio"
app:layout_constraintTop_toBottomOf="@id/btnVideos">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Documents"
android:textSize="18sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</LinearLayout>

View File

@@ -11,7 +11,8 @@
android:id="@+id/displayContainer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:layout_margin="0dp"
android:background="@drawable/bottom_corner"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.3"
app:layout_constraintStart_toStartOf="parent"
@@ -40,14 +41,14 @@
android:id="@+id/display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoSizeMaxTextSize="48sp"
android:autoSizeMaxTextSize="70sp"
android:autoSizeMinTextSize="16sp"
android:autoSizeStepGranularity="2sp"
android:gravity="end|bottom"
android:autoSizeTextType="uniform"
android:padding="10dp"
android:text="0"
android:textSize="48sp"
android:text=""
android:textSize="70sp"
tools:ignore="Suspicious0dp" />
</LinearLayout>
@@ -57,7 +58,7 @@
android:id="@+id/total"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoSizeMaxTextSize="26sp"
android:autoSizeMaxTextSize="40sp"
android:autoSizeMinTextSize="24sp"
android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform"
@@ -65,7 +66,7 @@
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:text=""
android:textSize="26sp"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -183,6 +184,7 @@
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:textColor="@color/white"
android:text="×"
android:textSize="30sp"
app:cornerRadius="15dp" />
@@ -236,6 +238,7 @@
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:textColor="@color/white"
android:text="-"
android:textSize="30sp"
app:cornerRadius="15dp" />
@@ -289,6 +292,7 @@
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:textColor="@color/white"
android:text="+"
android:textSize="30sp"
app:cornerRadius="15dp" />
@@ -331,6 +335,7 @@
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1"
android:textColor="@color/white"
android:text="="
android:textSize="30sp"
app:cornerRadius="15dp" />

View File

@@ -29,7 +29,7 @@
android:textSize="22sp"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="Preview File"/>
android:text="@string/preview_file"/>
</LinearLayout>
<androidx.viewpager2.widget.ViewPager2
@@ -60,7 +60,7 @@
android:minWidth="120dp"
android:layout_weight="1"
style="@style/Widget.Material3.Button.OutlinedButton"
android:text="Unhide" />
android:text="@string/un_hide" />
<com.google.android.material.button.MaterialButton
android:id="@+id/delete"
@@ -69,7 +69,7 @@
android:layout_weight="1"
android:layout_margin="6dp"
android:minWidth="120dp"
android:text="Delete" />
android:text="@string/delete" />
</LinearLayout>

View File

@@ -1,10 +1,291 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout 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.SettingsActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp"
android:id="@+id/toolBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_back"
android:scaleType="fitCenter"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/back"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/settings"
android:textSize="22sp"
android:singleLine="true"
android:padding="4dp"
android:textStyle="bold"
android:layout_weight="1"
android:id="@+id/folderName"/>
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- App Details Section -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_details"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="10dp"
app:cardElevation="5dp">
<ImageView
android:id="@+id/appLogo"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@mipmap/ic_launcher" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/full_app_name"
android:textAppearance="?attr/textAppearanceBodyLarge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/version"
android:textAppearance="?attr/textAppearanceBodyMedium" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/githubButton"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_github" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Developer Details Section -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/developer_details"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/binondi_borthakur"
android:textAppearance="?attr/textAppearanceBodyLarge" />
<com.google.android.material.button.MaterialButton
android:id="@+id/devGithubButton"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_github" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Theme Settings Section -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/theme_settings"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textStyle="bold" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/dynamicThemeSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/dynamic_theme"
android:textAppearance="?attr/textAppearanceBodyLarge" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/themeModeSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/theme_mode"
android:textAppearance="?attr/textAppearanceBodyLarge" />
<RadioGroup
android:id="@+id/themeRadioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/lightThemeRadio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/light" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/darkThemeRadio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dark" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/systemThemeRadio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/system_default" />
</RadioGroup>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Security Settings Section -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/security_settings"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textStyle="bold" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/screenshotRestrictionSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/restrict_screenshots_in_hidden_section"
android:textAppearance="?attr/textAppearanceBodyLarge" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_settings"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textStyle="bold" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/showFileNames"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/show_file_names"
android:textAppearance="?attr/textAppearanceBodyLarge" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -12,7 +12,7 @@
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Setup Password"
android:text="@string/setup_password"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="Enter Password"
android:hint="@string/enter_password"
app:endIconMode="password_toggle"
app:layout_constraintTop_toBottomOf="@id/tvTitle">
@@ -42,7 +42,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="Confirm Password"
android:hint="@string/confirm_password"
app:endIconMode="password_toggle"
app:layout_constraintTop_toBottomOf="@id/tilPassword">
@@ -60,7 +60,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="Security Question (For Password Reset)"
android:hint="@string/security_question_for_password_reset"
app:layout_constraintTop_toBottomOf="@id/tilConfirmPassword">
<com.google.android.material.textfield.TextInputEditText
@@ -75,7 +75,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="Security Answer"
android:hint="@string/security_answer"
app:layout_constraintTop_toBottomOf="@id/tilSecurityQuestion">
<com.google.android.material.textfield.TextInputEditText
@@ -91,7 +91,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:padding="12dp"
android:text="Save Password"
android:text="@string/save_password"
app:layout_constraintTop_toBottomOf="@id/tilSecurityAnswer" />
<com.google.android.material.button.MaterialButton
@@ -100,7 +100,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Forgot Password?"
android:text="@string/forgot_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnSavePassword" />

View File

@@ -0,0 +1,169 @@
<?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.ViewFolderActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp"
android:id="@+id/toolBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_back"
android:scaleType="fitCenter"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/back"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/hidden_space"
android:textSize="22sp"
android:singleLine="true"
android:padding="4dp"
android:textStyle="bold"
android:layout_weight="1"
android:id="@+id/folderName"/>
<ImageButton
android:id="@+id/menuButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_more"
android:scaleType="fitCenter"
android:padding="7dp"
android:visibility="gone"
android:background="#00000000"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolBar">
</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"
app:tint="@color/white"
android:visibility="gone"
app:fabCustomSize="48dp"
android:backgroundTint="?attr/colorPrimary"
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"
app:fabCustomSize="49dp"
android:visibility="gone"
app:tint="@color/white"
android:backgroundTint="?attr/colorPrimary"
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:tint="@color/white"
android:backgroundTint="?attr/colorPrimary"
app:layout_constraintBottom_toTopOf="@+id/addDocument"
app:layout_constraintEnd_toEndOf="@+id/addDocument"
app:layout_constraintStart_toStartOf="@+id/addDocument" />
<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"
app:tint="@color/white"
android:visibility="gone"
android:backgroundTint="?attr/colorPrimary"
app:layout_constraintBottom_toTopOf="@+id/fabExpend"
app:layout_constraintEnd_toEndOf="@+id/fabExpend"
app:layout_constraintStart_toStartOf="@+id/fabExpend" />
<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:backgroundTint="?attr/colorPrimary"
android:text="@string/add_image"
app:fabCustomSize="60dp"
app:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,12 +5,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -55,19 +54,22 @@
android:layout_marginTop="4dp"
android:ellipsize="end"
android:gravity="center"
android:singleLine="true"
android:textColor="?attr/colorPrimary"
android:maxLines="1"
android:textSize="14sp" />
</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.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<ImageView
android:id="@+id/selected"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/gradient_bg"
android:padding="3dp"
android:src="@drawable/selected"
android:layout_gravity="end"
android:visibility="visible"
app:tint="#fff" />
</FrameLayout>

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
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="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/selectedLayer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha="0.3"
android:background="?attr/colorControlNormal"
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" />
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center_vertical"
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/folderIcon"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:layout_gravity="center"
android:src="@drawable/ic_folder" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/folderName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:textColor="?attr/colorPrimary"
android:maxLines="1"
android:singleLine="true"
android:textSize="18sp" />
<TextView
android:id="@+id/timeModified"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:text=""
android:visibility="gone"
android:textColor="#767676"
android:maxLines="1"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/selected"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/gradient_bg"
android:padding="3dp"
android:src="@drawable/selected"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="#fff" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>

View File

@@ -13,18 +13,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<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" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
@@ -38,36 +27,49 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/fileIconImageView"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@drawable/add_image" />
android:layout_height="match_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" />
<LinearLayout
android:id="@+id/selectedLayer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.3"
android:background="?attr/colorControlNormal"
android:orientation="horizontal"
android:visibility="gone" />
</FrameLayout>
</androidx.cardview.widget.CardView>
<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>
<ImageView
android:id="@+id/videoPlay"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription=""
android:layout_width="35dp"
android:layout_height="35dp"
android:visibility="gone"
android:scaleType="fitCenter"
android:src="@drawable/play"
android:src="@drawable/ic_play_circle"
android:layout_gravity="center"/>
<ImageView
android:id="@+id/selected"
android:layout_width="20dp"
android:layout_height="20dp"
android:padding="3dp"
app:tint="#fff"
android:visibility="gone"
android:src="@drawable/selected"
android:background="@drawable/gradient_bg"
android:layout_gravity="end"/>
</FrameLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<color name="white">#FF000000</color>
<color name="black">#FFFFFFFF</color>
@@ -8,4 +8,5 @@
<color name="colorPrimaryVariant">#00C15C</color>
<color name="colorSecondary">#39FF97</color>
<color name="textColor">#ffffff</color>
<color name="svgTintColor" tools:ignore="MissingDefaultResource">#ffffff</color>
</resources>

View File

@@ -1,20 +1,15 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Primary brand color -->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color -->
<item name="colorAccent">@color/primary</item>
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondary</item>
<item name="colorOnSecondary">@color/black</item>
<item name="fontFamily">@font/ubuntu_regular</item>
<!-- Status bar color -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<!-- Enable window decor fitting -->
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
</style>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
@@ -8,4 +8,5 @@
<color name="colorPrimaryVariant">#00C15C</color>
<color name="colorSecondary">#39FF97</color>
<color name="textColor">#000000</color>
<color name="svgTintColor" tools:ignore="MissingDefaultResource">#000000</color>
</resources>

View File

@@ -91,4 +91,39 @@
<string name="failed_to_create_uri_for_file">Failed to create URI for file</string>
<string name="error_unhiding_file">Error unhiding file: %1$s</string>
<string name="select_multiple">Select Multiple</string>
<string name="settings">Settings</string>
<string name="binondi_borthakur">Binondi Borthakur</string>
<string name="dynamic_theme">Dynamic Theme</string>
<string name="theme_mode">Theme Mode</string>
<string name="light">Light</string>
<string name="dark">Dark</string>
<string name="system_default">System Default</string>
<string name="full_app_name">Calculator Hide Files</string>
<string name="developer_details">Developer Details</string>
<string name="theme_settings">Theme Settings</string>
<string name="files_deleted_successfully">Files deleted successfully</string>
<string name="some_files_could_not_be_deleted">Some files could not be deleted</string>
<string name="un_hide_files">Unhide Files</string>
<string name="are_you_sure_you_want_to_un_hide_selected_files">Are you sure you want to unhide the selected files?</string>
<string name="files_unhidden_successfully">Files unhidden successfully</string>
<string name="some_files_could_not_be_unhidden">Some files could not be unhidden</string>
<string name="copy_to_another_folder">Copy to Another Folder</string>
<string name="move_to_another_folder">Move to Another Folder</string>
<string name="select_destination_folder">Select Destination Folder</string>
<string name="no_folders_available">No other folders available</string>
<string name="change_password">Change Password</string>
<string name="enter_old_password">Enter Old Password</string>
<string name="enter_new_password">Enter New Password</string>
<string name="forgot_password">Forgot Password?</string>
<string name="preview_file">Preview File</string>
<string name="show_file_names">Show File Names</string>
<string name="app_settings">App Settings</string>
<string name="restrict_screenshots_in_hidden_section">Restrict Screenshots in Hidden Section</string>
<string name="security_settings">Security Settings</string>
<string name="version">Version 1.3</string>
<string name="app_details">App Details</string>
<string name="setup_password">Setup Password</string>
<string name="security_question_for_password_reset">Security Question (For Password Reset)</string>
<string name="security_answer">Security Answer</string>
<string name="save_password">Save Password</string>
</resources>

View File

@@ -1,20 +1,18 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Primary brand color -->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
<item name="colorAccent">@color/primary</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color -->
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondary</item>
<item name="colorOnSecondary">@color/white</item>
<item name="fontFamily">@font/ubuntu_regular</item>
<!-- Status bar color -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="colorControlNormal">#483CFF61</item>
<!-- Enable window decor fitting -->
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
</style>