Changes For Folder Feature

This commit is contained in:
Binondi
2025-06-03 20:29:39 +05:30
parent 970cde737a
commit bfc339c101
31 changed files with 758 additions and 577 deletions

View File

@@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "devs.org.calculator",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 4,
"versionName": "1.3",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 26
}

View File

@@ -66,6 +66,10 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<meta-data
android:name="android.app.icon"
android:resource="@mipmap/ic_launcher_monochrome" />
</application> </application>
</manifest> </manifest>

View File

@@ -3,19 +3,14 @@ package devs.org.calculator
import android.app.Application import android.app.Application
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import devs.org.calculator.utils.PrefsUtil
class CalculatorApp : Application() { class CalculatorApp : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
val prefs = PrefsUtil(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) val themeMode = prefs.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
AppCompatDelegate.setDefaultNightMode(themeMode) AppCompatDelegate.setDefaultNightMode(themeMode)
// Apply dynamic colors only if dynamic theme is enabled
if (prefs.getBoolean("dynamic_theme", true)) { if (prefs.getBoolean("dynamic_theme", true)) {
DynamicColors.applyToActivitiesIfAvailable(this) DynamicColors.applyToActivitiesIfAvailable(this)
} }

View File

@@ -5,7 +5,6 @@ import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
@@ -14,6 +13,7 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R import devs.org.calculator.R
import devs.org.calculator.adapters.FolderAdapter import devs.org.calculator.adapters.FolderAdapter
@@ -36,22 +36,20 @@ class HiddenActivity : AppCompatActivity() {
private var folderAdapter: FolderAdapter? = null private var folderAdapter: FolderAdapter? = null
private var listFolderAdapter: ListFolderAdapter? = null private var listFolderAdapter: ListFolderAdapter? = null
private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR) private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
private val mainHandler = Handler(Looper.getMainLooper()) private val mainHandler = Handler(Looper.getMainLooper())
companion object {
private const val TAG = "HiddenActivity"
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityHiddenBinding.inflate(layoutInflater) binding = ActivityHiddenBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
fileManager = FileManager(this, this) fileManager = FileManager(this, this)
folderManager = FolderManager(this) folderManager = FolderManager()
dialogUtil = DialogUtil(this) dialogUtil = DialogUtil(this)
setupInitialUIState() setupInitialUIState()
setupClickListeners() setupClickListeners()
setupBackPressedHandler() setupBackPressedHandler()
@@ -100,20 +98,17 @@ class HiddenActivity : AppCompatActivity() {
} }
binding.folderOrientation.setOnClickListener { binding.folderOrientation.setOnClickListener {
// Switch between grid mode and list mode
val currentIsList = PrefsUtil(this).getBoolean("isList", false) val currentIsList = PrefsUtil(this).getBoolean("isList", false)
val newIsList = !currentIsList val newIsList = !currentIsList
if (newIsList) { if (newIsList) {
// Switch to list view
showListUI() showListUI()
PrefsUtil(this).setBoolean("isList", true) PrefsUtil(this).setBoolean("isList", true)
binding.folderOrientation.setImageResource(R.drawable.ic_grid) binding.folderOrientation.setIconResource(R.drawable.ic_grid)
} else { } else {
// Switch to grid view
showGridUI() showGridUI()
PrefsUtil(this).setBoolean("isList", false) PrefsUtil(this).setBoolean("isList", false)
binding.folderOrientation.setImageResource(R.drawable.ic_list) binding.folderOrientation.setIconResource(R.drawable.ic_list)
} }
} }
} }
@@ -141,11 +136,10 @@ class HiddenActivity : AppCompatActivity() {
showEmptyState() showEmptyState()
} }
} else { } else {
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
showEmptyState() showEmptyState()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error listing folders: ${e.message}")
showEmptyState() showEmptyState()
} }
} }
@@ -173,7 +167,6 @@ class HiddenActivity : AppCompatActivity() {
folderManager.createFolder(hiddenDir, newName) folderManager.createFolder(hiddenDir, newName)
refreshCurrentView() refreshCurrentView()
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error creating folder: ${e.message}")
Toast.makeText( Toast.makeText(
this@HiddenActivity, this@HiddenActivity,
"Failed to create folder", "Failed to create folder",
@@ -220,11 +213,9 @@ class HiddenActivity : AppCompatActivity() {
showEmptyState() showEmptyState()
} }
} else { } else {
Log.e(TAG, "Hidden directory is not accessible: ${hiddenDir.absolutePath}")
showEmptyState() showEmptyState()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error listing folders: ${e.message}")
showEmptyState() showEmptyState()
} }
} }
@@ -232,8 +223,6 @@ class HiddenActivity : AppCompatActivity() {
private fun showFolderList(folders: List<File>) { private fun showFolderList(folders: List<File>) {
binding.noItems.visibility = View.GONE binding.noItems.visibility = View.GONE
binding.recyclerView.visibility = View.VISIBLE binding.recyclerView.visibility = View.VISIBLE
// Clear the existing adapter to avoid conflicts
listFolderAdapter = null listFolderAdapter = null
binding.recyclerView.layoutManager = GridLayoutManager(this, 2) binding.recyclerView.layoutManager = GridLayoutManager(this, 2)
@@ -247,14 +236,13 @@ class HiddenActivity : AppCompatActivity() {
onSelectionModeChanged = { isSelectionMode -> onSelectionModeChanged = { isSelectionMode ->
handleFolderSelectionModeChange(isSelectionMode) handleFolderSelectionModeChange(isSelectionMode)
}, },
onSelectionCountChanged = { selectedCount -> onSelectionCountChanged = { _ ->
updateEditButtonVisibility() updateEditButtonVisibility()
} }
) )
binding.recyclerView.adapter = folderAdapter binding.recyclerView.adapter = folderAdapter
folderAdapter?.submitList(folders) folderAdapter?.submitList(folders)
// Ensure proper icon state for folder view
if (folderAdapter?.isInSelectionMode() != true) { if (folderAdapter?.isInSelectionMode() != true) {
showFolderViewIcons() showFolderViewIcons()
} }
@@ -262,8 +250,6 @@ class HiddenActivity : AppCompatActivity() {
private fun showFolderListStyle(folders: List<File>) { private fun showFolderListStyle(folders: List<File>) {
binding.noItems.visibility = View.GONE binding.noItems.visibility = View.GONE
binding.recyclerView.visibility = View.VISIBLE binding.recyclerView.visibility = View.VISIBLE
// Clear the existing adapter to avoid conflicts
folderAdapter = null folderAdapter = null
binding.recyclerView.layoutManager = GridLayoutManager(this, 1) binding.recyclerView.layoutManager = GridLayoutManager(this, 1)
@@ -277,14 +263,13 @@ class HiddenActivity : AppCompatActivity() {
onSelectionModeChanged = { isSelectionMode -> onSelectionModeChanged = { isSelectionMode ->
handleFolderSelectionModeChange(isSelectionMode) handleFolderSelectionModeChange(isSelectionMode)
}, },
onSelectionCountChanged = { selectedCount -> onSelectionCountChanged = { _ ->
updateEditButtonVisibility() updateEditButtonVisibility()
} }
) )
binding.recyclerView.adapter = listFolderAdapter binding.recyclerView.adapter = listFolderAdapter
listFolderAdapter?.submitList(folders) listFolderAdapter?.submitList(folders)
// Ensure proper icon state for folder view
if (listFolderAdapter?.isInSelectionMode() != true) { if (listFolderAdapter?.isInSelectionMode() != true) {
showFolderViewIcons() showFolderViewIcons()
} }
@@ -312,10 +297,10 @@ class HiddenActivity : AppCompatActivity() {
private fun refreshCurrentView() { private fun refreshCurrentView() {
val isList = PrefsUtil(this).getBoolean("isList", false) val isList = PrefsUtil(this).getBoolean("isList", false)
if (isList) { if (isList) {
binding.folderOrientation.setImageResource(R.drawable.ic_grid) binding.folderOrientation.setIconResource(R.drawable.ic_grid)
listFoldersInHiddenDirectoryListStyle() listFoldersInHiddenDirectoryListStyle()
} else { } else {
binding.folderOrientation.setImageResource(R.drawable.ic_list) binding.folderOrientation.setIconResource(R.drawable.ic_list)
listFoldersInHiddenDirectory() listFoldersInHiddenDirectory()
} }
} }
@@ -344,11 +329,11 @@ class HiddenActivity : AppCompatActivity() {
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {
// Do nothing
} }
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {
// Do nothing
} }
} }
) )
@@ -360,7 +345,6 @@ class HiddenActivity : AppCompatActivity() {
selectedFolders.forEach { folder -> selectedFolders.forEach { folder ->
if (!folderManager.deleteFolder(folder)) { if (!folderManager.deleteFolder(folder)) {
allDeleted = false allDeleted = false
Log.e(TAG, "Failed to delete folder: ${folder.name}")
} }
} }
@@ -372,26 +356,20 @@ class HiddenActivity : AppCompatActivity() {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Clear selection from both adapters
folderAdapter?.clearSelection() folderAdapter?.clearSelection()
listFolderAdapter?.clearSelection() listFolderAdapter?.clearSelection()
// This will trigger the selection mode change callback and show proper icons
exitFolderSelectionMode() exitFolderSelectionMode()
// Refresh the current view based on orientation
refreshCurrentView() refreshCurrentView()
} }
private fun handleBackPress() { private fun handleBackPress() {
// Check if folder adapters are in selection mode
if (folderAdapter?.onBackPressed() == true || listFolderAdapter?.onBackPressed() == true) { if (folderAdapter?.onBackPressed() == true || listFolderAdapter?.onBackPressed() == true) {
return return
} }
// Handle navigation back
if (currentFolder != null) { if (currentFolder != null) {
navigateBackToFolders() navigateBackToFolders()
} else { } else {
@@ -402,25 +380,18 @@ class HiddenActivity : AppCompatActivity() {
private fun navigateBackToFolders() { private fun navigateBackToFolders() {
currentFolder = null currentFolder = null
// Clean up file adapter
refreshCurrentView() refreshCurrentView()
binding.folderName.text = getString(R.string.hidden_space) binding.folderName.text = getString(R.string.hidden_space)
// Set proper icons for folder view
showFolderViewIcons() showFolderViewIcons()
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
// Remove any pending callbacks
mainHandler.removeCallbacksAndMessages(null) mainHandler.removeCallbacksAndMessages(null)
} }
//visibility related code
private fun showFolderViewIcons() { private fun showFolderViewIcons() {
binding.folderOrientation.visibility = View.VISIBLE binding.folderOrientation.visibility = View.VISIBLE
binding.settings.visibility = View.VISIBLE binding.settings.visibility = View.VISIBLE
@@ -429,7 +400,6 @@ class HiddenActivity : AppCompatActivity() {
binding.menuButton.visibility = View.GONE binding.menuButton.visibility = View.GONE
binding.addFolder.visibility = View.VISIBLE binding.addFolder.visibility = View.VISIBLE
binding.edit.visibility = View.GONE binding.edit.visibility = View.GONE
// Ensure FABs are properly managed
if (currentFolder == null) { if (currentFolder == null) {
binding.addFolder.visibility = View.VISIBLE binding.addFolder.visibility = View.VISIBLE
@@ -442,8 +412,6 @@ class HiddenActivity : AppCompatActivity() {
binding.deleteSelected.visibility = View.VISIBLE binding.deleteSelected.visibility = View.VISIBLE
binding.menuButton.visibility = View.GONE binding.menuButton.visibility = View.GONE
binding.addFolder.visibility = View.GONE binding.addFolder.visibility = View.GONE
// Update edit button visibility based on current selection count
updateEditButtonVisibility() updateEditButtonVisibility()
} }
private fun exitFolderSelectionMode() { private fun exitFolderSelectionMode() {
@@ -456,7 +424,6 @@ class HiddenActivity : AppCompatActivity() {
} else { } else {
enterFolderSelectionMode() enterFolderSelectionMode()
} }
// Always update edit button visibility when selection mode changes
updateEditButtonVisibility() updateEditButtonVisibility()
} }
@@ -520,11 +487,8 @@ class HiddenActivity : AppCompatActivity() {
} }
if (oldFolder.renameTo(newFolder)) { if (oldFolder.renameTo(newFolder)) {
// Clear selection from both adapters
folderAdapter?.clearSelection() folderAdapter?.clearSelection()
listFolderAdapter?.clearSelection() listFolderAdapter?.clearSelection()
// Exit selection mode
exitFolderSelectionMode() exitFolderSelectionMode()
refreshCurrentView() refreshCurrentView()

View File

@@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
@@ -22,6 +23,8 @@ import devs.org.calculator.utils.FileManager
import devs.org.calculator.utils.PrefsUtil import devs.org.calculator.utils.PrefsUtil
import net.objecthunter.exp4j.ExpressionBuilder import net.objecthunter.exp4j.ExpressionBuilder
import java.util.regex.Pattern import java.util.regex.Pattern
import androidx.core.content.edit
import com.google.android.material.color.DynamicColors
class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.DialogCallback { class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.DialogCallback {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@@ -34,13 +37,14 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
private val dialogUtil = DialogUtil(this) private val dialogUtil = DialogUtil(this)
private val fileManager = FileManager(this, this) private val fileManager = FileManager(this, this)
private val sp by lazy { getSharedPreferences("app", MODE_PRIVATE) } private val sp by lazy { getSharedPreferences("app", MODE_PRIVATE) }
private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
enableEdgeToEdge()
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
handleActivityResult(result) handleActivityResult(result)
} }
@@ -49,15 +53,14 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
binding.display.text = getString(R.string.enter_123456) binding.display.text = getString(R.string.enter_123456)
} }
// Ask permission
if(!Environment.isExternalStorageManager()) { if(!Environment.isExternalStorageManager()) {
dialogUtil.showMaterialDialog( dialogUtil.showMaterialDialog(
"Storage Permission", getString(R.string.storage_permission),
"To ensure the app works properly and allows you to easily hide or un-hide your private files, please grant storage access permission.\n" + getString(R.string.to_ensure_the_app_works_properly_and_allows_you_to_easily_hide_or_un_hide_your_private_files_please_grant_storage_access_permission) +
"\n" + "\n" +
"For devices running Android 11 or higher, you'll need to grant the 'All Files Access' permission.", getString(R.string.for_devices_running_android_11_or_higher_you_ll_need_to_grant_the_all_files_access_permission),
"Grant", getString(R.string.grant_permission),
"Later", getString(R.string.later),
object : DialogUtil.DialogCallback { object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() { override fun onPositiveButtonClicked() {
fileManager.askPermission(this@MainActivity) fileManager.askPermission(this@MainActivity)
@@ -65,19 +68,20 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {
Toast.makeText(this@MainActivity, Toast.makeText(this@MainActivity,
"Storage permission is required for the app to function properly", getString(R.string.storage_permission_is_required_for_the_app_to_function_properly),
Toast.LENGTH_LONG).show() Toast.LENGTH_LONG).show()
} }
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {
Toast.makeText(this@MainActivity, Toast.makeText(this@MainActivity,
"You can grant permission later from Settings", getString(R.string.you_can_grant_permission_later_from_settings),
Toast.LENGTH_LONG).show() Toast.LENGTH_LONG).show()
} }
} }
) )
} }
setupNumberButton(binding.btn0, "0") setupNumberButton(binding.btn0, "0")
setupNumberButton(binding.btn00, "00")
setupNumberButton(binding.btn1, "1") setupNumberButton(binding.btn1, "1")
setupNumberButton(binding.btn2, "2") setupNumberButton(binding.btn2, "2")
setupNumberButton(binding.btn3, "3") setupNumberButton(binding.btn3, "3")
@@ -99,6 +103,7 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
binding.cut.setOnClickListener { cutNumbers() } binding.cut.setOnClickListener { cutNumbers() }
} }
private fun handleActivityResult(result: androidx.activity.result.ActivityResult) { private fun handleActivityResult(result: androidx.activity.result.ActivityResult) {
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri -> result.data?.data?.let { uri ->
@@ -107,10 +112,8 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
contentResolver.takePersistableUriPermission(uri, takeFlags) contentResolver.takePersistableUriPermission(uri, takeFlags)
val preferences = getSharedPreferences("com.example.fileutility", MODE_PRIVATE) val preferences = getSharedPreferences("com.example.fileutility", MODE_PRIVATE)
preferences.edit().putString("filestorageuri", uri.toString()).apply() preferences.edit { putString("filestorageuri", uri.toString()) }
} }
} else {
Log.e("FileUtility", "Error occurred or operation cancelled: $result")
} }
} }
@@ -149,7 +152,7 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
} }
private fun clearDisplay() { private fun clearDisplay() {
currentExpression = "0" currentExpression = ""
binding.total.text = "" binding.total.text = ""
lastWasOperator = false lastWasOperator = false
lastWasPercent = false lastWasPercent = false
@@ -173,41 +176,24 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
} }
} }
private fun calculatePercentage() {
try {
val value = currentExpression.toDouble()
currentExpression = (value / 100).toString()
updateDisplay()
} catch (e: Exception) {
binding.display.text = getString(R.string.invalid_message)
}
}
private fun preprocessExpression(expression: String): String { private fun preprocessExpression(expression: String): String {
val percentagePattern = Pattern.compile("(\\d+\\.?\\d*)%") val percentagePattern = Pattern.compile("(\\d+\\.?\\d*)%")
val operatorPercentPattern = Pattern.compile("([+\\-*/])(\\d+\\.?\\d*)%") val operatorPercentPattern = Pattern.compile("([+\\-*/])(\\d+\\.?\\d*)%")
var processedExpression = expression var processedExpression = expression
// Replace standalone percentages (like "50%") with their decimal form (0.5)
val matcher = percentagePattern.matcher(processedExpression) val matcher = percentagePattern.matcher(processedExpression)
while (matcher.find()) { while (matcher.find()) {
val fullMatch = matcher.group(0) val fullMatch = matcher.group(0)
val number = matcher.group(1) val number = matcher.group(1)
// Check if it's a standalone percentage or part of an operation
val start = matcher.start() val start = matcher.start()
if (start == 0 || !isOperator(processedExpression[start-1].toString())) { if (start == 0 || !isOperator(processedExpression[start-1].toString())) {
val percentageValue = number.toDouble() / 100 val percentageValue = number!!.toDouble() / 100
processedExpression = processedExpression.replace(fullMatch, percentageValue.toString()) processedExpression = processedExpression.replace(fullMatch!!.toString(), percentageValue.toString())
} }
} }
// Handle operator-percentage combinations (like "100-20%")
val opMatcher = operatorPercentPattern.matcher(processedExpression) val opMatcher = operatorPercentPattern.matcher(processedExpression)
val sb = StringBuilder(processedExpression) val sb = StringBuilder(processedExpression)
// We need to process matches from right to left to maintain indices
val matches = mutableListOf<Triple<Int, Int, String>>() val matches = mutableListOf<Triple<Int, Int, String>>()
while (opMatcher.find()) { while (opMatcher.find()) {
@@ -219,15 +205,11 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
matches.add(Triple(start, end, "$operator$percentValue%")) matches.add(Triple(start, end, "$operator$percentValue%"))
} }
// Process matches from right to left
for (match in matches.reversed()) { for (match in matches.reversed()) {
val (start, end, fullMatch) = match val (start, end, fullMatch) = match
// Find the number before this operator
var leftNumberEnd = start
var leftNumberStart = start - 1 var leftNumberStart = start - 1
// Skip parentheses and move to the actual number
if (leftNumberStart >= 0 && sb[leftNumberStart] == ')') { if (leftNumberStart >= 0 && sb[leftNumberStart] == ')') {
var openParens = 1 var openParens = 1
leftNumberStart-- leftNumberStart--
@@ -238,7 +220,6 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
leftNumberStart-- leftNumberStart--
} }
// Now we need to find the start of the expression
if (leftNumberStart >= 0) { if (leftNumberStart >= 0) {
while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.' || sb[leftNumberStart] == '-')) { while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.' || sb[leftNumberStart] == '-')) {
leftNumberStart-- leftNumberStart--
@@ -248,26 +229,24 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
leftNumberStart = 0 leftNumberStart = 0
} }
} else { } else {
// For simple numbers, just find the start of the number
while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.')) { while (leftNumberStart >= 0 && (isDigit(sb[leftNumberStart].toString()) || sb[leftNumberStart] == '.')) {
leftNumberStart-- leftNumberStart--
} }
leftNumberStart++ leftNumberStart++
} }
if (leftNumberStart < leftNumberEnd) { if (leftNumberStart < start) {
val leftPart = sb.substring(leftNumberStart, leftNumberEnd) val leftPart = sb.substring(leftNumberStart, start)
try { try {
// Extract the numerical values
val baseNumber = evaluateExpression(leftPart) val baseNumber = evaluateExpression(leftPart)
val operator = fullMatch.substring(0, 1) val operator = fullMatch.substring(0, 1)
val percentNumber = fullMatch.substring(1, fullMatch.length - 1).toDouble() val percentNumber = fullMatch.substring(1, fullMatch.length - 1).toDouble()
// Calculate the percentage of the base number
val percentValue = baseNumber * (percentNumber / 100) val percentValue = baseNumber * (percentNumber / 100)
// Calculate the new value based on the operator
val newValue = when (operator) { val newValue = when (operator) {
"+" -> baseNumber + percentValue "+" -> baseNumber + percentValue
"-" -> baseNumber - percentValue "-" -> baseNumber - percentValue
@@ -276,7 +255,6 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
else -> baseNumber else -> baseNumber
} }
// Replace the entire expression "number operator percent%" with the result
sb.replace(leftNumberStart, end, newValue.toString()) sb.replace(leftNumberStart, end, newValue.toString())
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Calculator", "Error processing percentage expression: $e") Log.e("Calculator", "Error processing percentage expression: $e")
@@ -303,10 +281,11 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
} }
} }
@SuppressLint("DefaultLocale")
private fun calculateResult() { private fun calculateResult() {
if (currentExpression == "123456") { if (currentExpression == "123456") {
val intent = Intent(this, SetupPasswordActivity::class.java) val intent = Intent(this, SetupPasswordActivity::class.java)
sp.edit().putBoolean("isFirst", false).apply() sp.edit { putBoolean("isFirst", false) }
intent.putExtra("password", currentExpression) intent.putExtra("password", currentExpression)
startActivity(intent) startActivity(intent)
clearDisplay() clearDisplay()
@@ -363,8 +342,6 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
binding.total.text = "" binding.total.text = ""
return return
} }
// Process the expression for preview calculation
var processedExpression = currentExpression.replace("×", "*") var processedExpression = currentExpression.replace("×", "*")
if (processedExpression.contains("%")) { if (processedExpression.contains("%")) {
@@ -411,27 +388,24 @@ class MainActivity : AppCompatActivity(), DialogActionsCallback, DialogUtil.Dial
} }
override fun onPositiveButtonClicked() { override fun onPositiveButtonClicked() {
// Handle positive button click for both DialogUtil and DialogActionsCallback
fileManager.askPermission(this) fileManager.askPermission(this)
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {
// Handle negative button click Toast.makeText(this, getString(R.string.storage_permission_is_required_for_the_app_to_function_properly), Toast.LENGTH_LONG).show()
Toast.makeText(this, "Storage permission is required for the app to function properly", Toast.LENGTH_LONG).show()
} }
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {
// Handle neutral button click Toast.makeText(this, getString(R.string.you_can_grant_permission_later_from_settings), Toast.LENGTH_LONG).show()
Toast.makeText(this, "You can grant permission later from Settings", Toast.LENGTH_LONG).show()
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 6767) { if (requestCode == 6767) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.permission_granted), Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.permission_denied), Toast.LENGTH_SHORT).show()
} }
} }
} }

