CredentialProviderUnlockActivity.kt

4.3 KB · raw

1package com.peachpasswords.credentials23import android.content.Intent4import android.graphics.drawable.Icon5import android.os.Bundle6import com.peachpasswords.BuildConfig7import androidx.activity.compose.setContent8import com.peachpasswords.ui.components.QaFlags9import com.peachpasswords.ui.components.SecureWindowPolicy10import androidx.credentials.provider.BeginGetCredentialRequest11import androidx.credentials.provider.BeginGetPasswordOption12import androidx.credentials.provider.BeginGetCredentialResponse13import androidx.credentials.provider.PasswordCredentialEntry14import androidx.credentials.provider.PendingIntentHandler15import androidx.fragment.app.FragmentActivity16import com.peachpasswords.R17import com.peachpasswords.passkeys.PasskeyCallerOrigin18import com.peachpasswords.passkeys.PasskeyUnlockIntent19import com.peachpasswords.autofill.appLinkDomains20import com.peachpasswords.shared.extractDomain21import com.peachpasswords.ui.lock.LockScreen22import com.peachpasswords.ui.theme.ACCENT_COLORS23import com.peachpasswords.ui.theme.DEFAULT_ACCENT24import com.peachpasswords.ui.theme.PeachTheme25import com.peachpasswords.vault.VaultRepository26import dagger.hilt.android.AndroidEntryPoint27import java.time.Instant28import javax.inject.Inject2930@AndroidEntryPoint31class CredentialProviderUnlockActivity : FragmentActivity() {32    @Inject lateinit var vault: VaultRepository33    private var debugSecureLease = false3435    override fun onCreate(savedInstanceState: Bundle?) {36        super.onCreate(savedInstanceState)37        if (!BuildConfig.DEBUG) SecureWindowPolicy.setUserSecure(window, true)38        else if (!intent.getBooleanExtra(QaFlags.EXTRA_DISABLE_SECURE_CAPTURE, false)) {39            SecureWindowPolicy.acquire(window)40            debugSecureLease = true41        }42        val request = intent.getBundleExtra(PasskeyUnlockIntent.EXTRA_REQUEST)43            ?.let(BeginGetCredentialRequest::fromBundle)44        if (request == null) {45            finish()46            return47        }48        val settings = vault.settings.value49        val accent = ACCENT_COLORS[settings.accentColor] ?: DEFAULT_ACCENT50        setContent {51            PeachTheme(accent = accent, textSize = settings.textSize) {52                LockScreen(53                    onUnlocked = { complete(request) },54                    onNavigate = { finish() },55                )56            }57        }58    }5960    override fun onDestroy() {61        if (debugSecureLease) SecureWindowPolicy.release(window)62        super.onDestroy()63    }6465    private fun complete(request: BeginGetCredentialRequest) {66        val info = request.callingAppInfo67        val allowlist = PasskeyCallerOrigin.privilegedAllowlist(this)68        val webOrigin = info?.let { runCatching { it.getOrigin(allowlist) }.getOrNull() }69        val domains = if (!webOrigin.isNullOrBlank()) {70            setOfNotNull(extractDomain(webOrigin))71        } else {72            info?.let { appLinkDomains(this, it.packageName).toSet() }.orEmpty()73        }74        val packageName = info?.packageName?.takeIf { webOrigin.isNullOrBlank() }75        val entries = request.beginGetCredentialOptions76            .filterIsInstance<BeginGetPasswordOption>()77            .flatMap { option ->78                PasswordCredentialPlanner.candidates(79                    vault.getEntries(),80                    domains,81                    packageName = packageName,82                    allowedUserIds = option.allowedUserIds,83                ).map { candidate ->84                    PasswordCredentialEntry.Builder(85                        this,86                        candidate.username,87                        PasswordCredentialSelectionActivity.pendingIntent(this, candidate.entryId),88                        option,89                    )90                        .setDisplayName(candidate.title)91                        .setLastUsedTime(Instant.ofEpochMilli(candidate.lastUsedAt ?: 0L))92                        .setIcon(Icon.createWithResource(this, R.drawable.ic_peach_mark))93                        .setAutoSelectAllowed(false)94                        .build()95                }96            }97        val result = Intent()98        PendingIntentHandler.setBeginGetCredentialResponse(99            result,100            BeginGetCredentialResponse.Builder().setCredentialEntries(entries).build(),101        )102        setResult(RESULT_OK, result)103        finish()104    }105}