PeachAutofillService.kt

4.1 KB · raw

1package com.peachpasswords.app23import android.app.assist.AssistStructure4import android.os.CancellationSignal5import android.service.autofill.*6import android.view.autofill.AutofillValue7import android.widget.RemoteViews8import android.app.PendingIntent9import android.content.Intent1011class PeachAutofillService : AutofillService() {1213    override fun onFillRequest(14        request: FillRequest,15        cancellationSignal: CancellationSignal,16        callback: FillCallback17    ) {18        val structure = request.fillContexts.lastOrNull()?.structure ?: run {19            callback.onSuccess(null)20            return21        }22        val parser = AssistStructureParser(structure)2324        val domain = parser.webDomain25        val packageName = parser.packageName2627        if (!VaultBridge.isUnlocked()) {28            val authIntent = Intent(this, MainActivity::class.java).apply {29                putExtra("autofill_domain", domain)30                putExtra("autofill_package", packageName)31                // Pass field IDs so the JS side can complete auth with a populated32                // FillResponse via EXTRA_AUTHENTICATION_RESULT after unlock.33                parser.usernameId?.let { putExtra("autofill_username_id", it) }34                parser.passwordId?.let { putExtra("autofill_password_id", it) }35                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)36            }37            val pendingIntent = PendingIntent.getActivity(38                this, 0, authIntent,39                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE40            )41            val presentation = RemoteViews(packageName, R.layout.autofill_item).apply {42                setTextViewText(R.id.autofill_label, "Unlock Peach to autofill")43            }44            val usernameId = parser.usernameId45            if (usernameId == null) {46                callback.onSuccess(null)47                return48            }49            val authDataset = Dataset.Builder()50                .setAuthentication(pendingIntent.intentSender)51                .setValue(usernameId, null, presentation)52                .build()53            callback.onSuccess(54                FillResponse.Builder().addDataset(authDataset).build()55            )56            return57        }5859        val entries = VaultBridge.findEntries(domain ?: packageName ?: "")6061        if (entries.isEmpty()) {62            callback.onSuccess(null)63            return64        }6566        val response = FillResponse.Builder()6768        for (entry in entries.take(3)) {69            val presentation = RemoteViews(packageName, R.layout.autofill_item).apply {70                setTextViewText(R.id.autofill_label, entry.title)71                setTextViewText(R.id.autofill_subtitle, entry.username)72            }7374            val datasetBuilder = Dataset.Builder()7576            parser.usernameId?.let { id ->77                datasetBuilder.setValue(78                    id,79                    AutofillValue.forText(entry.username),80                    presentation81                )82            }8384            parser.passwordId?.let { id ->85                datasetBuilder.setValue(86                    id,87                    AutofillValue.forText(entry.password),88                    RemoteViews(packageName, R.layout.autofill_item).apply {89                        setTextViewText(R.id.autofill_label, entry.title)90                        setTextViewText(R.id.autofill_subtitle, "••••••••")91                    }92                )93            }9495            response.addDataset(datasetBuilder.build())96        }9798        callback.onSuccess(response.build())99    }100101    override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {102        val structure = request.fillContexts.lastOrNull()?.structure ?: run {103            callback.onSuccess()104            return105        }106        val parser = AssistStructureParser(structure)107108        val username = parser.extractUsername()109        val password = parser.extractPassword()110        val domain = parser.webDomain111112        if (username != null && password != null) {113            VaultBridge.offerSave(domain ?: "", username, password)114        }115116        callback.onSuccess()117    }118}