Hello! Can you help me with my prog on kotlin?

Hi all! I would love for you to help me with my little kotlin program. It is a small password generator. The problem I have is that when I restart the program, the passwords stop being copied, and if I don’t, then everything is fine. I think the problem is in preservation… Can you tell me how to correct this mistake?

MainActivity:

@file:OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class,
    ExperimentalMaterial3Api::class, DelicateCoroutinesApi::class
)
@file:Suppress("DEPRECATION")

package com.example.passwords
import android.content.Context
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.passwords.ui.theme.PasswordsTheme
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.security.Key
import javax.crypto.KeyGenerator


val secretKey: Key = KeyGenerator.getInstance("AES").apply {
    init(256)
    Log.d("PasswordLoading", "Loading password data...")
}.generateKey()


fun generatePassword(length: Int): String {
    val characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_"
    return (1..length)
        .map { characters.random() }
        .joinToString("")
    Log.d("PasswordLoading", "Loading password data...")
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            PasswordsTheme {
                PasswordGeneratorScreen(viewModel = PasswordViewModel(applicationContext))
            }
        }
    }
}




@Composable
fun PasswordGeneratorScreen(viewModel: PasswordViewModel = viewModel()) {
    var password by rememberSaveable { mutableStateOf("") }
    var isPasswordGenerated by rememberSaveable { mutableStateOf(false) }
    var showSaveDialog by remember { mutableStateOf(false) }
    var showOverwriteDialog by remember { mutableStateOf(false) }
    var passwordToOverwrite by remember { mutableStateOf<PasswordData?>(null) }
    var showDeleteDialog by remember { mutableStateOf<PasswordData?>(null) }
    val savedPasswords = viewModel.savedPasswords
    val context = LocalContext.current
    val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as android.media.AudioManager

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        topBar = {
            TopAppBar(
                title = {
                    Box(
                        modifier = Modifier.fillMaxWidth(),
                        contentAlignment = Alignment.Center
                    ) {
                        Text(
                            text = "Password Generator:3",
                            style = MaterialTheme.typography.headlineMedium,
                            modifier = Modifier
                                .fillMaxWidth()
                        )
                    }
                }
            )
        },
        content = { innerPadding ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(innerPadding)
                    .padding(16.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {

                Text(
                    text = if (password.isNotEmpty()) "Generated Password: $password" else "No password generated",
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier.padding(bottom = 16.dp)
                )

                Row(
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Button(
                        onClick = {
                            password = generatePassword(16)
                            isPasswordGenerated = true
                        },
                        modifier = Modifier.weight(1f)
                    ) {
                        Text(text = "Generate Password")
                    }

                    Button(
                        onClick = {
                            if (isPasswordGenerated) {
                                showSaveDialog = true
                            }
                        },
                        enabled = isPasswordGenerated,
                        modifier = Modifier.weight(1f)
                    ) {
                        Text(text = "Save Password")
                    }
                }

                Spacer(modifier = Modifier.height(16.dp))


                Text(
                    text = "Saved Passwords",
                    style = MaterialTheme.typography.headlineMedium,
                    modifier = Modifier.padding(vertical = 16.dp)
                )


                LazyVerticalGrid(
                    columns = GridCells.Fixed(2),
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    items(savedPasswords) { passwordData ->
                        Box(
                            modifier = Modifier
                                .size(100.dp)
                                .background(Color.Gray, RoundedCornerShape(18.dp))
                                .combinedClickable(

                                    onClick = {

                                        GlobalScope.launch(Dispatchers.Main) {
                                            try {

                                                val decryptedPassword = withContext(Dispatchers.IO) {
                                                    decryptPassword(passwordData.encryptedPassword, passwordData.iv)
                                                }


                                                copyPasswordToClipboard(decryptedPassword, context)


                                                Toast.makeText(context, "Password copied!", Toast.LENGTH_SHORT).show()
                                                Log.d("PasswordCopy", "Decrypted Password: $decryptedPassword")

                                            } catch (e: Exception) {

                                                Toast.makeText(context, "Error decrypting password", Toast.LENGTH_SHORT).show()
                                            }
                                        }
                                    },
                                    onLongClick = {

                                        if (audioManager.ringerMode == android.media.AudioManager.RINGER_MODE_NORMAL) {
                                            vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
                                        }
                                        showDeleteDialog = passwordData
                                    }
                                ),
                            contentAlignment = Alignment.Center
                        ) {
                            Column(horizontalAlignment = Alignment.CenterHorizontally) {
                                Text(
                                    text = passwordData.appName,
                                    style = MaterialTheme.typography.bodyLarge,
                                    color = Color.White,
                                    fontWeight = FontWeight.Bold,
                                    fontSize = 24.sp
                                )
                                Text(
                                    text = passwordData.login,
                                    style = MaterialTheme.typography.bodySmall,
                                    color = Color.White,
                                    fontSize= 12.sp
                                )
                                Text(
                                    text = "••••••••",
                                    style = MaterialTheme.typography.bodyMedium,
                                    color = Color.White,
                                    modifier = Modifier.padding(8.dp),
                                    fontSize = 28.sp
                                )
                            }
                        }
                    }
                }
            }


            if (showSaveDialog) {
                SavePasswordDialog(
                    onDismiss = { showSaveDialog = false },
                    onSave = { appName, login ->

                        if (audioManager.ringerMode == android.media.AudioManager.RINGER_MODE_NORMAL) {
                            vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
                        }

                      
                        GlobalScope.launch(Dispatchers.Main) {
                            val existingPassword = withContext(Dispatchers.IO) {
                                viewModel.findPassword(appName, login) 
                            }

                            if (existingPassword != null) {
                          
                                passwordToOverwrite = existingPassword
                                showOverwriteDialog = true
                                showSaveDialog = false
                            } else {
                               
                                val (encryptedPassword, iv) = withContext(Dispatchers.IO) {
                                    encryptPassword(password) 
                                }

                          
                                withContext(Dispatchers.IO) {
                                    viewModel.addPassword(PasswordData(encryptedPassword, iv, appName, login))
                                }

                             
                                withContext(Dispatchers.Main) {
                                    Toast.makeText(context, "Password saved!", Toast.LENGTH_SHORT).show()
                                    password = ""  
                                    isPasswordGenerated = false
                                    showSaveDialog = false
                                }
                            }
                        }
                    }
                )
            }


            if (showOverwriteDialog) {
                OverwritePasswordDialog(
                    onDismiss = { showOverwriteDialog = false },
                    onConfirm = {
                        
                        if (audioManager.ringerMode == android.media.AudioManager.RINGER_MODE_NORMAL) {
                            vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
                        }

                   
                        GlobalScope.launch(Dispatchers.Main) {
                            passwordToOverwrite?.let { oldData ->
                            
                                val (encryptedPassword, iv) = withContext(Dispatchers.IO) {
                                    encryptPassword(password)
                                }

                            
                                val newData = PasswordData(encryptedPassword, iv, oldData.appName, oldData.login)

                               
                                withContext(Dispatchers.IO) {
                                    viewModel.updatePassword(oldData, newData)
                                }

                       
                                withContext(Dispatchers.Main) {
                                    Toast.makeText(context, "Password overwritten!", Toast.LENGTH_SHORT).show()
                                    password = "" 
                                    isPasswordGenerated = false
                                }
                            }
                            showOverwriteDialog = false
                        }
                    }
                )
            }


     
            showDeleteDialog?.let { passwordData ->
                DeletePasswordDialog(
                    onDismiss = { showDeleteDialog = null },
                    onConfirm = {
                        viewModel.removePassword(passwordData)
                        Toast.makeText(context, "Password deleted!", Toast.LENGTH_SHORT).show()
                        showDeleteDialog = null
                    }
                )
            }
        }
    )
}


