FastestPassAutofillService.kt 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package com.fastest.pass.autofill
  2. import android.app.assist.AssistStructure
  3. import android.service.autofill.AutofillService
  4. import android.service.autofill.Dataset
  5. import android.service.autofill.FillCallback
  6. import android.service.autofill.FillContext
  7. import android.service.autofill.FillRequest
  8. import android.service.autofill.FillResponse
  9. import android.view.autofill.AutofillId
  10. import android.view.autofill.AutofillValue
  11. import android.widget.RemoteViews
  12. import com.fastest.pass.home.domain.model.AddPassword
  13. import com.fastest.pass.sharedpref.CredentialManager
  14. class FastestPassAutofillService : AutofillService() {
  15. override fun onFillRequest(
  16. request: FillRequest,
  17. cancellationSignal: android.os.CancellationSignal,
  18. callback: FillCallback
  19. ) {
  20. // Extract structure
  21. val context: List<FillContext> = request.fillContexts
  22. val structure: AssistStructure = context.last().structure
  23. val packageName = getRequestingPackageName(structure)
  24. if (packageName == null) {
  25. callback.onFailure("Unable to determine requesting package")
  26. return
  27. }
  28. // Fetch credentials matching the package name or domain
  29. /* val data = getCredentials().filter {
  30. it.url.contains(packageName)
  31. }*/
  32. val data = getCredentials()
  33. if (data.isEmpty()) {
  34. callback.onFailure("No matching credentials found")
  35. return
  36. }
  37. val fillResponseBuilder = FillResponse.Builder()
  38. for (i in 0 until structure.windowNodeCount) {
  39. val windowNode = structure.getWindowNodeAt(i)
  40. val viewNode = windowNode.rootViewNode
  41. parseViewNode(viewNode, data, fillResponseBuilder)
  42. }
  43. callback.onSuccess(fillResponseBuilder.build())
  44. }
  45. override fun onSaveRequest(
  46. request: android.service.autofill.SaveRequest,
  47. callback: android.service.autofill.SaveCallback
  48. ) {
  49. /* val credentialManager = CredentialManager(applicationContext)
  50. // Extracting the FillContexts from the SaveRequest
  51. val fillContexts = request.fillContexts
  52. // For each field in the SaveRequest, extract the autofill values (email and password)
  53. val credentials = mutableListOf<AddPassword>()
  54. for (context in fillContexts) {
  55. val structure = context.structure
  56. // Loop through the views in the structure and extract the autofill values
  57. for (i in 0 until structure.windowNodeCount) {
  58. val windowNode = structure.getWindowNodeAt(i)
  59. val rootNode = windowNode.rootViewNode
  60. // Extract the values from the autofill fields
  61. val title = extractAutofillValue(rootNode, "personFirstName") ?: ""
  62. val url = extractAutofillValue(rootNode, "personLastName") ?: ""
  63. val username = extractAutofillValue(rootNode, "username") ?: ""
  64. val password = extractAutofillValue(rootNode, "password") ?: ""
  65. val notes = extractAutofillValue(rootNode, "addressStreet") ?: ""
  66. // Create a new Credential object and add it to the list
  67. val credential = AddPassword(title, url, username, password, notes)
  68. credentials.add(credential)
  69. }
  70. }
  71. // Save the credentials in SharedPreferences
  72. credentialManager.saveCredentials(credentials)*/
  73. callback.onSuccess()
  74. }
  75. private fun extractAutofillValue(node: AssistStructure.ViewNode, hint: String): String? {
  76. val autofillHints = node.autofillHints
  77. // Check if the hint matches the autofill field (e.g., "emailAddress" or "password")
  78. if (autofillHints != null && autofillHints.contains(hint)) {
  79. val value = node.autofillValue
  80. return if (value != null && value.isText) value.textValue.toString() else null
  81. }
  82. // Recursively check child nodes if no value is found
  83. for (i in 0 until node.childCount) {
  84. val childNode = node.getChildAt(i)
  85. val result = extractAutofillValue(childNode, hint)
  86. if (result != null) return result
  87. }
  88. return null
  89. }
  90. private fun getCredentials(): List<AddPassword> {
  91. val credentialManager = CredentialManager(applicationContext)
  92. return credentialManager.getCredentials()
  93. }
  94. private fun parseViewNode(
  95. node: AssistStructure.ViewNode,
  96. credentials: List<AddPassword>,
  97. fillResponseBuilder: FillResponse.Builder
  98. ) {
  99. val autofillHints = node.autofillHints
  100. if (autofillHints != null) {
  101. // Collect Autofill IDs for username and password fields
  102. val emailAddress: AutofillId? =
  103. if ("emailAddress" in autofillHints) node.autofillId else null
  104. val username: AutofillId? =
  105. if ("username" in autofillHints) node.autofillId else null
  106. val passwordId: AutofillId? = if ("password" in autofillHints) node.autofillId else null
  107. if (username != null || emailAddress != null || passwordId != null) {
  108. credentials.forEach { credential ->
  109. val presentation =
  110. RemoteViews(packageName, android.R.layout.simple_list_item_1).apply {
  111. setTextViewText(
  112. android.R.id.text1,
  113. "${credential.username} @ ${credential.url}"
  114. )
  115. }
  116. val datasetBuilder = Dataset.Builder()
  117. emailAddress?.let {
  118. datasetBuilder.setValue(
  119. it,
  120. AutofillValue.forText(credential.username),
  121. presentation
  122. )
  123. }
  124. passwordId?.let {
  125. datasetBuilder.setValue(
  126. it,
  127. AutofillValue.forText(credential.password),
  128. presentation
  129. )
  130. }
  131. username?.let {
  132. datasetBuilder.setValue(
  133. it,
  134. AutofillValue.forText(credential.username),
  135. presentation
  136. )
  137. }
  138. fillResponseBuilder.addDataset(datasetBuilder.build())
  139. }
  140. }
  141. }
  142. // Recursively parse child nodes
  143. for (i in 0 until node.childCount) {
  144. parseViewNode(node.getChildAt(i), credentials, fillResponseBuilder)
  145. }
  146. }
  147. private fun getRequestingPackageName(structure: AssistStructure): String? {
  148. return structure.activityComponent?.packageName
  149. }
  150. }