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}