fork download
  1. /* package whatever; // don't place package name! */
  2.  
  3. import java.util.*;
  4. import java.lang.*;
  5. import java.io.*;
  6.  
  7. /* Name of the class has to be "Main" only if the class is public. */
  8. class Ideone
  9. {
  10. public static void main (String[] args) throws java.lang.Exception
  11. {
  12. // your code goes here
  13. }
  14. }
Success #stdin #stdout 0.07s 52608KB
stdin
package com.htbl.app

import android.Manifest
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.util.Log
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.security.crypto.EncryptedFile
import androidx.security.crypto.MasterKey
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter
import com.google.zxing.common.BitMatrix
import java.io.*
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec

class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: FileAdapter
    private val fileList = mutableListOf<VaultFile>()
    
    private lateinit var encryptionManager: EncryptionManager
    private lateinit var passwordManager: PasswordManager
    private lateinit var biometricAuth: BiometricAuth
    
    private var isUnlocked = false
    private var currentFilter = "all" // all, image, video, audio
    
    companion object {
        private const val PREFS_NAME = "vault_prefs"
        private const val PASSWORD_KEY = "password_hash"
        private const val SALT_KEY = "salt"
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        encryptionManager = EncryptionManager(this)
        passwordManager = PasswordManager(this)
        biometricAuth = BiometricAuth(this)
        
        setupUI()
        checkPermissions()
        
        // إذا كانت أول مرة، نطلب إنشاء كلمة مرور
        if (!passwordManager.isPasswordSet()) {
            showSetupPasswordDialog()
        } else {
            authenticateUser()
        }
    }
    
    private fun setupUI() {
        recyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = FileAdapter(fileList) { file ->
            if (isUnlocked) {
                showFileOptions(file)
            }
        }
        recyclerView.adapter = adapter
        
        findViewById<Button>(R.id.btnAddFile).setOnClickListener { openFilePicker() }
        findViewById<Button>(R.id.btnAddQR).setOnClickListener { openQRScanner() }
        findViewById<Button>(R.id.btnFilterAll).setOnClickListener { currentFilter = "all"; refreshFileList() }
        findViewById<Button>(R.id.btnFilterImage).setOnClickListener { currentFilter = "image"; refreshFileList() }
        findViewById<Button>(R.id.btnFilterVideo).setOnClickListener { currentFilter = "video"; refreshFileList() }
        findViewById<Button>(R.id.btnFilterAudio).setOnClickListener { currentFilter = "audio"; refreshFileList() }
        findViewById<Button>(R.id.btnLock).setOnClickListener { lockVault() }
        findViewById<Button>(R.id.btnGenerateQRCode).setOnClickListener { showGenerateQRDialog() }
    }
    
    private fun checkPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val permissions = arrayOf(
                Manifest.permission.READ_MEDIA_IMAGES,
                Manifest.permission.READ_MEDIA_VIDEO,
                Manifest.permission.READ_MEDIA_AUDIO,
                Manifest.permission.USE_BIOMETRIC
            )
            val needPermissions = permissions.filter {
                ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
            }.toTypedArray()
            
            if (needPermissions.isNotEmpty()) {
                ActivityCompat.requestPermissions(this, needPermissions, 100)
            }
        } else {
            val permissions = arrayOf(
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.USE_BIOMETRIC
            )
            val needPermissions = permissions.filter {
                ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
            }.toTypedArray()
            
            if (needPermissions.isNotEmpty()) {
                ActivityCompat.requestPermissions(this, needPermissions, 100)
            }
        }
    }
    
    private fun authenticateUser() {
        if (biometricAuth.isBiometricAvailable()) {
            biometricAuth.authenticate(
                onSuccess = { 
                    isUnlocked = true
                    refreshFileList()
                    Toast.makeText(this, "تم فتح الخزنة 🔓", Toast.LENGTH_SHORT).show()
                },
                onFailed = { showPasswordDialog() }
            )
        } else {
            showPasswordDialog()
        }
    }
    
    private fun showPasswordDialog() {
        val input = EditText(this)
        input.inputType = android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
        input.hint = "أدخل كلمة المرور"
        
        AlertDialog.Builder(this)
            .setTitle("فتح الخزنة")
            .setMessage("الرجاء إدخال كلمة المرور")
            .setView(input)
            .setPositiveButton("فتح") { _, _ ->
                if (passwordManager.verifyPassword(input.text.toString())) {
                    isUnlocked = true
                    refreshFileList()
                    Toast.makeText(this, "تم فتح الخزنة 🔓", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "كلمة مرور خاطئة ❌", Toast.LENGTH_SHORT).show()
                    finish()
                }
            }
            .setNegativeButton("إغلاق") { _, _ -> finish() }
            .show()
    }
    
    private fun showSetupPasswordDialog() {
        val passwordInput = EditText(this)
        passwordInput.inputType = android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
        passwordInput.hint = "كلمة المرور الجديدة"
        
        val confirmInput = EditText(this)
        confirmInput.inputType = android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
        confirmInput.hint = "تأكيد كلمة المرور"
        
        val layout = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            addView(passwordInput)
            addView(confirmInput)
        }
        
        AlertDialog.Builder(this)
            .setTitle("إعداد الخزنة")
            .setMessage("قم بإنشاء كلمة مرور لحماية ملفاتك")
            .setView(layout)
            .setPositiveButton("إنشاء") { _, _ ->
                val pass = passwordInput.text.toString()
                val confirm = confirmInput.text.toString()
                if (pass.isNotEmpty() && pass == confirm) {
                    passwordManager.registerPassword(pass)
                    authenticateUser()
                } else {
                    Toast.makeText(this, "كلمة المرور غير متطابقة أو فارغة", Toast.LENGTH_SHORT).show()
                    finish()
                }
            }
            .setNegativeButton("إلغاء") { _, _ -> finish() }
            .show()
    }
    
    private fun lockVault() {
        isUnlocked = false
        fileList.clear()
        adapter.notifyDataSetChanged()
        Toast.makeText(this, "تم قفل الخزنة 🔒", Toast.LENGTH_SHORT).show()
    }
    
    private fun refreshFileList() {
        if (!isUnlocked) return
        fileList.clear()
        val vaultDir = File(filesDir, "vault")
        if (vaultDir.exists()) {
            scanFiles(vaultDir)
        }
        adapter.notifyDataSetChanged()
    }
    
    private fun scanFiles(dir: File) {
        dir.listFiles()?.forEach { file ->
            if (file.isDirectory) {
                scanFiles(file)
            } else {
                val type = getFileType(file.name)
                if (currentFilter == "all" || currentFilter == type) {
                    fileList.add(VaultFile(file, type))
                }
            }
        }
    }
    
    private fun getFileType(fileName: String): String {
        return when {
            fileName.endsWith(".enc_image") -> "image"
            fileName.endsWith(".enc_video") -> "video"
            fileName.endsWith(".enc_audio") -> "audio"
            fileName.matches(Regex(".*\\.(jpg|jpeg|png|gif|bmp)$", RegexOption.IGNORE_CASE)) -> "image"
            fileName.matches(Regex(".*\\.(mp4|avi|mkv|mov|3gp)$", RegexOption.IGNORE_CASE)) -> "video"
            fileName.matches(Regex(".*\\.(mp3|wav|ogg|m4a|flac)$", RegexOption.IGNORE_CASE)) -> "audio"
            else -> "other"
        }
    }
    
    private val filePickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK && result.data != null) {
            val uri = result.data?.data
            uri?.let { saveFileToVault(it) }
        }
    }
    
    private fun openFilePicker() {
        val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*", "audio/*"))
        }
        filePickerLauncher.launch(Intent.createChooser(intent, "اختر ملفاً للحفظ"))
    }
    
    private fun saveFileToVault(uri: Uri) {
        try {
            val fileName = getFileName(uri)
            val type = getMimeType(uri)
            val extension = when {
                type.startsWith("image/") -> ".enc_image"
                type.startsWith("video/") -> ".enc_video"
                type.startsWith("audio/") -> ".enc_audio"
                else -> ".enc_file"
            }
            
            val encryptedName = "${System.currentTimeMillis()}_${fileName.replace(" ", "_")}$extension"
            val tempFile = File(cacheDir, "temp_${System.currentTimeMillis()}")
            
            contentResolver.openInputStream(uri)?.use { input ->
                FileOutputStream(tempFile).use { output ->
                    input.copyTo(output)
                }
            }
            
            val encrypted = encryptionManager.encryptFile(tempFile, encryptedName)
            if (encrypted != null) {
                tempFile.delete()
                Toast.makeText(this, "تم حفظ وتشفير الملف 🔒", Toast.LENGTH_SHORT).show()
                refreshFileList()
            } else {
                Toast.makeText(this, "فشل التشفير", Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "حدث خطأ: ${e.message}", Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun getFileName(uri: Uri): String {
        var fileName = "unknown"
        contentResolver.query(uri, null, null, null, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                if (nameIndex != -1) {
                    fileName = cursor.getString(nameIndex)
                }
            }
        }
        return fileName
    }
    
    private fun getMimeType(uri: Uri): String {
        return contentResolver.getType(uri) ?: "application/octet-stream"
    }
    
    private fun showFileOptions(file: VaultFile) {
        val options = arrayOf("عرض", "مشاركة", "حذف", "تصدير إلى الجهاز")
        AlertDialog.Builder(this)
            .setTitle(file.file.name)
            .setItems(options) { _, which ->
                when (which) {
                    0 -> viewFile(file)
                    1 -> shareFile(file)
                    2 -> deleteFile(file)
                    3 -> exportToDevice(file)
                }
            }
            .show()
    }
    
    private fun viewFile(file: VaultFile) {
        val decryptedFile = File(cacheDir, "decrypt_${System.currentTimeMillis()}")
        if (encryptionManager.decryptFile(file.file.name, decryptedFile)) {
            val uri = FileProvider.getUriForFile(this, "${packageName}.provider", decryptedFile)
            val intent = Intent(Intent.ACTION_VIEW).apply {
                setDataAndType(uri, getMimeTypeFromName(file.file.name))
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            }
            startActivity(Intent.createChooser(intent, "فتح الملف"))
        } else {
            Toast.makeText(this, "فشل فك التشفير", Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun shareFile(file: VaultFile) {
        val decryptedFile = File(cacheDir, "share_${System.currentTimeMillis()}")
        if (encryptionManager.decryptFile(file.file.name, decryptedFile)) {
            val uri = FileProvider.getUriForFile(this, "${packageName}.provider", decryptedFile)
            val intent = Intent(Intent.ACTION_SEND).apply {
                type = getMimeTypeFromName(file.file.name)
                putExtra(Intent.EXTRA_STREAM, uri)
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            }
            startActivity(Intent.createChooser(intent, "مشاركة الملف"))
        }
    }
    
    private fun deleteFile(file: VaultFile) {
        AlertDialog.Builder(this)
            .setTitle("حذف الملف")
            .setMessage("هل أنت متأكد من حذف هذا الملف؟")
            .setPositiveButton("حذف") { _, _ ->
                if (file.file.delete()) {
                    refreshFileList()
                    Toast.makeText(this, "تم الحذف", Toast.LENGTH_SHORT).show()
                }
            }
            .setNegativeButton("إلغاء", null)
            .show()
    }
    
    private fun exportToDevice(file: VaultFile) {
        val decryptedFile = File(cacheDir, "export_${System.currentTimeMillis()}")
        if (encryptionManager.decryptFile(file.file.name, decryptedFile)) {
            val originalName = file.file.name.replace(Regex("\\.enc_(image|video|audio|file)$"), "")
            val outputFileName = "${System.currentTimeMillis()}_$originalName"
            
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                val resolver = contentResolver
                val contentValues = ContentValues().apply {
                    put(MediaStore.MediaColumns.DISPLAY_NAME, outputFileName)
                    put(MediaStore.MediaColumns.MIME_TYPE, getMimeTypeFromName(file.file.name))
                    put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
                }
                
                val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
                uri?.let {
                    resolver.openOutputStream(it)?.use { output ->
                        FileInputStream(decryptedFile).use { input ->
                            input.copyTo(output)
                        }
                    }
                    Toast.makeText(this, "تم التصدير إلى مجلد التنزيلات", Toast.LENGTH_SHORT).show()
                }
            } else {
                val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                val outputFile = File(downloadsDir, outputFileName)
                decryptedFile.copyTo(outputFile, true)
                
                MediaScannerConnection.scanFile(this, arrayOf(outputFile.absolutePath), null, null)
                Toast.makeText(this, "تم التصدير إلى ${outputFile.absolutePath}", Toast.LENGTH_SHORT).show()
            }
            decryptedFile.delete()
        }
    }
    
    private fun getMimeTypeFromName(fileName: String): String {
        return when {
            fileName.contains(".enc_image") || fileName.matches(Regex(".*\\.(jpg|jpeg|png|gif)$", RegexOption.IGNORE_CASE)) -> "image/*"
            fileName.contains(".enc_video") || fileName.matches(Regex(".*\\.(mp4|avi|mkv)$", RegexOption.IGNORE_CASE)) -> "video/*"
            fileName.contains(".enc_audio") || fileName.matches(Regex(".*\\.(mp3|wav|ogg)$", RegexOption.IGNORE_CASE)) -> "audio/*"
            else -> "*/*"
        }
    }
    
    private fun openQRScanner() {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        startActivityForResult(intent, 200)
    }
    
    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 200 && resultCode == RESULT_OK) {
            val imageBitmap = data?.extras?.get("data") as Bitmap
            scanQRFromBitmap(imageBitmap)
        }
    }
    
    private fun scanQRFromBitmap(bitmap: Bitmap) {
        val image = InputImage.fromBitmap(bitmap, 0)
        val scanner = BarcodeScanning.getClient()
        
        scanner.process(image)
            .addOnSuccessListener { barcodes ->
                for (barcode in barcodes) {
                    val text = barcode.rawValue
                    if (!text.isNullOrEmpty()) {
                        Toast.makeText(this, "تم مسح الرمز: $text", Toast.LENGTH_LONG).show()
                        saveQRTextAsFile(text)
                    }
                }
            }
            .addOnFailureListener {
                Toast.makeText(this, "فشل مسح الرمز", Toast.LENGTH_SHORT).show()
            }
    }
    
    private fun saveQRTextAsFile(text: String) {
        val fileName = "qr_${System.currentTimeMillis()}.txt"
        val tempFile = File(cacheDir, fileName)
        tempFile.writeText(text)
        encryptionManager.encryptFile(tempFile, "qr_${System.currentTimeMillis()}.enc_file")
        tempFile.delete()
        refreshFileList()
        Toast.makeText(this, "تم حفظ محتوى الرمز", Toast.LENGTH_SHORT).show()
    }
    
    private fun showGenerateQRDialog() {
        val input = EditText(this)
        input.hint = "أدخل النص لتحويله إلى رمز QR"
        
        AlertDialog.Builder(this)
            .setTitle("إنشاء رمز QR")
            .setView(input)
            .setPositiveButton("إنشاء") { _, _ ->
                val text = input.text.toString()
                if (text.isNotEmpty()) {
                    generateQRCode(text)
                }
            }
            .setNegativeButton("إلغاء", null)
            .show()
    }
    
    private fun generateQRCode(text: String) {
        try {
            val writer = MultiFormatWriter()
            val matrix: BitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, 500, 500)
            val bitmap = matrixToBitmap(matrix)
            
            val fileName = "qrcode_${System.currentTimeMillis()}.png"
            val tempFile = File(cacheDir, fileName)
            FileOutputStream(tempFile).use { output ->
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
            }
            
            encryptionManager.encryptFile(tempFile, "qr_${System.currentTimeMillis()}.enc_image")
            tempFile.delete()
            refreshFileList()
            Toast.makeText(this, "تم حفظ رمز QR في الخزنة", Toast.LENGTH_SHORT).show()
        } catch (e: Exception) {
            Toast.makeText(this, "فشل إنشاء الرمز", Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun matrixToBitmap(matrix: BitMatrix): Bitmap {
        val width = matrix.width
        val height = matrix.height
        val pixels = IntArray(width * height)
        for (y in 0 until height) {
            for (x in 0 until width) {
                pixels[y * width + x] = if (matrix[x, y]) android.graphics.Color.BLACK else android.graphics.Color.WHITE
            }
        }
        return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888)
    }
}