View File

@@ -6,11 +6,13 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.color.DynamicColors
import devs.org.calculator.R import devs.org.calculator.R
import devs.org.calculator.adapters.ImagePreviewAdapter import devs.org.calculator.adapters.ImagePreviewAdapter
import devs.org.calculator.databinding.ActivityPreviewBinding import devs.org.calculator.databinding.ActivityPreviewBinding
import devs.org.calculator.utils.DialogUtil import devs.org.calculator.utils.DialogUtil
import devs.org.calculator.utils.FileManager import devs.org.calculator.utils.FileManager
import devs.org.calculator.utils.PrefsUtil
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
@@ -18,43 +20,31 @@ class PreviewActivity : AppCompatActivity() {
private lateinit var binding: ActivityPreviewBinding private lateinit var binding: ActivityPreviewBinding
private var currentPosition: Int = 0 private var currentPosition: Int = 0
private lateinit var files: List<File> private var files: MutableList<File> = mutableListOf()
private lateinit var type: String private lateinit var type: String
private lateinit var folder: String private lateinit var folder: String
private lateinit var filetype: FileManager.FileType private lateinit var filetype: FileManager.FileType
private lateinit var adapter: ImagePreviewAdapter private lateinit var adapter: ImagePreviewAdapter
private lateinit var fileManager: FileManager private lateinit var fileManager: FileManager
private val dialogUtil = DialogUtil(this) private val dialogUtil = DialogUtil(this)
private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityPreviewBinding.inflate(layoutInflater) binding = ActivityPreviewBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
fileManager = FileManager(this, this) fileManager = FileManager(this, this)
currentPosition = intent.getIntExtra("position", 0) currentPosition = intent.getIntExtra("position", 0)
type = intent.getStringExtra("type").toString() type = intent.getStringExtra("type") ?: "IMAGE"
folder = intent.getStringExtra("folder").toString() folder = intent.getStringExtra("folder") ?: ""
setupFileType() setupFileType()
files = fileManager.getFilesInHiddenDirFromFolder(filetype, folder = folder) loadFiles()
setupImagePreview() setupImagePreview()
clickListeners() setupClickListeners()
setupPageChangeCallback()
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
}
})
binding.back.setOnClickListener {
finish()
}
} }
override fun onResume() { override fun onResume() {
@@ -62,6 +52,16 @@ class PreviewActivity : AppCompatActivity() {
setupFlagSecure() setupFlagSecure()
} }
override fun onPause() {
super.onPause()
adapter.releaseAllResources()
}
override fun onDestroy() {
super.onDestroy()
adapter.releaseAllResources()
}
private fun setupFlagSecure() { private fun setupFlagSecure() {
val prefs = getSharedPreferences("app_settings", MODE_PRIVATE) val prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
if (prefs.getBoolean("screenshot_restriction", true)) { if (prefs.getBoolean("screenshot_restriction", true)) {
@@ -93,39 +93,83 @@ class PreviewActivity : AppCompatActivity() {
} }
} }
private fun loadFiles() {
try {
val filesList = fileManager.getFilesInHiddenDirFromFolder(filetype, folder = folder)
files = filesList.toMutableList()
if (currentPosition >= files.size) {
currentPosition = 0
}
} catch (e: Exception) {
e.printStackTrace()
files = mutableListOf()
}
}
private fun setupImagePreview() { private fun setupImagePreview() {
if (files.isEmpty()) {
finish()
return
}
adapter = ImagePreviewAdapter(this, this) adapter = ImagePreviewAdapter(this, this)
adapter.images = files adapter.images = files
binding.viewPager.adapter = adapter binding.viewPager.adapter = adapter
if (currentPosition < files.size) {
binding.viewPager.setCurrentItem(currentPosition, false) binding.viewPager.setCurrentItem(currentPosition, false)
}
updateFileInfo()
}
private fun setupPageChangeCallback() {
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
currentPosition = position
updateFileInfo()
}
})
}
private fun updateFileInfo() {
if (files.isNotEmpty() && currentPosition < files.size) {
val fileUri = Uri.fromFile(files[currentPosition]) val fileUri = Uri.fromFile(files[currentPosition])
val fileName = FileManager.FileName(this).getFileNameFromUri(fileUri).toString() val fileName = FileManager.FileName(this).getFileNameFromUri(fileUri) ?: "Unknown"
} //For Now File Name not Needed, i am keeping it for later use
override fun onPause() {
super.onPause()
if (filetype == FileManager.FileType.AUDIO) {
(binding.viewPager.adapter as? ImagePreviewAdapter)?.currentMediaPlayer?.pause()
} }
} }
override fun onDestroy() {
super.onDestroy()
if (filetype == FileManager.FileType.AUDIO) {
(binding.viewPager.adapter as? ImagePreviewAdapter)?.let { adapter ->
adapter.currentMediaPlayer?.release()
adapter.currentMediaPlayer = null
}
}
}
private fun clickListeners() { private fun setupClickListeners() {
binding.back.setOnClickListener {
finish()
}
binding.delete.setOnClickListener { binding.delete.setOnClickListener {
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem]) handleDeleteFile()
}
binding.unHide.setOnClickListener {
handleUnhideFile()
}
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
adapter.onItemScrolledAway(currentPosition)
currentPosition = position
}
})
}
private fun handleDeleteFile() {
if (files.isEmpty() || currentPosition >= files.size) return
val currentFile = files[currentPosition]
val fileUri = FileManager.FileManager().getContentUriImage(this, currentFile)
if (fileUri != null) { if (fileUri != null) {
dialogUtil.showMaterialDialog( dialogUtil.showMaterialDialog(
getString(R.string.delete_file), getString(R.string.delete_file),
@@ -135,25 +179,29 @@ class PreviewActivity : AppCompatActivity() {
object : DialogUtil.DialogCallback { object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() { override fun onPositiveButtonClicked() {
lifecycleScope.launch { lifecycleScope.launch {
FileManager(this@PreviewActivity, this@PreviewActivity).deletePhotoFromExternalStorage(fileUri) try {
removeFileFromList(binding.viewPager.currentItem) fileManager.deletePhotoFromExternalStorage(fileUri)
removeFileFromList(currentPosition)
} catch (e: Exception) {
e.printStackTrace()
}
} }
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {}
// Handle negative button click
}
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {}
// Handle neutral button click
}
} }
) )
} }
} }
binding.unHide.setOnClickListener { private fun handleUnhideFile() {
val fileUri = FileManager.FileManager().getContentUriImage(this, files[binding.viewPager.currentItem]) if (files.isEmpty() || currentPosition >= files.size) return
val currentFile = files[currentPosition]
val fileUri = FileManager.FileManager().getContentUriImage(this, currentFile)
if (fileUri != null) { if (fileUri != null) {
dialogUtil.showMaterialDialog( dialogUtil.showMaterialDialog(
getString(R.string.un_hide_file), getString(R.string.un_hide_file),
@@ -163,38 +211,44 @@ class PreviewActivity : AppCompatActivity() {
object : DialogUtil.DialogCallback { object : DialogUtil.DialogCallback {
override fun onPositiveButtonClicked() { override fun onPositiveButtonClicked() {
lifecycleScope.launch { lifecycleScope.launch {
FileManager(this@PreviewActivity, this@PreviewActivity).copyFileToNormalDir(fileUri) try {
removeFileFromList(binding.viewPager.currentItem) fileManager.copyFileToNormalDir(fileUri)
removeFileFromList(currentPosition)
} catch (e: Exception) {
e.printStackTrace()
}
} }
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {}
// Handle negative button click
}
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {}
// Handle neutral button click
}
} }
) )
} }
} }
}
private fun removeFileFromList(position: Int) { private fun removeFileFromList(position: Int) {
val updatedFiles = files.toMutableList().apply { removeAt(position) } if (position < 0 || position >= files.size) return
files = updatedFiles adapter.releaseAllResources()
adapter.images = updatedFiles // Update adapter with the new list files.removeAt(position)
adapter.images = files
// Update the ViewPager's position if (files.isEmpty()) {
if (updatedFiles.isEmpty()) finish() finish()
return
}
currentPosition = if (position >= files.size) {
files.size - 1
} else {
position
}
binding.viewPager.setCurrentItem(currentPosition, false)
updateFileInfo()
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
onBackPressed() finish()
return true return true
} }
} }

