1package com.peachpasswords.passkeys23import android.os.Build4import android.os.CancellationSignal5import android.graphics.drawable.Icon6import androidx.credentials.provider.BeginCreateCredentialRequest7import androidx.credentials.provider.BeginCreateCredentialResponse8import androidx.credentials.provider.BeginGetCredentialRequest9import androidx.credentials.provider.BeginGetCredentialResponse10import androidx.credentials.provider.BeginGetPublicKeyCredentialOption11import androidx.credentials.provider.CredentialProviderService12import androidx.credentials.provider.ProviderClearCredentialStateRequest13import androidx.credentials.provider.PublicKeyCredentialEntry14import android.os.OutcomeReceiver15import androidx.credentials.exceptions.ClearCredentialException16import androidx.credentials.exceptions.CreateCredentialException17import androidx.credentials.exceptions.GetCredentialException18import androidx.annotation.RequiresApi19import com.peachpasswords.R20import com.peachpasswords.vault.VaultRepository21import dagger.hilt.android.AndroidEntryPoint22import java.time.Instant23import javax.inject.Inject2425262728293031323334353637@AndroidEntryPoint38@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)39class PeachCredentialProviderService : CredentialProviderService() {4041 @Inject lateinit var vault: VaultRepository4243 override fun onBeginGetCredentialRequest(44 request: BeginGetCredentialRequest,45 cancellationSignal: CancellationSignal,46 callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,47 ) {48 if (!vault.settings.value.passkeyProviderEnabled || !vault.isUnlocked.value) {49 callback.onResult(BeginGetCredentialResponse())50 return51 }52 val passkeyOptions = request.beginGetCredentialOptions53 .filterIsInstance<BeginGetPublicKeyCredentialOption>()54 if (passkeyOptions.isEmpty()) {55 callback.onResult(BeginGetCredentialResponse())56 return57 }5859 val icon = Icon.createWithResource(this, R.drawable.ic_peach_mark)60 val entries = passkeyOptions.flatMap { option ->61 PasskeyCredentialPlanner.candidates(vault.getEntries(), option.requestJson).map { candidate ->62 PublicKeyCredentialEntry(63 context = this,64 username = candidate.username,65 pendingIntent = PasskeyCredentialSelectionActivity.pendingIntent(66 this,67 candidate.entryId,68 option.requestJson,69 ),70 beginGetPublicKeyCredentialOption = option,71 displayName = candidate.displayName,72 lastUsedTime = Instant.ofEpochMilli(candidate.lastUsedAt ?: 0L),73 icon = icon,74 isAutoSelectAllowed = false,75 )76 }77 }78 callback.onResult(BeginGetCredentialResponse(credentialEntries = entries))79 }8081 override fun onBeginCreateCredentialRequest(82 request: BeginCreateCredentialRequest,83 cancellationSignal: CancellationSignal,84 callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,85 ) {86 callback.onResult(BeginCreateCredentialResponse())87 }8889 override fun onClearCredentialStateRequest(90 request: ProviderClearCredentialStateRequest,91 cancellationSignal: CancellationSignal,92 callback: OutcomeReceiver<Void?, ClearCredentialException>,93 ) {94 callback.onResult(null)95 }96}