Android EncryptedSharedPreferences With Jetpack Compose (Alt to Secure Jetpack Datastore)

Edit module build.gradle

dependencies {

Edit AppSingleton.kt

import android.content.Contextimport android.content.SharedPreferencesimport AppSingleton {    companion object {        private var secureSharedPreferences: SharedPreferences? = null        fun getSecurePreferences(context: Context): SharedPreferences {            return secureSharedPreferences ?: let {                val masterKey: MasterKey = MasterKey.Builder(context)                    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)                    .build()                secureSharedPreferences = EncryptedSharedPreferences.create(                    context,                    "secureSharedPrefs",                    masterKey,                    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,                    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM                )                secureSharedPreferences!!            }        }    }}

Remove secureSharedPrefs from backup rules.

Edit AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=""



NOTE: If allowBackup=false, setup for dataExtractionRules and fullBackupContent is not required.

Edit @xml/backup_rules

<?xml version="1.0" encoding="utf-8"?><!--
  Sample backup rules file; uncomment and customize as necessary.
  for details.
  Note: This file is ignored for devices older that API 31
  <include domain="sharedpref" path="."/>
  <exclude domain="sharedpref" path="device.xml"/>
  <exclude domain="sharedpref" path="secureSharedPrefs"/>

Edit @xml/data_extraction_rules

<?xml version="1.0" encoding="utf-8"?><!--
   Sample data extraction rules file; uncomment and customize as necessary.
   for details.
        <!-- TODO: Use <include> and <exclude> to control what is backed up.
        <include .../>
        <exclude .../>
        <exclude domain="sharedpref" path="secureSharedPrefs"/>
        <include .../>
        <exclude .../>


class HomeViewModel() : ViewModel() {    private const val PREFERENCES_PASSCODE = "passcode"    fun isPasscodeAvailable(context: Context): Boolean {        val sharedPreferences = AppSingleton.getSecurePreferences(context)        return sharedPreferences.getString(PREFERENCES_PASSCODE, null) != null    }    fun setPasscode(context: Context, passcode: String?) {        val sharedPreferences = AppSingleton.getSecurePreferences(context)        with (sharedPreferences.edit()) {            if (passcode == null)                remove(PREFERENCES_PASSCODE)            else                putString(PREFERENCES_PASSCODE, passcode)            apply()        }    }}

Usage in compose

@Composablefun HomeScreen(    viewModel: HomeViewModel = viewModel()) {    val context = LocalContext.current    var isPasscodeAvailable by remember { mutableStateOf(viewModel.isPasscodeAvailable(context)) }    fun submit(passcode: String) {      viewModel.setPasscode(context, "1234")    }    Button(onClick = { submit("1234") }) {        Text("Set Passcode")    }    if (isPasscodeAvailable) {        Button(onClick = { submit(null) }) {            Text("Remove Passcode")        }    }}


❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.