@Composable
fun SavePasswordDialog(onDismiss: () -> Unit, onSave: (String, String) -> Unit) {
    var appName by remember { mutableStateOf("") }
    var login by remember { mutableStateOf("") }
    var isError by remember { mutableStateOf(false) }

    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Save Password") },
        text = {
            Column {
                OutlinedTextField(
                    value = appName,
                    onValueChange = { appName = it },
                    label = { Text("App Name") },
                    isError = isError,
                    modifier = Modifier.fillMaxWidth()
                )
                Spacer(modifier = Modifier.height(8.dp))
                OutlinedTextField(
                    value = login,
                    onValueChange = { login = it },
                    label = { Text("Login") },
                    isError = isError,
                    modifier = Modifier.fillMaxWidth()
                )
                if (isError) {
                    Text("Both fields are required.", color = MaterialTheme.colorScheme.error)
                }
            }
        },
        confirmButton = {
            Button(onClick = {
                if (appName.isNotBlank() && login.isNotBlank()) {
                    onSave(appName, login)
                } else {
                    isError = true
                }
            }) {
                Text("Save")
            }
        },
        dismissButton = {
            Button(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    )
}


@Composable
fun OverwritePasswordDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Overwrite Password") },
        text = { Text("A password for this application and login already exists. Do you want to overwrite it?") },
        confirmButton = {
            Button(onClick = onConfirm) {
                Text("Yes")
            }
        },
        dismissButton = {
            Button(onClick = onDismiss) {
                Text("No")
            }
        }
    )
}