View File

@@ -1,36 +1,32 @@
package devs.org.calculator.activities package devs.org.calculator.activities
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.ViewCompat import androidx.core.net.toUri
import androidx.core.view.WindowCompat import com.google.android.material.color.DynamicColors
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import devs.org.calculator.R import devs.org.calculator.R
import devs.org.calculator.databinding.ActivitySettingsBinding import devs.org.calculator.databinding.ActivitySettingsBinding
import devs.org.calculator.utils.PrefsUtil
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
private lateinit var prefs: SharedPreferences private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
private val DEV_GITHUB_URL = "https://github.com/binondi" private var DEV_GITHUB_URL = ""
private val GITHUB_URL = "$DEV_GITHUB_URL/calculator-hide-files" private var GITHUB_URL = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
DEV_GITHUB_URL = getString(R.string.github_profile)
prefs = getSharedPreferences("app_settings", MODE_PRIVATE) GITHUB_URL = getString(R.string.calculator_hide_files, DEV_GITHUB_URL)
setupUI() setupUI()
loadSettings() loadSettings()
setupListeners() setupListeners()
@@ -42,6 +38,7 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
private fun loadSettings() { private fun loadSettings() {
binding.dynamicThemeSwitch.isChecked = prefs.getBoolean("dynamic_theme", true) binding.dynamicThemeSwitch.isChecked = prefs.getBoolean("dynamic_theme", true)
@@ -72,9 +69,14 @@ class SettingsActivity : AppCompatActivity() {
} }
binding.dynamicThemeSwitch.setOnCheckedChangeListener { _, isChecked -> binding.dynamicThemeSwitch.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("dynamic_theme", isChecked).apply() prefs.setBoolean("dynamic_theme", isChecked)
if (!isChecked) { if (!isChecked) {
showThemeModeDialog() showThemeModeDialog()
}else{
showThemeModeDialog()
if (!prefs.getBoolean("isAppReopened",false)){
DynamicColors.applyToActivityIfAvailable(this)
}
} }
} }
@@ -97,7 +99,7 @@ class SettingsActivity : AppCompatActivity() {
} }
binding.screenshotRestrictionSwitch.setOnCheckedChangeListener { _, isChecked -> binding.screenshotRestrictionSwitch.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("screenshot_restriction", isChecked).apply() prefs.setBoolean("screenshot_restriction", isChecked)
if (isChecked) { if (isChecked) {
enableScreenshotRestriction() enableScreenshotRestriction()
} else { } else {
@@ -105,7 +107,7 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
binding.showFileNames.setOnCheckedChangeListener { _, isChecked -> binding.showFileNames.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("showFileName", isChecked).apply() prefs.setBoolean("showFileName", isChecked)
} }
} }
@@ -115,20 +117,16 @@ class SettingsActivity : AppCompatActivity() {
private fun showThemeModeDialog() { private fun showThemeModeDialog() {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle("Theme Mode") .setTitle(getString(R.string.attention))
.setMessage("Would you like to set a specific theme mode?") .setMessage(getString(R.string.if_you_turn_on_off_this_option_dynamic_theme_changes_will_be_visible_after_you_reopen_the_app))
.setPositiveButton("Yes") { _, _ -> .setPositiveButton(getString(R.string.ok)) { _, _ ->
binding.themeModeSwitch.isChecked = true
}
.setNegativeButton("No") { _, _ ->
binding.systemThemeRadio.isChecked = true
applyThemeMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
} }
.show() .show()
} }
private fun applyThemeMode(themeMode: Int) { private fun applyThemeMode(themeMode: Int) {
prefs.edit().putInt("theme_mode", themeMode).apply() prefs.setInt("theme_mode", themeMode)
AppCompatDelegate.setDefaultNightMode(themeMode) AppCompatDelegate.setDefaultNightMode(themeMode)
} }
@@ -145,10 +143,11 @@ class SettingsActivity : AppCompatActivity() {
private fun openUrl(url: String) { private fun openUrl(url: String) {
try { try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val intent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
Snackbar.make(binding.root, "Could not open URL", Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root,
getString(R.string.could_not_open_url), Snackbar.LENGTH_SHORT).show()
} }
} }
} }

