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(this, candidate.entryId),66 beginGetPublicKeyCredentialOption = option,67 displayName = candidate.displayName,68 lastUsedTime = Instant.ofEpochMilli(candidate.lastUsedAt ?: 0L),69 icon = icon,70 isAutoSelectAllowed = false,71 )72 }73 }74 callback.onResult(BeginGetCredentialResponse(credentialEntries = entries))75 }7677 override fun onBeginCreateCredentialRequest(78 request: BeginCreateCredentialRequest,79 cancellationSignal: CancellationSignal,80 callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,81 ) {82 callback.onResult(BeginCreateCredentialResponse())83 }8485 override fun onClearCredentialStateRequest(86 request: ProviderClearCredentialStateRequest,87 cancellationSignal: CancellationSignal,88 callback: OutcomeReceiver<Void?, ClearCredentialException>,89 ) {90 callback.onResult(null)91 }92}