@Composable
fun DeletePasswordDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Delete Password") },
        text = { Text("Are you sure you want to delete this password?") },
        confirmButton = {
            Button(onClick = onConfirm) {
                Text("Yes")
            }
        },
        dismissButton = {
            Button(onClick = onDismiss) {
                Text("No")
            }
        }
    )
}

@Preview(showBackground = true)
@Composable
fun PasswordGeneratorScreenPreview() {
    PasswordsTheme {
        PasswordGeneratorScreen()
    }
}

PasswordData:

package com.example.passwords

import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import java.io.Serializable
import android.util.Base64
import android.content.Context
import android.util.Log
import android.widget.Toast


data class PasswordData(
    val encryptedPassword: String, 
    val iv: String,                 
    val appName: String,
    val login: String
) : Serializable




fun generateIv(): ByteArray {
    return ByteArray(12).apply {
        SecureRandom().nextBytes(this)
        Log.d("PasswordLoading", "Loading password data...")
    }
}


fun copyPasswordToClipboard(password: String, context: Context) {
    val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
    val clip = android.content.ClipData.newPlainText("password", password)
    clipboard.setPrimaryClip(clip)
    Toast.makeText(context, "Password copied!", Toast.LENGTH_SHORT).show()
    Log.d("PasswordLoading", "Loading password data...")
}



fun encryptPassword(password: String): Pair<String, String> {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val iv = generateIv()  // Генерация уникального IV
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
    val encryptedPassword = cipher.doFinal(password.toByteArray())


    val encryptedPasswordBase64 = Base64.encodeToString(encryptedPassword, Base64.NO_WRAP)
    val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP)

    return encryptedPasswordBase64 to ivBase64
}

fun decryptPassword(encryptedData: String, iv: String): String {
    val encryptedPassword = Base64.decode(encryptedData, Base64.NO_WRAP)
    val ivBytes = Base64.decode(iv, Base64.NO_WRAP)
    Log.d("PasswordLoading", "Loading password data...")
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, ivBytes))
    return String(cipher.doFinal(encryptedPassword))
    Log.d("PasswordLoading", "Loading password data...")
}

PasswordViewModel:

package com.example.passwords

import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel


class PasswordViewModel(private val context: Context) : ViewModel() {

    var savedPasswords by mutableStateOf(listOf<PasswordData>())
        private set

    init {
        loadPasswords()
    }

    
    private fun savePasswords() {
        val sharedPreferences = context.getSharedPreferences("passwords", Context.MODE_PRIVATE)
        val editor = sharedPreferences.edit()

        
        val passwordListString = savedPasswords.joinToString("|") { passwordData ->
       
            "${passwordData.encryptedPassword},${passwordData.iv},${passwordData.appName},${passwordData.login}"
        }

   
        editor.putString("password_list", passwordListString)
        editor.apply()
    }


    private fun loadPasswords() {
        val sharedPreferences = context.getSharedPreferences("passwords", Context.MODE_PRIVATE)
        val passwordListString = sharedPreferences.getString("password_list", null)

        if (passwordListString != null) {
            val passwordDataList = passwordListString.split("|").mapNotNull { item ->
                val parts = item.split(",")
                if (parts.size == 4) {
                    val encryptedPassword = parts[0]
                    val iv = parts[1]
                    val appName = parts[2]
                    val login = parts[3]
                    PasswordData(encryptedPassword, iv, appName, login)
                } else {
           
                    null
                }
            } 

            savedPasswords = passwordDataList
        }
    }

    fun addPassword(passwordData: PasswordData) {
        savedPasswords = savedPasswords + passwordData
        savePasswords()  
    }

    fun updatePassword(oldData: PasswordData, newData: PasswordData) {
        savedPasswords = savedPasswords - oldData + newData
        savePasswords() 
    }

    fun removePassword(passwordData: PasswordData) {
        savedPasswords = savedPasswords - passwordData
        savePasswords() 
    }

    fun findPassword(appName: String, login: String): PasswordData? {
        return savedPasswords.find { it.appName == appName && it.login == login }
    }
}

Are there any other files ? is this a native program ? I used to work with sitemile and building lots of mobile apps with react native and kotlin android code, and thats very important if the app is native or not.

No, there are no other files. This is just a password generator with their subsequent preservation. I will be glad if you can help. I also noticed when I remove the decoding with passwords on a click, then in encrypted form they do not change, even after the recovery of the program. But when I add decryption, everything breaks