View File

@@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import devs.org.calculator.databinding.ActivitySetupPasswordBinding import devs.org.calculator.databinding.ActivitySetupPasswordBinding
@@ -18,6 +19,7 @@ class SetupPasswordActivity : AppCompatActivity() {
private lateinit var binding2: ActivityChangePasswordBinding private lateinit var binding2: ActivityChangePasswordBinding
private lateinit var prefsUtil: PrefsUtil private lateinit var prefsUtil: PrefsUtil
private var hasPassword = false private var hasPassword = false
private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -72,8 +74,6 @@ class SetupPasswordActivity : AppCompatActivity() {
} }
binding.btnResetPassword.setOnClickListener { binding.btnResetPassword.setOnClickListener {
// Implement password reset logic
// Could use security questions or email verification
if (prefsUtil.getSecurityQuestion() != null) showSecurityQuestionDialog(prefsUtil.getSecurityQuestion().toString()) if (prefsUtil.getSecurityQuestion() != null) showSecurityQuestionDialog(prefsUtil.getSecurityQuestion().toString())
else Toast.makeText(this, else Toast.makeText(this,
getString(R.string.security_question_not_set_yet), Toast.LENGTH_SHORT).show() getString(R.string.security_question_not_set_yet), Toast.LENGTH_SHORT).show()

View File

@@ -2,7 +2,6 @@ package devs.org.calculator.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
@@ -20,8 +19,9 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import devs.org.calculator.R import devs.org.calculator.R
import devs.org.calculator.adapters.FileAdapter import devs.org.calculator.adapters.FileAdapter
import devs.org.calculator.adapters.FolderSelectionAdapter import devs.org.calculator.adapters.FolderSelectionAdapter
@@ -32,6 +32,7 @@ import devs.org.calculator.utils.DialogUtil
import devs.org.calculator.utils.FileManager import devs.org.calculator.utils.FileManager
import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR import devs.org.calculator.utils.FileManager.Companion.HIDDEN_DIR
import devs.org.calculator.utils.FolderManager import devs.org.calculator.utils.FolderManager
import devs.org.calculator.utils.PrefsUtil
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
@@ -51,12 +52,12 @@ class ViewFolderActivity : AppCompatActivity() {
private var currentFolder: File? = null private var currentFolder: File? = null
private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR) private val hiddenDir = File(Environment.getExternalStorageDirectory(), HIDDEN_DIR)
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent> private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
private lateinit var prefs: SharedPreferences
private var customDialog: androidx.appcompat.app.AlertDialog? = null private var customDialog: androidx.appcompat.app.AlertDialog? = null
private var dialogShowTime: Long = 0 private var dialogShowTime: Long = 0
private val MINIMUM_DIALOG_DURATION = 1200L private val MINIMUM_DIALOG_DURATION = 1200L
private val prefs:PrefsUtil by lazy { PrefsUtil(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -81,11 +82,11 @@ class ViewFolderActivity : AppCompatActivity() {
private fun initialize() { private fun initialize() {
fileManager = FileManager(this, this) fileManager = FileManager(this, this)
folderManager = FolderManager(this) folderManager = FolderManager()
dialogUtil = DialogUtil(this) dialogUtil = DialogUtil(this)
prefs = getSharedPreferences("app_settings", MODE_PRIVATE)
} }
private fun setupActivityResultLaunchers() { private fun setupActivityResultLaunchers() {
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
@@ -192,19 +193,12 @@ class ViewFolderActivity : AppCompatActivity() {
} }
} }
private fun refreshCurrentView() {
if (currentFolder != null) {
refreshCurrentFolder()
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
refreshCurrentFolder() refreshCurrentFolder()
} }
private fun openFolder(folder: File) { private fun openFolder(folder: File) {
// Ensure folder exists and has .nomedia file
if (!folder.exists()) { if (!folder.exists()) {
folder.mkdirs() folder.mkdirs()
File(folder, ".nomedia").createNewFile() File(folder, ".nomedia").createNewFile()
@@ -227,8 +221,6 @@ class ViewFolderActivity : AppCompatActivity() {
private fun showFileList(files: List<File>, folder: File) { private fun showFileList(files: List<File>, folder: File) {
binding.recyclerView.layoutManager = GridLayoutManager(this, 3) binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
// Clean up previous adapter
fileAdapter?.cleanup() fileAdapter?.cleanup()
fileAdapter = FileAdapter(this, this, folder, prefs.getBoolean("showFileName", true), fileAdapter = FileAdapter(this, this, folder, prefs.getBoolean("showFileName", true),
@@ -344,13 +336,9 @@ class ViewFolderActivity : AppCompatActivity() {
performFileUnhiding(selectedFiles) performFileUnhiding(selectedFiles)
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {}
// Do nothing
}
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {}
// Do nothing
}
} }
) )
} }
@@ -368,13 +356,9 @@ class ViewFolderActivity : AppCompatActivity() {
performFileDeletion(selectedFiles) performFileDeletion(selectedFiles)
} }
override fun onNegativeButtonClicked() { override fun onNegativeButtonClicked() {}
// Do nothing
}
override fun onNaturalButtonClicked() { override fun onNaturalButtonClicked() {}
// Do nothing
}
} }
) )
} }
@@ -523,8 +507,6 @@ class ViewFolderActivity : AppCompatActivity() {
} }
Toast.makeText(this@ViewFolderActivity, message, Toast.LENGTH_SHORT).show() Toast.makeText(this@ViewFolderActivity, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode() fileAdapter?.exitSelectionMode()
refreshCurrentFolder() refreshCurrentFolder()
} }
@@ -546,8 +528,6 @@ class ViewFolderActivity : AppCompatActivity() {
} }
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode() fileAdapter?.exitSelectionMode()
refreshCurrentFolder() refreshCurrentFolder()
} }
@@ -569,10 +549,8 @@ class ViewFolderActivity : AppCompatActivity() {
} }
} }
val message = if (allCopied) "Files copied successfully" else "Some files could not be copied" val message = if (allCopied) getString(R.string.files_copied_successfully) else getString(R.string.some_files_could_not_be_copied)
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode() fileAdapter?.exitSelectionMode()
refreshCurrentFolder() refreshCurrentFolder()
} }
@@ -589,17 +567,15 @@ class ViewFolderActivity : AppCompatActivity() {
} }
} }
val message = if (allMoved) "Files moved successfully" else "Some files could not be moved" val message = if (allMoved) getString(R.string.files_moved_successfully) else getString(R.string.some_files_could_not_be_moved)
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// Fixed: Ensure proper order of operations
fileAdapter?.exitSelectionMode() fileAdapter?.exitSelectionMode()
refreshCurrentFolder() refreshCurrentFolder()
} }
private fun showFolderSelectionDialog(onFolderSelected: (File) -> Unit) { private fun showFolderSelectionDialog(onFolderSelected: (File) -> Unit) {
val folders = folderManager.getFoldersInDirectory(hiddenDir) val folders = folderManager.getFoldersInDirectory(hiddenDir)
.filter { it != currentFolder } // Exclude current folder .filter { it != currentFolder }
if (folders.isEmpty()) { if (folders.isEmpty()) {
Toast.makeText(this, getString(R.string.no_folders_available), Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.no_folders_available), Toast.LENGTH_SHORT).show()

View File

@@ -38,10 +38,8 @@ class FileAdapter(
private val selectedItems = mutableSetOf<Int>() private val selectedItems = mutableSetOf<Int>()
private var isSelectionMode = false private var isSelectionMode = false
// Use WeakReference to prevent memory leaks
private var fileOperationCallback: WeakReference<FileOperationCallback>? = null private var fileOperationCallback: WeakReference<FileOperationCallback>? = null
// Background executor for file operations
private val fileExecutor = Executors.newSingleThreadExecutor() private val fileExecutor = Executors.newSingleThreadExecutor()
private val mainHandler = Handler(Looper.getMainLooper()) private val mainHandler = Handler(Looper.getMainLooper())
@@ -49,7 +47,6 @@ class FileAdapter(
private const val TAG = "FileAdapter" private const val TAG = "FileAdapter"
} }
// Callback interface for handling file operations and selection changes
interface FileOperationCallback { interface FileOperationCallback {
fun onFileDeleted(file: File) fun onFileDeleted(file: File)
fun onFileRenamed(oldFile: File, newFile: File) fun onFileRenamed(oldFile: File, newFile: File)
@@ -75,7 +72,6 @@ class FileAdapter(
setupClickListeners(file, fileType) setupClickListeners(file, fileType)
fileNameTextView.visibility = if (showFileName) View.VISIBLE else View.GONE fileNameTextView.visibility = if (showFileName) View.VISIBLE else View.GONE
// Update selection state
val position = adapterPosition val position = adapterPosition
if (position != RecyclerView.NO_POSITION) { if (position != RecyclerView.NO_POSITION) {
val isSelected = selectedItems.contains(position) val isSelected = selectedItems.contains(position)
@@ -89,7 +85,6 @@ class FileAdapter(
return return
} }
// Handle partial updates based on payload
val changes = payloads.firstOrNull() as? List<String> val changes = payloads.firstOrNull() as? List<String>
changes?.forEach { change -> changes?.forEach { change ->
when (change) { when (change) {
@@ -97,14 +92,13 @@ class FileAdapter(
fileNameTextView.text = file.name fileNameTextView.text = file.name
} }
"SIZE_CHANGED", "MODIFIED_DATE_CHANGED" -> { "SIZE_CHANGED", "MODIFIED_DATE_CHANGED" -> {
// Could update file info if displayed
} }
"SELECTION_CHANGED" -> { "SELECTION_CHANGED" -> {
val position = adapterPosition val position = adapterPosition
if (position != RecyclerView.NO_POSITION) { if (position != RecyclerView.NO_POSITION) {
val isSelected = selectedItems.contains(position) val isSelected = selectedItems.contains(position)
updateSelectionUI(isSelected) updateSelectionUI(isSelected)
// Notify activity about selection change
notifySelectionModeChange() notifySelectionModeChange()
} }
} }
@@ -128,7 +122,6 @@ class FileAdapter(
.centerCrop() .centerCrop()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.error(R.drawable.ic_document) .error(R.drawable.ic_document)
.placeholder(R.drawable.ic_document)
.into(imageView) .into(imageView)
} }
FileManager.FileType.VIDEO -> { FileManager.FileType.VIDEO -> {
@@ -138,12 +131,12 @@ class FileAdapter(
.centerCrop() .centerCrop()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.error(R.drawable.ic_document) .error(R.drawable.ic_document)
.placeholder(R.drawable.ic_document)
.into(imageView) .into(imageView)
} }
FileManager.FileType.AUDIO -> { FileManager.FileType.AUDIO -> {
playIcon.visibility = View.GONE playIcon.visibility = View.GONE
imageView.setImageResource(R.drawable.ic_audio) imageView.setImageResource(R.drawable.ic_audio)
imageView.setPadding(50,50,50,50)
} }
else -> { else -> {
playIcon.visibility = View.GONE playIcon.visibility = View.GONE
@@ -191,16 +184,18 @@ class FileAdapter(
} }
private fun openAudioFile(file: File) { private fun openAudioFile(file: File) {
val fileType = FileManager(context,lifecycleOwner).getFileType(file)
try { try {
val uri = FileProvider.getUriForFile( val fileTypeString = when (fileType) {
context, FileManager.FileType.IMAGE -> context.getString(R.string.image)
"${context.packageName}.fileprovider", FileManager.FileType.VIDEO -> context.getString(R.string.video)
file else -> "unknown"
) }
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "audio/*") val intent = Intent(context, PreviewActivity::class.java).apply {
putExtra("type", fileTypeString)
putExtra("folder", currentFolder.toString()) putExtra("folder", currentFolder.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) putExtra("position", adapterPosition)
} }
context.startActivity(intent) context.startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
@@ -316,7 +311,6 @@ class FileAdapter(
} }
private fun deleteFile(file: File) { private fun deleteFile(file: File) {
// Show confirmation dialog first
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(context)
.setTitle("Delete File") .setTitle("Delete File")
.setMessage("Are you sure you want to delete ${file.name}?") .setMessage("Are you sure you want to delete ${file.name}?")
@@ -436,12 +430,10 @@ class FileAdapter(
} }
private fun copyToAnotherFolder(file: File) { private fun copyToAnotherFolder(file: File) {
// This will be handled by the activity
fileOperationCallback?.get()?.onRefreshNeeded() fileOperationCallback?.get()?.onRefreshNeeded()
} }
private fun moveToAnotherFolder(file: File) { private fun moveToAnotherFolder(file: File) {
// This will be handled by the activity
fileOperationCallback?.get()?.onRefreshNeeded() fileOperationCallback?.get()?.onRefreshNeeded()
} }
@@ -489,7 +481,6 @@ class FileAdapter(
currentList.clear() currentList.clear()
super.submitList(null) super.submitList(null)
} else { } else {
// Create a new list to force update
val newList = list.toMutableList() val newList = list.toMutableList()
super.submitList(newList) super.submitList(newList)
} }
@@ -502,6 +493,7 @@ class FileAdapter(
} }
} }
@SuppressLint("NotifyDataSetChanged")
fun exitSelectionMode() { fun exitSelectionMode() {
if (isSelectionMode) { if (isSelectionMode) {
isSelectionMode = false isSelectionMode = false
@@ -574,8 +566,6 @@ class FileAdapter(
fun deleteSelectedFiles() { fun deleteSelectedFiles() {
val selectedFiles = getSelectedItems() val selectedFiles = getSelectedItems()
if (selectedFiles.isEmpty()) return if (selectedFiles.isEmpty()) return
// Show confirmation dialog
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(context)
.setTitle("Delete Files") .setTitle("Delete Files")
.setMessage("Are you sure you want to delete ${selectedFiles.size} file(s)?") .setMessage("Are you sure you want to delete ${selectedFiles.size} file(s)?")
@@ -611,10 +601,8 @@ class FileAdapter(
} }
mainHandler.post { mainHandler.post {
// Exit selection mode first
exitSelectionMode() exitSelectionMode()
// Show detailed result message
when { when {
deletedCount > 0 && failedCount == 0 -> { deletedCount > 0 && failedCount == 0 -> {
Toast.makeText(context, "Deleted $deletedCount file(s)", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Deleted $deletedCount file(s)", Toast.LENGTH_SHORT).show()
@@ -641,7 +629,6 @@ class FileAdapter(
try { try {
if (selectedFiles.size == 1) { if (selectedFiles.size == 1) {
// Share single file
val file = selectedFiles.first() val file = selectedFiles.first()
val uri = FileProvider.getUriForFile( val uri = FileProvider.getUriForFile(
context, context,
@@ -657,7 +644,6 @@ class FileAdapter(
Intent.createChooser(shareIntent, context.getString(R.string.share_file)) Intent.createChooser(shareIntent, context.getString(R.string.share_file))
) )
} else { } else {
// Share multiple files
val uris = selectedFiles.mapNotNull { file -> val uris = selectedFiles.mapNotNull { file ->
try { try {
FileProvider.getUriForFile( FileProvider.getUriForFile(
@@ -709,7 +695,6 @@ class FileAdapter(
notifyItemChanged(position, listOf("SELECTION_CHANGED")) notifyItemChanged(position, listOf("SELECTION_CHANGED"))
} }
} }
// Ensure callback is notified
notifySelectionModeChange() notifySelectionModeChange()
} }
} }

View File

@@ -6,13 +6,10 @@ import java.io.File
class FileDiffCallback : DiffUtil.ItemCallback<File>() { class FileDiffCallback : DiffUtil.ItemCallback<File>() {
override fun areItemsTheSame(oldItem: File, newItem: File): Boolean { override fun areItemsTheSame(oldItem: File, newItem: File): Boolean {
// Compare by absolute path since File objects might be different instances
// but represent the same file
return oldItem.absolutePath == newItem.absolutePath return oldItem.absolutePath == newItem.absolutePath
} }
override fun areContentsTheSame(oldItem: File, newItem: File): Boolean { override fun areContentsTheSame(oldItem: File, newItem: File): Boolean {
// Compare all relevant properties that might change and affect the UI
return oldItem.name == newItem.name && return oldItem.name == newItem.name &&
oldItem.length() == newItem.length() && oldItem.length() == newItem.length() &&
oldItem.lastModified() == newItem.lastModified() && oldItem.lastModified() == newItem.lastModified() &&
@@ -22,8 +19,6 @@ class FileDiffCallback : DiffUtil.ItemCallback<File>() {
} }
override fun getChangePayload(oldItem: File, newItem: File): Any? { override fun getChangePayload(oldItem: File, newItem: File): Any? {
// Return a payload if only specific properties changed
// This allows for partial updates instead of full rebinding
val changes = mutableListOf<String>() val changes = mutableListOf<String>()
if (oldItem.name != newItem.name) { if (oldItem.name != newItem.name) {
@@ -42,6 +37,6 @@ class FileDiffCallback : DiffUtil.ItemCallback<File>() {
changes.add("EXISTENCE_CHANGED") changes.add("EXISTENCE_CHANGED")
} }
return if (changes.isNotEmpty()) changes else null return changes.ifEmpty { null }
} }
} }

View File

@@ -1,5 +1,6 @@
package devs.org.calculator.adapters package devs.org.calculator.adapters
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -80,6 +81,7 @@ class FolderAdapter(
} }
} }
@SuppressLint("NotifyDataSetChanged")
fun clearSelection() { fun clearSelection() {
val wasInSelectionMode = isSelectionMode val wasInSelectionMode = isSelectionMode
selectedItems.clear() selectedItems.clear()

View File

@@ -9,7 +9,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.MediaController import android.widget.MediaController
import android.widget.SeekBar
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -25,11 +24,8 @@ class ImagePreviewAdapter(
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() { ) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
private val differ = AsyncListDiffer(this, FileDiffCallback()) private val differ = AsyncListDiffer(this, FileDiffCallback())
var currentMediaPlayer: MediaPlayer? = null
var isMediaPlayerPrepared = false
var currentViewHolder: ImageViewHolder? = null
private var currentPlayingPosition = -1 private var currentPlayingPosition = -1
private var isPlaying = false private var currentViewHolder: ImageViewHolder? = null
var images: List<File> var images: List<File>
get() = differ.currentList get() = differ.currentList
@@ -43,32 +39,35 @@ class ImagePreviewAdapter(
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) { override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
val imageUrl = images[position] val imageUrl = images[position]
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position]) val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
holder.bind(imageUrl,fileType)
currentViewHolder = holder
currentMediaPlayer?.let { stopAndResetCurrentAudio()
if (it.isPlaying) it.pause()
it.seekTo(0)
}
currentMediaPlayer = null
isMediaPlayerPrepared = false
if (currentMediaPlayer?.isPlaying == true) { holder.bind(imageUrl, fileType, position)
currentMediaPlayer?.stop()
currentMediaPlayer?.release()
}
currentMediaPlayer = null
} }
override fun getItemCount(): Int = images.size override fun getItemCount(): Int = images.size
private fun stopAndResetCurrentAudio() {
currentViewHolder?.stopAndResetAudio()
currentPlayingPosition = -1
currentViewHolder = null
}
inner class ImageViewHolder(private val binding: ViewpagerItemsBinding) : RecyclerView.ViewHolder(binding.root) { inner class ImageViewHolder(private val binding: ViewpagerItemsBinding) : RecyclerView.ViewHolder(binding.root) {
private var mediaPlayer: MediaPlayer? = null
private var seekHandler = Handler(Looper.getMainLooper()) private var seekHandler = Handler(Looper.getMainLooper())
private var seekRunnable: Runnable? = null private var seekRunnable: Runnable? = null
private var mediaPlayer: MediaPlayer? = null
private var isMediaPlayerPrepared = false
private var isPlaying = false
private var currentPosition = 0
fun bind(file: File, fileType: FileManager.FileType, position: Int) {
currentPosition = position
releaseMediaPlayer()
resetAudioUI()
fun bind(file: File, fileType: FileManager.FileType) {
when (fileType) { when (fileType) {
FileManager.FileType.VIDEO -> { FileManager.FileType.VIDEO -> {
binding.imageView.visibility = View.GONE binding.imageView.visibility = View.GONE
@@ -114,7 +113,6 @@ class ImagePreviewAdapter(
binding.audioTitle.text = file.name binding.audioTitle.text = file.name
setupAudioPlayer(file) setupAudioPlayer(file)
setupSeekBar()
setupPlaybackControls() setupPlaybackControls()
} }
else -> { else -> {
@@ -125,27 +123,40 @@ class ImagePreviewAdapter(
} }
} }
private fun resetAudioUI() {
binding.playPause.setImageResource(R.drawable.play)
binding.audioSeekBar.value = 0f
binding.audioSeekBar.valueTo = 100f // Default value
seekRunnable?.let { seekHandler.removeCallbacks(it) }
}
private fun setupAudioPlayer(file: File) { private fun setupAudioPlayer(file: File) {
try {
mediaPlayer = MediaPlayer().apply { mediaPlayer = MediaPlayer().apply {
setDataSource(file.absolutePath) setDataSource(file.absolutePath)
setOnPreparedListener { mp -> setOnPreparedListener { mp ->
binding.audioSeekBar.valueTo = mp.duration.toFloat() binding.audioSeekBar.valueTo = mp.duration.toFloat()
binding.audioSeekBar.value = 0f
setupSeekBar()
isMediaPlayerPrepared = true isMediaPlayerPrepared = true
} }
setOnCompletionListener { setOnCompletionListener {
// isPlaying = false stopAndResetAudio()
binding.playPause.setImageResource(R.drawable.play) }
binding.audioSeekBar.value = 0f setOnErrorListener { _, _, _ ->
releaseMediaPlayer()
seekHandler.removeCallbacks(seekRunnable!!) true
} }
prepareAsync() prepareAsync()
} }
} catch (e: Exception) {
e.printStackTrace()
releaseMediaPlayer()
}
} }
private fun setupSeekBar() { private fun setupSeekBar() {
binding.audioSeekBar.addOnChangeListener { slider, value, fromUser -> binding.audioSeekBar.addOnChangeListener { _, value, fromUser ->
if (fromUser && mediaPlayer != null && isMediaPlayerPrepared) { if (fromUser && mediaPlayer != null && isMediaPlayerPrepared) {
mediaPlayer?.seekTo(value.toInt()) mediaPlayer?.seekTo(value.toInt())
} }
@@ -153,14 +164,17 @@ class ImagePreviewAdapter(
seekRunnable = Runnable { seekRunnable = Runnable {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
if (mp.isPlaying) { if (mp.isPlaying && isMediaPlayerPrepared) {
try {
binding.audioSeekBar.value = mp.currentPosition.toFloat() binding.audioSeekBar.value = mp.currentPosition.toFloat()
seekHandler.postDelayed(seekRunnable!!, 100) seekHandler.postDelayed(seekRunnable!!, 100)
} catch (e: Exception) {
e.printStackTrace()
}
} }
} }
} }
} }
private fun setupPlaybackControls() { private fun setupPlaybackControls() {
binding.playPause.setOnClickListener { binding.playPause.setOnClickListener {
@@ -173,59 +187,120 @@ class ImagePreviewAdapter(
binding.preview.setOnClickListener { binding.preview.setOnClickListener {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
if (isMediaPlayerPrepared) {
try {
val newPosition = mp.currentPosition - 10000 val newPosition = mp.currentPosition - 10000
mp.seekTo(maxOf(0, newPosition)) mp.seekTo(maxOf(0, newPosition))
binding.audioSeekBar.value = mp.currentPosition.toFloat() binding.audioSeekBar.value = mp.currentPosition.toFloat()
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
} }
binding.next.setOnClickListener { binding.next.setOnClickListener {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
if (isMediaPlayerPrepared) {
try {
val newPosition = mp.currentPosition + 10000 val newPosition = mp.currentPosition + 10000
mp.seekTo(minOf(mp.duration, newPosition)) mp.seekTo(minOf(mp.duration, newPosition))
binding.audioSeekBar.value = mp.currentPosition.toFloat() binding.audioSeekBar.value = mp.currentPosition.toFloat()
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
} }
} }
private fun playAudio() { private fun playAudio() {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
if (currentPlayingPosition != -1 && currentPlayingPosition != adapterPosition) { if (isMediaPlayerPrepared) {
currentViewHolder?.pauseAudio() try {
if (currentPlayingPosition != currentPosition) {
stopAndResetCurrentAudio()
} }
mp.start() mp.start()
isPlaying = true isPlaying = true
binding.playPause.setImageResource(R.drawable.pause) binding.playPause.setImageResource(R.drawable.pause)
seekHandler.post(seekRunnable!!) seekRunnable?.let { seekHandler.post(it) }
currentPlayingPosition = adapterPosition
currentViewHolder = this currentPlayingPosition = currentPosition
currentViewHolder = this@ImageViewHolder
} catch (e: Exception) {
e.printStackTrace()
releaseMediaPlayer()
}
}
} }
} }
private fun pauseAudio() { private fun pauseAudio() {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
try {
if (mp.isPlaying) { if (mp.isPlaying) {
mp.pause() mp.pause()
isPlaying = false isPlaying = false
binding.playPause.setImageResource(R.drawable.play) binding.playPause.setImageResource(R.drawable.play)
seekHandler.removeCallbacks(seekRunnable!!) seekRunnable?.let { seekHandler.removeCallbacks(it) }
}
} catch (e: Exception) {
e.printStackTrace()
releaseMediaPlayer()
} }
} }
} }
fun stopAndResetAudio() {
try {
mediaPlayer?.let { mp ->
if (mp.isPlaying) {
mp.stop()
mp.prepare()
} else if (isMediaPlayerPrepared) {
mp.seekTo(0)
}
}
isPlaying = false
resetAudioUI()
if (currentPlayingPosition == currentPosition) {
currentPlayingPosition = -1
currentViewHolder = null
}
} catch (e: Exception) {
e.printStackTrace()
releaseMediaPlayer()
}
}
fun releaseMediaPlayer() { fun releaseMediaPlayer() {
try {
mediaPlayer?.let { mp -> mediaPlayer?.let { mp ->
if (mp.isPlaying) { if (mp.isPlaying) {
mp.stop() mp.stop()
} }
mp.release() mp.release()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
mediaPlayer = null mediaPlayer = null
isPlaying = false isPlaying = false
seekHandler.removeCallbacks(seekRunnable!!) isMediaPlayerPrepared = false
seekRunnable?.let { seekHandler.removeCallbacks(it) }
if (currentPlayingPosition == currentPosition) {
currentPlayingPosition = -1
currentViewHolder = null
}
} }
} }
private fun playVideoAtPosition(position: Int) { private fun playVideoAtPosition(position: Int) {
if (position < images.size) {
val nextFile = images[position] val nextFile = images[position]
val fileType = FileManager(context, lifecycleOwner).getFileType(images[position]) val fileType = FileManager(context, lifecycleOwner).getFileType(images[position])
if (fileType == FileManager.FileType.VIDEO) { if (fileType == FileManager.FileType.VIDEO) {
@@ -235,10 +310,20 @@ class ImagePreviewAdapter(
} }
} }
} }
}
override fun onViewRecycled(holder: ImageViewHolder) { override fun onViewRecycled(holder: ImageViewHolder) {
super.onViewRecycled(holder) super.onViewRecycled(holder)
holder.releaseMediaPlayer() holder.releaseMediaPlayer()
} }
}
fun onItemScrolledAway(position: Int) {
if (currentPlayingPosition == position) {
stopAndResetCurrentAudio()
}
}
fun releaseAllResources() {
stopAndResetCurrentAudio()
}
}

View File

@@ -1,9 +1,9 @@
package devs.org.calculator.adapters package devs.org.calculator.adapters
import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
@@ -22,9 +22,9 @@ class ListFolderAdapter(
private var isSelectionMode = false private var isSelectionMode = false
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val folderNameTextView: TextView = itemView.findViewById(R.id.folderName) private val folderNameTextView: TextView = itemView.findViewById(R.id.folderName)
val selectedLayer: View = itemView.findViewById(R.id.selectedLayer) private val selectedLayer: View = itemView.findViewById(R.id.selectedLayer)
fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) { fun bind(folder: File, onFolderClick: (File) -> Unit, onFolderLongClick: (File) -> Unit, isSelected: Boolean) {
folderNameTextView.text = folder.name folderNameTextView.text = folder.name
@@ -89,6 +89,7 @@ class ListFolderAdapter(
} }
} }
@SuppressLint("NotifyDataSetChanged")
fun clearSelection() { fun clearSelection() {
val wasInSelectionMode = isSelectionMode val wasInSelectionMode = isSelectionMode
selectedItems.clear() selectedItems.clear()

View File

@@ -1,19 +1,14 @@
package devs.org.calculator.utils package devs.org.calculator.utils
import android.content.Context
import android.os.Environment
import java.io.File import java.io.File
class FolderManager(private val context: Context) { class FolderManager {
companion object {
const val HIDDEN_DIR = ".CalculatorHide"
}
fun createFolder(parentDir: File, folderName: String): Boolean { fun createFolder(parentDir: File, folderName: String): Boolean {
val newFolder = File(parentDir, folderName) val newFolder = File(parentDir, folderName)
return if (!newFolder.exists()) { return if (!newFolder.exists()) {
newFolder.mkdirs() newFolder.mkdirs()
// Create .nomedia file to hide from media scanners
File(newFolder, ".nomedia").createNewFile() File(newFolder, ".nomedia").createNewFile()
true true
} else { } else {
@@ -54,20 +49,4 @@ class FolderManager(private val context: Context) {
emptyList() emptyList()
} }
} }
fun moveFileToFolder(file: File, targetFolder: File): Boolean {
return try {
if (!targetFolder.exists()) {
targetFolder.mkdirs()
File(targetFolder, ".nomedia").createNewFile()
}
val newFile = File(targetFolder, file.name)
file.copyTo(newFile, overwrite = true)
file.delete()
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
} }

View File

@@ -3,6 +3,7 @@ package devs.org.calculator.utils
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import java.security.MessageDigest import java.security.MessageDigest
import androidx.core.content.edit
class PrefsUtil(context: Context) { class PrefsUtil(context: Context) {
private val prefs: SharedPreferences = context.getSharedPreferences("Calculator", Context.MODE_PRIVATE) private val prefs: SharedPreferences = context.getSharedPreferences("Calculator", Context.MODE_PRIVATE)
@@ -13,26 +14,33 @@ class PrefsUtil(context: Context) {
fun savePassword(password: String) { fun savePassword(password: String) {
val hashedPassword = hashPassword(password) val hashedPassword = hashPassword(password)
prefs.edit() prefs.edit {
.putString("password", hashedPassword) putString("password", hashedPassword)
.apply() }
} }
fun setBoolean(key:String, value: Boolean){ fun setBoolean(key:String, value: Boolean){
return prefs.edit().putBoolean(key,value).apply() return prefs.edit { putBoolean(key, value) }
}
fun setInt(key:String, value: Int){
return prefs.edit { putInt(key, value) }
} }
fun getBoolean(key: String, defValue: Boolean = false): Boolean{ fun getBoolean(key: String, defValue: Boolean = false): Boolean{
return prefs.getBoolean(key,defValue) return prefs.getBoolean(key,defValue)
} }
fun getInt(key: String, defValue: Int): Int{
return prefs.getInt(key,defValue)
}
fun resetPassword(){ fun resetPassword(){
prefs.edit() prefs.edit {
.remove("password") remove("password")
.remove("security_question") .remove("security_question")
.remove("security_answer") .remove("security_answer")
.apply() }
} }
fun validatePassword(input: String): Boolean { fun validatePassword(input: String): Boolean {
@@ -41,10 +49,10 @@ class PrefsUtil(context: Context) {
} }
fun saveSecurityQA(question: String, answer: String) { fun saveSecurityQA(question: String, answer: String) {
prefs.edit() prefs.edit {
.putString("security_question", question) putString("security_question", question)
.putString("security_answer", hashPassword(answer)) .putString("security_answer", hashPassword(answer))
.apply() }
} }
fun validateSecurityAnswer(answer: String): Boolean { fun validateSecurityAnswer(answer: String): Boolean {

View File

@@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker
import androidx.core.net.toUri
class StoragePermissionUtil(private val activity: AppCompatActivity) { class StoragePermissionUtil(private val activity: AppCompatActivity) {
@@ -34,7 +35,7 @@ class StoragePermissionUtil(private val activity: AppCompatActivity) {
onGranted() onGranted()
} else { } else {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
data = Uri.parse("package:${activity.packageName}") data = "package:${activity.packageName}".toUri()
} }
activity.startActivity(intent) activity.startActivity(intent)
} }

View File

@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp" android:width="50dp"
android:height="800dp" android:height="50dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path

View File

@@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:bottomLeftRadius="15dp" android:bottomRightRadius="15dp"/> <corners android:bottomLeftRadius="15dp" android:bottomRightRadius="15dp"/>
<solid android:color="?attr/cardForegroundColor"/> <solid android:color="?attr/colorSecondaryContainer"/>
</shape> </shape>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="50dp"
android:height="24dp" android:height="50dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="@color/textColor" android:fillColor="#FF000000"
android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6H6zM13,9V3.5L18.5,9H13z"/> android:pathData="M13.172,2H6C4.9,2 4,2.9 4,4v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V8.828c0,-0.53 -0.211,-1.039 -0.586,-1.414l-4.828,-4.828C14.211,2.211 13.702,2 13.172,2zM15,18H9c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0C16,17.552 15.552,18 15,18zM15,14H9c-0.552,0 -1,-0.448 -1,-1v0c0,-0.552 0.448,-1 1,-1h6c0.552,0 1,0.448 1,1v0C16,13.552 15.552,14 15,14zM13,9V3.5L18.5,9H13z"/>
</vector> </vector>

View File

@@ -39,57 +39,38 @@
android:layout_weight="1" android:layout_weight="1"
android:id="@+id/folderName"/> android:id="@+id/folderName"/>
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:src="@drawable/ic_edit" app:icon="@drawable/ic_edit"
app:tint="?attr/colorPrimary" style="@style/Widget.Material3.Button.IconButton"
android:scaleType="fitCenter"
android:padding="9dp"
android:visibility="gone"
android:background="#00000000"
android:id="@+id/edit"/> android:id="@+id/edit"/>
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:id="@+id/delete" android:id="@+id/delete"
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:background="#00000000" app:icon="@drawable/ic_delete"
android:padding="9dp" style="@style/Widget.Material3.Button.IconButton" />
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
app:tint="?attr/colorPrimary"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:id="@+id/menuButton" android:id="@+id/menuButton"
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:src="@drawable/ic_more" app:icon="@drawable/ic_more"
app:tint="?attr/colorPrimary" style="@style/Widget.Material3.Button.IconButton"/>
android:scaleType="fitCenter"
android:padding="9dp"
android:visibility="gone"
android:background="#00000000"/>
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:src="@drawable/ic_list" app:icon="@drawable/ic_list"
android:scaleType="fitCenter" style="@style/Widget.Material3.Button.IconButton"
app:tint="?attr/colorPrimary"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/folderOrientation"/> android:id="@+id/folderOrientation"/>
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:src="@drawable/ic_settings" app:icon="@drawable/ic_settings"
android:scaleType="fitCenter" style="@style/Widget.Material3.Button.IconButton"
app:tint="?attr/colorPrimary"
android:background="#00000000"
android:padding="8dp"
android:id="@+id/settings"/> android:id="@+id/settings"/>
</LinearLayout> </LinearLayout>
@@ -128,7 +109,7 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:gravity="center" android:gravity="center"
android:padding="10dp" android:padding="10dp"
android:text="@string/no_items_available_add_one_by_clicking_on_the_plus_button" android:text="@string/there_is_no_folders_available_create_one_by_clicking_on_the_add_folder_button_showing_in_the_bottom"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@@ -14,7 +14,7 @@
android:layout_margin="0dp" android:layout_margin="0dp"
android:background="@drawable/bottom_corner" android:background="@drawable/bottom_corner"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.3" app:layout_constraintHeight_percent="0.4"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@@ -23,6 +23,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:scrollbars="none" android:scrollbars="none"
android:paddingTop="27dp"
android:gravity="end|bottom" android:gravity="end|bottom"
app:layout_constraintBottom_toTopOf="@+id/total" app:layout_constraintBottom_toTopOf="@+id/total"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -59,7 +60,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoSizeMaxTextSize="40sp" android:autoSizeMaxTextSize="40sp"
android:autoSizeMinTextSize="24sp" android:autoSizeMinTextSize="12sp"
android:autoSizeStepGranularity="2sp" android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform" android:autoSizeTextType="uniform"
android:gravity="end|bottom" android:gravity="end|bottom"
@@ -85,7 +86,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/displayContainer"> app:layout_constraintTop_toBottomOf="@id/displayContainer">
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
@@ -95,49 +96,68 @@
android:id="@+id/btnClear" android:id="@+id/btnClear"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_marginHorizontal="3dp"
android:layout_weight="1" android:backgroundTint="?attr/colorSecondaryContainer"
android:text="C" android:text="C"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnPercent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnPercent" android:id="@+id/btnPercent"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:backgroundTint="?attr/colorSecondaryContainer"
android:layout_weight="1"
android:text="%" android:text="%"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> android:layout_marginHorizontal="3dp"
app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnDivide"
app:layout_constraintStart_toEndOf="@+id/btnClear"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnDivide" android:id="@+id/btnDivide"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:backgroundTint="?attr/colorSecondaryContainer"
android:layout_weight="1"
android:text="÷" android:text="÷"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> android:layout_marginHorizontal="3dp"
app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/cut"
app:layout_constraintStart_toEndOf="@+id/btnPercent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton <ImageButton
android:id="@+id/cut" android:id="@+id/cut"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:layout_marginVertical="8dp"
android:background="@drawable/gradient_bg" android:background="@drawable/gradient_bg"
android:scaleType="centerInside"
android:src="@drawable/backspace" android:src="@drawable/backspace"
android:scaleType="centerInside"/> android:layout_marginHorizontal="3dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnDivide"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
@@ -147,10 +167,16 @@
android:id="@+id/btn7" android:id="@+id/btn7"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="7" android:text="7"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn8"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp" />
@@ -158,21 +184,32 @@
android:id="@+id/btn8" android:id="@+id/btn8"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="8" android:text="8"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn9"
app:layout_constraintStart_toEndOf="@+id/btn7"
app:layout_constraintTop_toTopOf="parent"
/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn9" android:id="@+id/btn9"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="9" android:text="9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnMultiply"
app:layout_constraintStart_toEndOf="@+id/btn8"
app:layout_constraintTop_toTopOf="parent"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp" />
@@ -180,18 +217,22 @@
android:id="@+id/btnMultiply" android:id="@+id/btnMultiply"
style="@style/CustomMaterialButton" style="@style/CustomMaterialButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1" android:layout_weight="1"
android:textColor="@color/white" android:layout_marginHorizontal="3dp"
android:text="×" android:text="×"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn9"
app:layout_constraintTop_toTopOf="parent"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
@@ -201,51 +242,71 @@
android:id="@+id/btn4" android:id="@+id/btn4"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_marginHorizontal="3dp"
android:layout_weight="1" android:layout_weight="1"
android:text="4" android:text="4"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn5"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn5" android:id="@+id/btn5"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_marginHorizontal="3dp"
android:layout_weight="1" android:layout_weight="1"
android:text="5" android:text="5"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn6"
app:layout_constraintStart_toEndOf="@+id/btn4"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn6" android:id="@+id/btn6"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_marginHorizontal="3dp"
android:layout_weight="1" android:layout_weight="1"
android:text="6" android:text="6"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnMinus"
app:layout_constraintStart_toEndOf="@+id/btn5"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnMinus" android:id="@+id/btnMinus"
style="@style/CustomMaterialButton" style="@style/CustomMaterialButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginHorizontal="4dp" android:layout_marginHorizontal="3dp"
android:layout_marginVertical="8dp"
android:layout_weight="1" android:layout_weight="1"
android:textColor="@color/white" android:textColor="@color/white"
android:text="-" android:text="-"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn6"
app:layout_constraintTop_toTopOf="parent"/>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
@@ -255,92 +316,144 @@
android:id="@+id/btn1" android:id="@+id/btn1"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="1" android:text="1"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn2"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn2" android:id="@+id/btn2"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="2" android:text="2"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn3"
app:layout_constraintStart_toEndOf="@+id/btn1"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn3" android:id="@+id/btn3"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginHorizontal="3dp"
android:text="3" android:text="3"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnPlus"
app:layout_constraintStart_toEndOf="@+id/btn2"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnPlus" android:id="@+id/btnPlus"
style="@style/CustomMaterialButton" style="@style/CustomMaterialButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1" android:layout_weight="1"
android:textColor="@color/white" android:textColor="@color/white"
android:layout_marginHorizontal="3dp"
android:text="+" android:text="+"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn3"
app:layout_constraintTop_toTopOf="parent"
/>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn00"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginHorizontal="3dp"
android:text="00"
android:layout_weight="1"
android:textSize="30sp"
app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btn0"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn0" android:id="@+id/btn0"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_weight="1"
android:layout_weight="2" android:layout_marginHorizontal="3dp"
android:text="0" android:text="0"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnDot"
app:layout_constraintStart_toEndOf="@+id/btn00"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnDot" android:id="@+id/btnDot"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_margin="4dp" android:layout_marginHorizontal="3dp"
android:layout_weight="1"
android:text="." android:text="."
android:layout_weight="1"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1.15"
app:layout_constraintEnd_toStartOf="@+id/btnEquals"
app:layout_constraintStart_toEndOf="@+id/btn0"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btnEquals" android:id="@+id/btnEquals"
style="@style/CustomMaterialButton" style="@style/CustomMaterialButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="8dp"
android:layout_weight="1" android:layout_weight="1"
android:textColor="@color/white" android:layout_marginHorizontal="3dp"
android:text="=" android:text="="
android:textColor="@color/white"
android:textSize="30sp" android:textSize="30sp"
app:cornerRadius="15dp" /> app:cornerRadius="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btnDot"
app:layout_constraintTop_toTopOf="parent"/>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@@ -81,7 +81,9 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="10dp" app:cardCornerRadius="10dp"
app:cardElevation="5dp"> android:background="#00000000"
android:backgroundTint="#00000000"
app:cardElevation="0dp">
<ImageView <ImageView
android:id="@+id/appLogo" android:id="@+id/appLogo"
android:layout_width="48dp" android:layout_width="48dp"

View File

@@ -40,16 +40,12 @@
android:id="@+id/folderName"/> android:id="@+id/folderName"/>
<androidx.appcompat.widget.AppCompatImageButton <com.google.android.material.button.MaterialButton
android:id="@+id/menuButton" android:id="@+id/menuButton"
android:layout_width="40dp" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="wrap_content"
android:src="@drawable/ic_more" app:icon="@drawable/ic_more"
app:tint="?attr/colorPrimary" style="@style/Widget.Material3.Button.IconButton"/>
android:scaleType="fitCenter"
android:padding="7dp"
android:visibility="gone"
android:background="#00000000"/>
</LinearLayout> </LinearLayout>
@@ -79,7 +75,7 @@
<ImageView <ImageView
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:src="@drawable/ic_no_items" /> android:src="@drawable/ic_file" />
<TextView <TextView
android:id="@+id/noItemsTxt" android:id="@+id/noItemsTxt"

View File

@@ -10,8 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="4dp" android:layout_margin="4dp"
app:cardCornerRadius="8dp" app:cardCornerRadius="8dp">
app:cardElevation="2dp">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -1,12 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="12dp" android:layout_margin="3dp"
android:gravity="center_vertical"> android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView <ImageView
android:id="@+id/folderIcon" android:id="@+id/folderIcon"
android:layout_width="34dp" android:layout_width="34dp"
@@ -20,5 +26,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:textSize="18sp"/> android:textSize="18sp"/>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -15,13 +15,12 @@
<androidx.cardview.widget.CardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cardView" android:id="@+id/cardView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:cardCornerRadius="10dp" app:cardCornerRadius="10dp"
android:layout_margin="5dp" android:layout_margin="5dp"
app:cardElevation="3dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -49,7 +48,7 @@
</androidx.cardview.widget.CardView> </com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<ImageView <ImageView

View File

@@ -42,7 +42,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
app:cardCornerRadius="15dp" app:cardCornerRadius="15dp"
app:cardElevation="10dp"
android:layout_margin="20dp"> android:layout_margin="20dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -11,6 +11,7 @@
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">false</item> <item name="android:windowLightStatusBar">false</item>
<item name="colorSecondaryContainer">#481A4324</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item> <item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
</style> </style>

View File

@@ -64,7 +64,7 @@
<string name="unknown_file">Unknown File</string> <string name="unknown_file">Unknown File</string>
<string name="details"> DETAILS</string> <string name="details"> DETAILS</string>
<string name="audio_hidded_successfully">Audios hidden successfully</string> <string name="audio_hidded_successfully">Audios hidden successfully</string>
<string name="no_items_available_add_one_by_clicking_on_the_plus_button">No Items Available, Add one by clicking on the</string> <string name="no_items_available_add_one_by_clicking_on_the_plus_button">No Files Available, Add one by clicking on the \'+\' button</string>
<string name="now_enter_button">Now Enter \'=\' button</string> <string name="now_enter_button">Now Enter \'=\' button</string>
<string name="enter_123456">Enter 123456</string> <string name="enter_123456">Enter 123456</string>
<string name="create_folder">Create Folder</string> <string name="create_folder">Create Folder</string>
@@ -126,4 +126,26 @@
<string name="security_question_for_password_reset">Security Question (For Password Reset)</string> <string name="security_question_for_password_reset">Security Question (For Password Reset)</string>
<string name="security_answer">Security Answer</string> <string name="security_answer">Security Answer</string>
<string name="save_password">Save Password</string> <string name="save_password">Save Password</string>
<string name="there_is_no_folders_available_create_one_by_clicking_on_the_add_folder_button_showing_in_the_bottom">There is no Folders Available, create one by clicking on the \'Add Folder\' Button showing in the bottom.</string>
<string name="storage_permission">Storage Permission</string>
<string name="to_ensure_the_app_works_properly_and_allows_you_to_easily_hide_or_un_hide_your_private_files_please_grant_storage_access_permission">To ensure the app works properly and allows you to easily hide or un-hide your private files, please grant storage access permission.\n</string>
<string name="for_devices_running_android_11_or_higher_you_ll_need_to_grant_the_all_files_access_permission">For devices running Android 11 or higher, you\'ll need to grant the \'All Files Access\' permission.</string>
<string name="grant_permission">Grant Permission</string>
<string name="later">Later</string>
<string name="storage_permission_is_required_for_the_app_to_function_properly">Storage permission is required for the app to function properly</string>
<string name="you_can_grant_permission_later_from_settings">You can grant permission later from Settings</string>
<string name="permission_granted">Permission granted</string>
<string name="permission_denied">Permission denied</string>
<string name="github_profile">https://github.com/binondi</string>
<string name="calculator_hide_files">%1$s/calculator-hide-files</string>
<string name="would_you_like_to_set_a_specific_theme_mode">Would you like to set a specific theme mode?</string>
<string name="no">No</string>
<string name="could_not_open_url">Could not open URL</string>
<string name="files_copied_successfully">Files copied successfully</string>
<string name="some_files_could_not_be_copied">Some files could not be copied</string>
<string name="files_moved_successfully">Files moved successfully</string>
<string name="some_files_could_not_be_moved">Some files could not be moved</string>
<string name="ok">OK</string>
<string name="if_you_turn_on_off_this_option_dynamic_theme_changes_will_be_visible_after_you_reopen_the_app">If you turn on/off this option, dynamic theme changes will be visible after you reopen the app.</string>
<string name="attention">Attention!</string>
</resources> </resources>

View File

@@ -12,6 +12,7 @@
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item> <item name="android:navigationBarColor">@android:color/transparent</item>
<item name="colorControlNormal">#483CFF61</item> <item name="colorControlNormal">#483CFF61</item>
<item name="colorSecondaryContainer">#48BDFFCB</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item> <item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>