// ===================== CLASSES =====================

data class VaultFile(val file: File, val type: String)

class EncryptionManager(private val context: Context) {
    
    private val masterKey by lazy {
        MasterKey.Builder(context)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build()
    }
    
    fun encryptFile(inputFile: File, outputFileName: String): File? {
        return try {
            val encryptedFileDir = File(context.filesDir, "vault")
            if (!encryptedFileDir.exists()) encryptedFileDir.mkdirs()
            
            val encryptedFile = EncryptedFile.Builder(
                context,
                File(encryptedFileDir, outputFileName),
                masterKey,
                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
            ).build()
            
            FileInputStream(inputFile).use { input ->
                encryptedFile.openFileOutput().use { output ->
                    input.copyTo(output)
                }
            }
            File(encryptedFileDir, outputFileName)
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
    
    fun decryptFile(encryptedFileName: String, outputFile: File): Boolean {
        return try {
            val encryptedFileDir = File(context.filesDir, "vault")
            val encryptedFile = EncryptedFile.Builder(
                context,
                File(encryptedFileDir, encryptedFileName),
                masterKey,
                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
            ).build()
            
            encryptedFile.openFileInput().use { input ->
                FileOutputStream(outputFile).use { output ->
                    input.copyTo(output)
                }
            }
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }
}

class PasswordManager(private val context: Context) {
    
    private val prefs = context.getSharedPreferences(MainActivity.PREFS_NAME, Context.MODE_PRIVATE)
    
    fun registerPassword(password: String): Boolean {
        val salt = generateSalt()
        val hash = hashPassword(password, salt)
        prefs.edit().putString(MainActivity.SALT_KEY, salt).apply()
        prefs.edit().putString(MainActivity.PASSWORD_KEY, hash).apply()
        return true
    }
    
    fun verifyPassword(password: String): Boolean {
        val salt = prefs.getString(MainActivity.SALT_KEY, null) ?: return false
        val savedHash = prefs.getString(MainActivity.PASSWORD_KEY, null) ?: return false
        return hashPassword(password, salt) == savedHash
    }
    
    fun isPasswordSet(): Boolean = prefs.contains(MainActivity.PASSWORD_KEY)
    
    private fun generateSalt(): String {
        val salt = ByteArray(16)
        SecureRandom().nextBytes(salt)
        return Base64.getEncoder().encodeToString(salt)
    }
    
    private fun hashPassword(password: String, salt: String): String {
        val spec = PBEKeySpec(password.toCharArray(), Base64.getDecoder().decode(salt), 10000, 256)
        val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
        val hash = factory.generateSecret(spec).encoded
        return Base64.getEncoder().encodeToString(hash)
    }
}

class BiometricAuth(private val activity: AppCompatActivity) {
    
    private val executor = androidx.core.content.ContextCompat.getMainExecutor(activity)
    
    fun isBiometricAvailable(): Boolean {
        val biometricManager = BiometricManager.from(activity)
        return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS
    }
    
    fun authenticate(onSuccess: () -> Unit, onFailed: () -> Unit) {
        val biometricPrompt = BiometricPrompt(activity, executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    onSuccess()
                }
                override fun onAuthenticationFailed() { onFailed() }
                override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { onFailed() }
            }
        )
        
        val promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("فتح الخزنة")
            .setSubtitle("استخدم بصمتك لفتح التطبيق")
            .setNegativeButtonText("إلغاء")
            .build()
        
        biometricPrompt.authenticate(promptInfo)
    }
}

class FileAdapter(private val files: List<VaultFile>, private val onItemClick: (VaultFile) -> Unit) :
    RecyclerView.Adapter<FileAdapter.ViewHolder>() {
    
    inner class ViewHolder(itemView: android.view.View) : RecyclerView.ViewHolder(itemView) {
        val icon: ImageView = itemView.findViewById(R.id.fileIcon)
        val name: TextView = itemView.findViewById(R.id.fileName)
        val size: TextView = itemView.findViewById(R.id.fileSize)
    }
    
    override fun onCreateViewHolder(parent: android.view.ViewGroup, viewType: Int): ViewHolder {
        val view = android.view.LayoutInflater.from(parent.context)
            .inflate(R.layout.item_file, parent, false)
        return ViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val file = files[position]
        holder.name.text = file.file.name.replace(Regex("\\.enc_.*$"), "")
        holder.size.text = formatFileSize(file.file.length())
        holder.icon.setImageResource(getIconForType(file.type))
        holder.itemView.setOnClickListener { onItemClick(file) }
    }
    
    override fun getItemCount(): Int = files.size
    
    private fun getIconForType(type: String): Int {
        return when (type) {
            "image" -> android.R.drawable.ic_menu_gallery
            "video" -> android.R.drawable.ic_media_play
            "audio" -> android.R.drawable.ic_media_play
            else -> android.R.drawable.ic_menu_save
        }
    }
    
    private fun formatFileSize(size: Long): String {
        return when {
            size < 1024 -> "$size B"
            size < 1024 * 1024 -> "${size / 1024} KB"
            size < 1024 * 1024 * 1024 -> "${size / (1024 * 1024)} MB"
            else -> "${size / (1024 * 1024 * 1024)} GB"
        }
    }
}
stdout
Standard output is empty