✨
This commit is contained in:
@@ -6,13 +6,17 @@ import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.FileProcessCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class AudioGalleryActivity : BaseGalleryActivity() {
|
||||
class AudioGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.AUDIO
|
||||
private lateinit var pickAudioLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedUri: Uri? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -20,25 +24,35 @@ class AudioGalleryActivity : BaseGalleryActivity() {
|
||||
|
||||
pickAudioLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
if (uri != null) {
|
||||
selectedUri = uri
|
||||
try {
|
||||
val file = fileManager.copyFileToHiddenDir(selectedUri!!, fileType)
|
||||
if (file != null && file.exists()) {
|
||||
Toast.makeText(this, "File hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@AudioGalleryActivity, this@AudioGalleryActivity).processMultipleFiles(uriList, fileType,this@AudioGalleryActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@AudioGalleryActivity, "${copiedFiles.size} Audios hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to hide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No audio selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@AudioGalleryActivity, "Failed to hide Audios", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
@@ -46,6 +60,7 @@ class AudioGalleryActivity : BaseGalleryActivity() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "audio/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
@@ -57,4 +72,6 @@ class AudioGalleryActivity : BaseGalleryActivity() {
|
||||
override fun openPreview() {
|
||||
// Implement audio preview
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -22,21 +22,33 @@ abstract class BaseGalleryActivity : AppCompatActivity() {
|
||||
protected lateinit var binding: ActivityGalleryBinding
|
||||
protected lateinit var fileManager: FileManager
|
||||
protected lateinit var adapter: FileAdapter
|
||||
protected lateinit var files: List<File>
|
||||
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
private val storagePermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
val granted = permissions.values.all { it }
|
||||
if (granted || Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) {
|
||||
loadFiles()
|
||||
} else {
|
||||
// Handle permission denial case
|
||||
showPermissionDeniedDialog()
|
||||
}
|
||||
}
|
||||
|
||||
abstract val fileType: FileManager.FileType
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupIntentSenderLauncher()
|
||||
checkPermissions()
|
||||
binding = ActivityGalleryBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
fileManager = FileManager(this, this)
|
||||
|
||||
setupRecyclerView()
|
||||
loadFiles()
|
||||
checkPermissionsAndLoadFiles()
|
||||
}
|
||||
|
||||
private fun setupIntentSenderLauncher() {
|
||||
@@ -51,52 +63,55 @@ abstract class BaseGalleryActivity : AppCompatActivity() {
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
|
||||
adapter = FileAdapter(
|
||||
fileType,
|
||||
this, this
|
||||
)
|
||||
adapter = FileAdapter(fileType, this, this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
protected fun loadFiles() {
|
||||
val files = fileManager.getFilesInHiddenDir(fileType)
|
||||
private fun checkPermissionsAndLoadFiles() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
.addCategory("android.intent.category.DEFAULT")
|
||||
.setData(Uri.parse("package:${applicationContext.packageName}"))
|
||||
startActivityForResult(intent, 2296)
|
||||
} else {
|
||||
loadFiles()
|
||||
}
|
||||
} else {
|
||||
val permissions = arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
if (permissions.any { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED }) {
|
||||
storagePermissionLauncher.launch(permissions)
|
||||
} else {
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun loadFiles() {
|
||||
files = fileManager.getFilesInHiddenDir(fileType)
|
||||
adapter.submitList(files)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
abstract fun openPreview()
|
||||
|
||||
private fun checkPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
intent.addCategory("android.intent.category.DEFAULT")
|
||||
intent.data = Uri.parse("package:${applicationContext.packageName}")
|
||||
startActivityForResult(intent, 2296)
|
||||
}
|
||||
} else {
|
||||
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
|
||||
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
1001
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun showPermissionDeniedDialog() {
|
||||
// Show a dialog or a message informing the user about the importance of permissions
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == 2296) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (requestCode == 2296 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (Environment.isExternalStorageManager()) {
|
||||
// Permission granted
|
||||
loadFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,39 +6,56 @@ import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.activities.AudioGalleryActivity
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.FileProcessCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class DocumentsActivity : BaseGalleryActivity() {
|
||||
class DocumentsActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.DOCUMENT
|
||||
private lateinit var pickDocumentLauncher: ActivityResultLauncher<Intent>
|
||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedUri: Uri? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupFabButton()
|
||||
|
||||
pickDocumentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
if (uri != null) {
|
||||
selectedUri = uri
|
||||
try {
|
||||
val file = fileManager.copyFileToHiddenDir(selectedUri!!, fileType)
|
||||
if (file != null && file.exists()) {
|
||||
Toast.makeText(this, "File hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@DocumentsActivity, this@DocumentsActivity).processMultipleFiles(uriList, fileType,this@DocumentsActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@DocumentsActivity, "${copiedFiles.size} Documents hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to hide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No document selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@DocumentsActivity, "Failed to hide Documents", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
@@ -46,11 +63,12 @@ class DocumentsActivity : BaseGalleryActivity() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "*/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickDocumentLauncher.launch(intent)
|
||||
pickLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
import android.app.RecoverableSecurityException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import android.Manifest
|
||||
import devs.org.calculator.activities.AudioGalleryActivity
|
||||
import devs.org.calculator.utils.FileProcessCallback
|
||||
|
||||
class ImageGalleryActivity : BaseGalleryActivity() {
|
||||
class ImageGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.IMAGE
|
||||
private val STORAGE_PERMISSION_CODE = 100
|
||||
|
||||
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
|
||||
private var selectedImageUri: Uri? = null
|
||||
private lateinit var pickImageLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -32,36 +37,48 @@ class ImageGalleryActivity : BaseGalleryActivity() {
|
||||
|
||||
intentSenderLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()){
|
||||
if (it.resultCode == RESULT_OK){
|
||||
Toast.makeText(this, "Photo Deleted Successfully", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(this, "Photo Deleted Successfully", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(this, "Failed to delete photo", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Failed to hide/unhide photo", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
if (uri != null) {
|
||||
selectedImageUri = uri
|
||||
try {
|
||||
val file = fileManager.copyFileToHiddenDir(selectedImageUri!!, fileType)
|
||||
if (file != null && file.exists()) {
|
||||
Toast.makeText(this, "File hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to hide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No image selected", Toast.LENGTH_SHORT).show()
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@ImageGalleryActivity, this@ImageGalleryActivity)
|
||||
.processMultipleFiles(uriList, fileType,this@ImageGalleryActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
askPermissiom()
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@ImageGalleryActivity, "${copiedFiles.size} Images hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@ImageGalleryActivity, "Failed to hide images", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupIntentSenderLauncher() {
|
||||
intentSenderLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartIntentSenderForResult()
|
||||
@@ -80,7 +97,45 @@ class ImageGalleryActivity : BaseGalleryActivity() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Toast.makeText(this, "Android Version Lower", Toast.LENGTH_SHORT).show()
|
||||
checkAndRequestStoragePermission()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndRequestStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
),
|
||||
STORAGE_PERMISSION_CODE
|
||||
)
|
||||
} else {
|
||||
//storage permission granted
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == STORAGE_PERMISSION_CODE) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, "Storage permissions granted", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Storage permissions denied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +144,7 @@ class ImageGalleryActivity : BaseGalleryActivity() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "image/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
|
||||
@@ -57,6 +57,7 @@ class MainActivity : AppCompatActivity() {
|
||||
binding.btnDot.setOnClickListener { addDecimal() }
|
||||
binding.btnEquals.setOnClickListener { calculateResult() }
|
||||
binding.btnPercent.setOnClickListener { calculatePercentage() }
|
||||
binding.cut.setOnClickListener { cutNumbers() }
|
||||
}
|
||||
|
||||
private fun launchBaseDirectoryPicker() {
|
||||
@@ -162,11 +163,23 @@ class MainActivity : AppCompatActivity() {
|
||||
hasDecimal = currentExpression.contains(".")
|
||||
updateDisplay()
|
||||
} catch (e: Exception) {
|
||||
binding.display.text = "Error"
|
||||
binding.display.text = "Invalid Value"
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDisplay() {
|
||||
binding.display.text = currentExpression
|
||||
}
|
||||
private fun cutNumbers() {
|
||||
if (currentExpression.isNotEmpty()){
|
||||
if (currentExpression.length == 1){
|
||||
currentExpression = currentExpression.substring(0, currentExpression.length - 1)
|
||||
currentExpression = "0"
|
||||
}else currentExpression = currentExpression.substring(0, currentExpression.length - 1)
|
||||
}else currentExpression = "0"
|
||||
updateDisplay()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package devs.org.calculator.activities
|
||||
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import devs.org.calculator.adapters.ImagePreviewAdapter
|
||||
import devs.org.calculator.databinding.ActivityPreviewBinding
|
||||
import devs.org.calculator.utils.DialogUtil
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class PreviewActivity : AppCompatActivity() {
|
||||
@@ -29,70 +30,102 @@ class PreviewActivity : AppCompatActivity() {
|
||||
fileManager = FileManager(this, this)
|
||||
|
||||
currentPosition = intent.getIntExtra("position", 0)
|
||||
|
||||
|
||||
type = intent.getStringExtra("type").toString()
|
||||
|
||||
clickListeners()
|
||||
setupFileType()
|
||||
files = fileManager.getFilesInHiddenDir(filetype)
|
||||
|
||||
setupImagePreview()
|
||||
clickListeners()
|
||||
}
|
||||
|
||||
private fun setupFileType() {
|
||||
when (type) {
|
||||
"IMAGE" -> {
|
||||
filetype = FileManager.FileType.IMAGE
|
||||
binding.title.text = "Preview Images"
|
||||
}
|
||||
|
||||
"VIDEO" -> {
|
||||
filetype = FileManager.FileType.VIDEO
|
||||
binding.title.text = "Preview Videos"
|
||||
}
|
||||
|
||||
"AUDIO" -> {
|
||||
filetype = FileManager.FileType.AUDIO
|
||||
binding.title.text = "Preview Audios"
|
||||
}
|
||||
|
||||
else -> {
|
||||
filetype = FileManager.FileType.DOCUMENT
|
||||
binding.title.text = "Preview Docomnts"
|
||||
binding.title.text = "Preview Documents"
|
||||
}
|
||||
}
|
||||
files = fileManager.getFilesInHiddenDir(filetype)
|
||||
|
||||
setupImagePreview()
|
||||
}
|
||||
|
||||
private fun clickListeners() {
|
||||
binding.delete.setOnClickListener{
|
||||
var fileUri = FileManager.FileManager().getContentUri(this, files[binding.viewPager.currentItem])
|
||||
if (fileUri != null) {
|
||||
DialogUtil(this, this).showMaterialDialog(
|
||||
"Delete File",
|
||||
"Are you sure you want to delete this file ?",
|
||||
"Delete",
|
||||
"Cancel",
|
||||
fileUri!!
|
||||
)
|
||||
}
|
||||
}
|
||||
binding.unHide.setOnClickListener{
|
||||
DialogUtil(this, this).showMaterialDialog("Unhide File","Are you sure you want to unhide this file ?", "Unhide", "Cancel")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupImagePreview() {
|
||||
adapter = ImagePreviewAdapter(this, files,filetype)
|
||||
adapter = ImagePreviewAdapter(this, filetype)
|
||||
adapter.images = files // Set initial data
|
||||
binding.viewPager.adapter = adapter
|
||||
|
||||
val fileUri = Uri.fromFile(files[currentPosition])
|
||||
val filesName = FileManager.FileName(this).getFileNameFromUri(fileUri!!).toString()
|
||||
binding.viewPager.setCurrentItem(currentPosition, false)
|
||||
|
||||
val fileUri = Uri.fromFile(files[currentPosition])
|
||||
val fileName = FileManager.FileName(this).getFileNameFromUri(fileUri).toString()
|
||||
binding.title.text = fileName
|
||||
}
|
||||
|
||||
private fun clickListeners() {
|
||||
binding.delete.setOnClickListener {
|
||||
val fileUri = FileManager.FileManager().getContentUri(this, files[binding.viewPager.currentItem])
|
||||
if (fileUri != null) {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Delete File")
|
||||
.setMessage("Are you sure you want to Delete this file?")
|
||||
.setPositiveButton("Delete") { dialog, _ ->
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@PreviewActivity, this@PreviewActivity).deletePhotoFromExternalStorage(fileUri)
|
||||
removeFileFromList(binding.viewPager.currentItem)
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
binding.unHide.setOnClickListener {
|
||||
val fileUri = FileManager.FileManager().getContentUri(this, files[binding.viewPager.currentItem])
|
||||
if (fileUri != null) {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Unhide File")
|
||||
.setMessage("Are you sure you want to Unhide this file?")
|
||||
.setPositiveButton("Unhide") { dialog, _ ->
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@PreviewActivity, this@PreviewActivity).copyFileToNormalDir(fileUri)
|
||||
removeFileFromList(binding.viewPager.currentItem)
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeFileFromList(position: Int) {
|
||||
val updatedFiles = files.toMutableList().apply { removeAt(position) }
|
||||
files = updatedFiles
|
||||
adapter.images = updatedFiles // Update adapter with the new list
|
||||
|
||||
// Update the ViewPager's position
|
||||
if (!updatedFiles.isNotEmpty()) finish()
|
||||
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,12 +6,18 @@ import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.activities.ImageGalleryActivity
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import devs.org.calculator.utils.FileProcessCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class VideoGalleryActivity : BaseGalleryActivity() {
|
||||
class VideoGalleryActivity : BaseGalleryActivity(), FileProcessCallback {
|
||||
override val fileType = FileManager.FileType.VIDEO
|
||||
private lateinit var pickVideoLauncher: ActivityResultLauncher<Intent>
|
||||
private lateinit var pickLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedUri: Uri? = null
|
||||
|
||||
|
||||
@@ -19,27 +25,39 @@ class VideoGalleryActivity : BaseGalleryActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupFabButton()
|
||||
|
||||
pickVideoLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
pickLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
if (uri != null) {
|
||||
selectedUri = uri
|
||||
try {
|
||||
val file = fileManager.copyFileToHiddenDir(selectedUri!!, fileType)
|
||||
if (file != null && file.exists()) {
|
||||
Toast.makeText(this, "File hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
val clipData = result.data?.clipData
|
||||
val uriList = mutableListOf<Uri>()
|
||||
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
uriList.add(uri)
|
||||
}
|
||||
} else {
|
||||
result.data?.data?.let { uriList.add(it) } // Single file selected
|
||||
}
|
||||
|
||||
if (uriList.isNotEmpty()) {
|
||||
lifecycleScope.launch {
|
||||
FileManager(this@VideoGalleryActivity, this@VideoGalleryActivity)
|
||||
.processMultipleFiles(uriList, fileType,this@VideoGalleryActivity )
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No files selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilesProcessedSuccessfully(copiedFiles: List<File>) {
|
||||
Toast.makeText(this@VideoGalleryActivity, "${copiedFiles.size} Videos hidden successfully", Toast.LENGTH_SHORT).show()
|
||||
loadFiles()
|
||||
} else {
|
||||
Toast.makeText(this, "Failed to hide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "No video selected", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFileProcessFailed() {
|
||||
Toast.makeText(this@VideoGalleryActivity, "Failed to hide videos", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun setupFabButton() {
|
||||
@@ -47,11 +65,12 @@ class VideoGalleryActivity : BaseGalleryActivity() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
type = "video/*"
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
}
|
||||
pickVideoLauncher.launch(intent)
|
||||
pickLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class FileAdapter(private val fileType: FileManager.FileType, var context: Conte
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("Details")
|
||||
.setMessage("File Name: $filesName\\n\\nYou can delete or unghide this file\", \"Delete")
|
||||
.setMessage("File Name: $filesName\n\nYou can delete or Unhide this file.")
|
||||
.setPositiveButton("Delete") { dialog, _ ->
|
||||
// Handle positive button click
|
||||
lifecycleOwner.lifecycleScope.launch{
|
||||
@@ -100,7 +100,11 @@ class FileAdapter(private val fileType: FileManager.FileType, var context: Conte
|
||||
}
|
||||
.setNegativeButton("Unhide") { dialog, _ ->
|
||||
// Handle negative button click
|
||||
|
||||
FileManager(context, context as LifecycleOwner).copyFileToNormalDir(fileUri)
|
||||
val currentList = currentList.toMutableList()
|
||||
currentList.remove(file)
|
||||
submitList(currentList)
|
||||
dialog.dismiss()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
@@ -110,11 +114,6 @@ class FileAdapter(private val fileType: FileManager.FileType, var context: Conte
|
||||
|
||||
}
|
||||
}
|
||||
fun reloadList(file: File){
|
||||
val currentList = currentList.toMutableList()
|
||||
currentList.remove(file)
|
||||
submitList(currentList)
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileViewHolder {
|
||||
|
||||
@@ -6,28 +6,35 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import devs.org.calculator.adapters.FileAdapter.FileDiffCallback
|
||||
import devs.org.calculator.databinding.ViewpagerItemsBinding
|
||||
import devs.org.calculator.utils.FileManager
|
||||
import java.io.File
|
||||
|
||||
class ImagePreviewAdapter(
|
||||
private val context: Context,
|
||||
private val images: List<File>,
|
||||
private var fileType: FileManager.FileType
|
||||
) : RecyclerView.Adapter<ImagePreviewAdapter.ImageViewHolder>() {
|
||||
|
||||
// Use AsyncListDiffer for managing the list
|
||||
private val differ = AsyncListDiffer(this, FileDiffCallback())
|
||||
|
||||
// Expose data management through differ
|
||||
var images: List<File>
|
||||
get() = differ.currentList
|
||||
set(value) = differ.submitList(value)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
|
||||
val binding = ViewpagerItemsBinding.inflate(LayoutInflater.from(context), parent, false
|
||||
)
|
||||
val binding = ViewpagerItemsBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return ImageViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||
val imageUrl = images[position]
|
||||
holder.bind(imageUrl)
|
||||
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = images.size
|
||||
@@ -39,31 +46,27 @@ class ImagePreviewAdapter(
|
||||
binding.imageView.visibility = View.GONE
|
||||
binding.videoView.visibility = View.VISIBLE
|
||||
|
||||
// Set up the VideoView with the current video file
|
||||
val videoUri = Uri.fromFile(file)
|
||||
binding.videoView.setVideoURI(videoUri)
|
||||
binding.videoView.start()
|
||||
|
||||
// Create and attach MediaController
|
||||
val mediaController = MediaController(context)
|
||||
mediaController.setAnchorView(binding.videoView)
|
||||
binding.videoView.setMediaController(mediaController)
|
||||
|
||||
// Handle the "Next" button logic
|
||||
mediaController.setPrevNextListeners(
|
||||
{ // Next button clicked
|
||||
val nextPosition = (adapterPosition + 1) % images.size // Loop to start if last
|
||||
{
|
||||
val nextPosition = (adapterPosition + 1) % images.size
|
||||
playVideoAtPosition(nextPosition)
|
||||
},
|
||||
{ // Previous button clicked
|
||||
{
|
||||
val prevPosition = if (adapterPosition - 1 < 0) images.size - 1 else adapterPosition - 1
|
||||
playVideoAtPosition(prevPosition)
|
||||
}
|
||||
)
|
||||
|
||||
// Play next video automatically when the current one finishes
|
||||
binding.videoView.setOnCompletionListener {
|
||||
val nextPosition = (adapterPosition + 1) % images.size // Loop to start if last
|
||||
val nextPosition = (adapterPosition + 1) % images.size
|
||||
playVideoAtPosition(nextPosition)
|
||||
}
|
||||
}
|
||||
@@ -92,7 +95,5 @@ class ImagePreviewAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ class DialogUtil(private val context: Context, private var lifecycleOwner: Lifec
|
||||
val deleted = documentFile.delete()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (deleted) {
|
||||
Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.Directory
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.webkit.MimeTypeMap
|
||||
@@ -15,6 +17,7 @@ import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import devs.org.calculator.activities.VideoGalleryActivity
|
||||
import devs.org.calculator.adapters.FileAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -95,6 +98,46 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
null
|
||||
}
|
||||
}
|
||||
fun copyFileToNormalDir(uri: Uri): File? {
|
||||
return try {
|
||||
val contentResolver = context.contentResolver
|
||||
|
||||
// Get the target directory
|
||||
val targetDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
targetDir.mkdirs()
|
||||
|
||||
// Create target file
|
||||
val mimeType = contentResolver.getType(uri)
|
||||
val extension = MimeTypeMap.getSingleton()
|
||||
.getExtensionFromMimeType(mimeType) ?: ""
|
||||
val fileName = "${System.currentTimeMillis()}.${extension}"
|
||||
val targetFile = File(targetDir, fileName)
|
||||
|
||||
// Copy file using DocumentFile
|
||||
contentResolver.openInputStream(uri)?.use { input ->
|
||||
targetFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify copy success
|
||||
if (!targetFile.exists() || targetFile.length() == 0L) {
|
||||
throw Exception("File copy failed")
|
||||
}
|
||||
|
||||
// Media scan the new file to hide it
|
||||
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
|
||||
mediaScanIntent.data = Uri.fromFile(targetDir)
|
||||
context.sendBroadcast(mediaScanIntent)
|
||||
lifecycleOwner.lifecycleScope.launch {
|
||||
deletePhotoFromExternalStorage(uri)
|
||||
}
|
||||
targetFile
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -108,9 +151,9 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
val deleted = documentFile.delete()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (deleted) {
|
||||
Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to delete file", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Failed to hide/unhide file", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
return@withContext
|
||||
@@ -120,7 +163,7 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
try {
|
||||
context.contentResolver.delete(photoUri, null, null)
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(context, "File deleted successfully", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
// Handle security exception for Android 10 and above
|
||||
@@ -145,7 +188,7 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Error deleting file: ${e.message}",
|
||||
"Error hiding/unhiding file: ${e.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
@@ -196,6 +239,31 @@ class FileManager(private val context: Context, private val lifecycleOwner: Life
|
||||
}
|
||||
|
||||
|
||||
suspend fun processMultipleFiles(
|
||||
uriList: List<Uri>,
|
||||
fileType: FileType,
|
||||
callback: FileProcessCallback
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val copiedFiles = mutableListOf<File>()
|
||||
for (uri in uriList) {
|
||||
try {
|
||||
val file = copyFileToHiddenDir(uri, fileType)
|
||||
file?.let { copiedFiles.add(it) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (copiedFiles.isNotEmpty()) {
|
||||
callback.onFilesProcessedSuccessfully(copiedFiles)
|
||||
} else {
|
||||
callback.onFileProcessFailed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum class FileType(val dirName: String) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package devs.org.calculator.utils
|
||||
|
||||
import java.io.File
|
||||
|
||||
interface FileProcessCallback {
|
||||
fun onFilesProcessedSuccessfully(copiedFiles: List<File>)
|
||||
fun onFileProcessFailed()
|
||||
}
|
||||
15
app/src/main/res/drawable/backspace.xml
Normal file
15
app/src/main/res/drawable/backspace.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M7.7,6.36L3.533,11.36C3.224,11.731 3.224,12.269 3.533,12.64L7.7,17.64C7.89,17.868 8.172,18 8.468,18H18C19.657,18 21,16.657 21,15V9C21,7.343 19.657,6 18,6H8.468C8.172,6 7.89,6.132 7.7,6.36Z"
|
||||
android:strokeColor="@color/textColor" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="2"/>
|
||||
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M15,10L13,12M13,12L11,14M13,12L11,10M13,12L15,14"
|
||||
android:strokeColor="@color/textColor"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"/>
|
||||
|
||||
</vector>
|
||||
@@ -47,18 +47,6 @@
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnParentheses"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
android:text="( )"
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPercent"
|
||||
android:layout_width="0dp"
|
||||
@@ -81,8 +69,20 @@
|
||||
android:layout_margin="4dp"
|
||||
android:text="÷"
|
||||
app:cornerRadius="15dp"
|
||||
style="@style/Widget.MaterialComponents.Button"/>
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/cut"
|
||||
android:layout_height="0dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:textSize="30sp"
|
||||
android:layout_margin="4dp"
|
||||
app:icon="@drawable/backspace"
|
||||
|
||||
app:iconSize="30dp"
|
||||
app:cornerRadius="15dp"/>
|
||||
<!-- Row 2 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn7"
|
||||
|
||||
20
app/src/main/res/values-night/colors.xml
Normal file
20
app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<!-- Material You dynamic colors -->
|
||||
<color name="primary">@android:color/system_accent1_600</color>
|
||||
<color name="on_primary">@android:color/system_accent1_0</color>
|
||||
<color name="primary_container">@android:color/system_accent1_100</color>
|
||||
<color name="on_primary_container">@android:color/system_accent1_900</color>
|
||||
|
||||
<color name="secondary">@android:color/system_accent2_600</color>
|
||||
<color name="on_secondary">@android:color/system_accent2_0</color>
|
||||
<color name="secondary_container">@android:color/system_accent2_100</color>
|
||||
<color name="on_secondary_container">@android:color/system_accent2_900</color>
|
||||
|
||||
<color name="surface">@android:color/system_neutral1_50</color>
|
||||
<color name="on_surface">@android:color/system_neutral1_900</color>
|
||||
<color name="textColor">#ffffff</color>
|
||||
</resources>
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color -->
|
||||
<item name="colorPrimary">@android:color/system_accent1_600</item>
|
||||
<item name="colorPrimaryVariant">@android:color/system_accent1_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<item name="fontFamily">@font/ubuntu_regular</item>
|
||||
|
||||
<!-- Secondary brand color -->
|
||||
<item name="colorSecondary">@android:color/system_accent2_600</item>
|
||||
<item name="colorSecondaryVariant">@android:color/system_accent2_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
|
||||
<!-- Status bar color -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
|
||||
<!-- Enable window decor fitting -->
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.Theme.Calculator" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color -->
|
||||
<item name="colorPrimary">@android:color/system_accent1_600</item>
|
||||
<item name="colorPrimaryVariant">@android:color/system_accent1_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
|
||||
<!-- Secondary brand color -->
|
||||
<item name="colorSecondary">@android:color/system_accent2_600</item>
|
||||
<item name="colorSecondaryVariant">@android:color/system_accent2_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="fontFamily">@font/ubuntu_regular</item>
|
||||
|
||||
<!-- Status bar color -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
|
||||
<!-- Enable window decor fitting -->
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -16,4 +16,5 @@
|
||||
|
||||
<color name="surface">@android:color/system_neutral1_50</color>
|
||||
<color name="on_surface">@android:color/system_neutral1_900</color>
|
||||
<color name="textColor">#000000</color>
|
||||
</resources>
|
||||
@@ -5,6 +5,7 @@
|
||||
<item name="colorPrimary">@android:color/system_accent1_600</item>
|
||||
<item name="colorPrimaryVariant">@android:color/system_accent1_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<item name="android:textColor"></item>
|
||||
<item name="fontFamily">@font/ubuntu_regular</item>
|
||||
|
||||
<!-- Secondary brand color -->
|
||||
|
||||
Reference in New Issue
Block a user