diff --git a/README.md b/README.md index 9f9c5373..4bfdde04 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A android app that take down the headache of remembering passwords. It is open s help you in keeping your passwords safe and secure, in your own local storage without ever needing to push them to cloud. -[Visit Website](https://jeeldobariya38.github.io/Passcodes-Website/) +[Visit Website](https://passcodesapp.github.io/Passcodes-Website/) > What we think of passcodes? > Password management is one such thing that is as simple as remember a password yet, it very @@ -53,7 +53,7 @@ Note: high versions can still run, but we are not guaranteed offically. - [ ] Intuitive UI. - [ ] Update Checkers & Manager. - [x] Password Management. (Current Priority) -- [x] Autofill. +- [x] Autofill. (as preview feature) - [ ] Secure File. (Least Priority, Because it include permission. Which, I am as developer not familiar with 😂) - Could be Image. (JPG. PNG ....) diff --git a/autofill/build.gradle.kts b/autofill/build.gradle.kts index a52bb00a..e4d906b2 100644 --- a/autofill/build.gradle.kts +++ b/autofill/build.gradle.kts @@ -46,12 +46,16 @@ android { } dependencies { + implementation(projects.core) implementation(projects.database) implementation(libs.androidx.core.ktx) implementation(libs.appcompat) implementation(libs.material) + // Datastore Preferences + implementation(libs.androidx.datastore.preferences) + // Data/Persistence (Room Bundle) ksp(libs.room.compiler) implementation(libs.room.ktx) diff --git a/autofill/src/main/kotlin/com/jeeldobariya/passcodes/autofill/PasswordAutofillService.kt b/autofill/src/main/kotlin/com/jeeldobariya/passcodes/autofill/PasswordAutofillService.kt index 37042437..03027e77 100644 --- a/autofill/src/main/kotlin/com/jeeldobariya/passcodes/autofill/PasswordAutofillService.kt +++ b/autofill/src/main/kotlin/com/jeeldobariya/passcodes/autofill/PasswordAutofillService.kt @@ -1,6 +1,7 @@ package com.jeeldobariya.passcodes.autofill import android.app.assist.AssistStructure +import android.content.Context import android.os.CancellationSignal import android.service.autofill.AutofillService import android.service.autofill.FillCallback @@ -11,6 +12,7 @@ import android.service.autofill.SaveInfo import android.service.autofill.SaveRequest import android.view.autofill.AutofillValue import android.widget.RemoteViews +import com.jeeldobariya.passcodes.core.feature_flags.featureFlagsDatastore import com.jeeldobariya.passcodes.database.master.PasswordEntity import com.jeeldobariya.passcodes.database.master.PasswordsDao import kotlinx.coroutines.CoroutineScope @@ -19,6 +21,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.koin.android.ext.android.inject + // TODO: Docs for this autofill is need in @github:PasscodesApp/Passcodes-Docs class PasswordAutofillService : AutofillService() { @@ -29,30 +32,36 @@ class PasswordAutofillService : AutofillService() { cancellationSignal: CancellationSignal, callback: FillCallback ) { - val context = request.fillContexts - val structure = context.last().structure + cancellationSignal.setOnCancelListener { + // TODO: Handle cancellation + } - val viewNodes = mutableMapOf() - parseStructure(structure.getWindowNodeAt(0).rootViewNode, viewNodes) + serviceScope.launch { + if (!isAutofillFeaturesEnabled()) { + callback.onSuccess(null) + return@launch + } - // TODO: Add support for newUsername & newPassword autofill hints. - // https://developer.android.com/reference/androidx/autofill/HintConstants#AUTOFILL_HINT_NEW_USERNAME() - val usernameNode = viewNodes["username"] ?: viewNodes["emailAddress"] - val passwordNode = viewNodes["password"] + val context = request.fillContexts + val structure = context.last().structure - if (usernameNode?.autofillId == null || passwordNode?.autofillId == null) { - callback.onSuccess(null) - return - } + val viewNodes = mutableMapOf() + parseStructure(structure.getWindowNodeAt(0).rootViewNode, viewNodes) - val usernameId = usernameNode.autofillId!! - val passwordId = passwordNode.autofillId!! + // TODO: Add support for newUsername & newPassword autofill hints. + // https://developer.android.com/reference/androidx/autofill/HintConstants#AUTOFILL_HINT_NEW_USERNAME() + val usernameNode = viewNodes["username"] ?: viewNodes["emailAddress"] + val passwordNode = viewNodes["password"] + + if (usernameNode?.autofillId == null || passwordNode?.autofillId == null) { + callback.onSuccess(null) + return@launch + } + + val usernameId = usernameNode.autofillId!! + val passwordId = passwordNode.autofillId!! - cancellationSignal.setOnCancelListener { - // TODO: Handle cancellation - } - serviceScope.launch { val passwordsDao by inject() val passwords = passwordsDao.getAllPasswords().first() val responseBuilder = FillResponse.Builder() @@ -90,35 +99,44 @@ class PasswordAutofillService : AutofillService() { } override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { - val context = request.fillContexts - val structure = context.last().structure + serviceScope.launch { + if (!isAutofillFeaturesEnabled()) { + // developer note: failure is sent cuz, we are not saving the incoming credentials. so, android can let user know something is off... + callback.onFailure(getString(R.string.autofill_feature_are_not_enabled)) + return@launch + } + + val context = request.fillContexts + val structure = context.last().structure + val packageName = structure.activityComponent.packageName - val viewNodes = mutableMapOf() - parseStructure(structure.getWindowNodeAt(0).rootViewNode, viewNodes) - // TODO: Add support for newUsername & newPassword autofill hints. - val usernameNode = viewNodes["username"] ?: viewNodes["emailAddress"] - val passwordNode = viewNodes["password"] + val viewNodes = mutableMapOf() + parseStructure(structure.getWindowNodeAt(0).rootViewNode, viewNodes) - val username = usernameNode?.text?.toString() - val password = passwordNode?.text?.toString() + // TODO: Add support for newUsername & newPassword autofill hints. + val usernameNode = viewNodes["username"] ?: viewNodes["emailAddress"] + val passwordNode = viewNodes["password"] + + val username = usernameNode?.text?.toString() + val password = passwordNode?.text?.toString() + + if (!username.isNullOrEmpty() && !password.isNullOrEmpty()) { - if (!username.isNullOrEmpty() && !password.isNullOrEmpty()) { - serviceScope.launch { val passwordsDao by inject() passwordsDao.insertPassword( PasswordEntity( domain = "Autofill", username = username, password = password, - notes = "Save using autofill service..." + notes = "Save using autofill service...\nIdentifier(maybe): $packageName" ) ) - } - callback.onSuccess() - } else { - callback.onFailure(getString(R.string.could_not_save_credentials)) + callback.onSuccess() + } else { + callback.onFailure(getString(R.string.could_not_save_credentials)) + } } } @@ -136,4 +154,8 @@ class PasswordAutofillService : AutofillService() { parseStructure(node.getChildAt(i), viewNodes) } } + + private suspend fun Context.isAutofillFeaturesEnabled(): Boolean { + return this.featureFlagsDatastore.data.first().isPreviewFeaturesEnabled + } } diff --git a/autofill/src/main/res/values/strings.xml b/autofill/src/main/res/values/strings.xml index dae15923..84457cc6 100644 --- a/autofill/src/main/res/values/strings.xml +++ b/autofill/src/main/res/values/strings.xml @@ -5,4 +5,5 @@ Open Settings Passcodes Autofill is preview feature and shouldn\'t be use in production!! Could not save credentials. + Autofill feature are not enabled!!