我有一个flutter移动的应用程序,我需要实现一个自动填充提供商服务,所以它作为一个密码管理器。Flutter还没有提供任何工具来实现这个功能,所以我需要使用原生Android(使用Kotlin)来实现Android手机,使用原生iOS(使用Swift)来实现iOS手机。从Android开始,作为第一步,我在Flutter和Android之间架起了桥梁。下面是一些代码片段。在我的应用程序的登录页面中 Flutter :
static const channelKotlin = MethodChannel('com.myapp/channel');
个字符
在Android中,我的代码结构如下:
android
|__ app
|__ src
|__ main
|__kotlin
|__myapp
|__ services__MyAutofillService.kt
|__MainActivity.kt
型
其中MainActivity.kt文件是flutter生成的文件,经过编辑以完成flutter和android之间的桥梁,如下面的代码片段所示:
class MainActivity: FlutterFragmentActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.myapp/channel").setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if(call.method == "isAutoFillEnabled") {
var enabled = MyFunctions.openAutoFillSetting(ContextWrapper(getApplicationContext()))
println(enabled.toString())
} else {
result.notImplemented()
}
}
}
}
型
对于MyAutofillService.kt文件,如果自动填充服务提供商遵循官方文档Build autofill services,则实现如下:
const val TAG = "My Autofill"
class MyAutofillService : AutofillService() {
data class ParsedStructure(var usernameId: AutofillId, var passwordId: AutofillId)
data class UserData(var username: String, var password: String)
private fun parseStructure(structure: AssistStructure) : ParsedStructure {
println(structure.getActivityComponent())
var viewNode = structure.getWindowNodeAt(0).getRootViewNode()
return ParsedStructure(viewNode.getAutofillId()!!, viewNode.getAutofillId()!!)
}
private fun fetchUserData(structure: ParsedStructure): UserData {
return UserData(structure.usernameId.toString(), structure.passwordId.toString())
}
private fun traverseStructure(structure: AssistStructure) {
val windowNodes: List<AssistStructure.WindowNode> =
structure.run {
(0 until windowNodeCount).map { getWindowNodeAt(it) }
}
windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
val viewNode: ViewNode? = windowNode.rootViewNode
traverseNode(viewNode)
}
}
private fun traverseNode(viewNode: ViewNode?) {
if (viewNode?.autofillHints?.isNotEmpty() == true) {
// If the client app provides autofill hints, you can obtain them using
//viewNode.getAutofillHints();
println("1")
} else {
// Or use your own heuristics to describe the contents of a view
// using methods such as getText() or getHint()
println("2")
}
val children: List<ViewNode>? =
viewNode?.run {
(0 until childCount).map { getChildAt(it) }
}
children?.forEach { childNode: ViewNode ->
traverseNode(childNode)
}
}
override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, callback: FillCallback) {
Log.d(TAG, "onFillRequest()")
// Get the structure from the request
val context: List<FillContext> = request.fillContexts
val structure: AssistStructure = context[context.size - 1].structure
// Traverse the structure looking for nodes to fill out
val parsedStructure: ParsedStructure = parseStructure(structure)
// Fetch user data that matches the fields
val userData: UserData = fetchUserData(parsedStructure)
// Build the presentation of the datasets
val usernamePresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
usernamePresentation.setTextViewText(android.R.id.text1, "my_username")
val passwordPresentation = RemoteViews(packageName, android.R.layout.simple_list_item_1)
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username")
// Add a dataset to the response
// Builder object requires a non-null presentation
val fillResponse: FillResponse = FillResponse.Builder()
.addDataset(
Dataset.Builder()
.setValue(
parsedStructure.usernameId,
AutofillValue.forText(userData.username),
usernamePresentation
)
.setValue(
parsedStructure.passwordId,
AutofillValue.forText(userData.password), passwordPresentation
)
.build()
)
.setSaveInfo(
SaveInfo.Builder(
SaveInfo.SAVE_DATA_TYPE_USERNAME or SaveInfo.SAVE_DATA_TYPE_PASSWORD,
arrayOf(parsedStructure.usernameId, parsedStructure.passwordId)
).build()
)
.build()
println(fillResponse.toString())
// If there are no errors, call onSuccess() and pass the response
callback.onSuccess(fillResponse)
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
Log.d(TAG, "onSaveRequest()")
// Get the structure from the request
val context: List<FillContext> = request.fillContexts
val structure: AssistStructure = context[context.size - 1].structure
// Traverse the structure looking for data to save
traverseStructure(structure)
// Persist the data - if there are no errors, call onSuccess()
callback.onSuccess()
}
override fun onCreate() {
super.onCreate()
// Perform any initialization tasks here
Log.d(TAG, "onCreate")
}
override fun onConnected() {
super.onConnected()
Log.d(TAG, "onConnected")
}
override fun onDisconnected() {
super.onDisconnected()
Log.d(TAG, "onDisconnected")
}
}
object MyFunctions{
fun openAutoFillSetting(context: Context){
val mAutoFillManager = context.getSystemService(AutofillManager::class.java)
if (mAutoFillManager != null && mAutoFillManager.hasEnabledAutofillServices()) {
Log.d(TAG, "Autofill service already enabled.")
} else {
Log.d(TAG, "Autofill service not yet enabled.")
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
intent.setData(Uri.parse("package:myapp.services.MyAutofillService"))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent)
}
}
}
型
上面MyAutofillService.kt文件的逻辑只是文档中的代码,还没有在我的自定义实现中开始,它只是为了测试应用程序的交互,以及它是否显示数据。同样使用AndroidManifest.xml配置完成。
有了这个代码,我可以得到弹出窗口,使我的应用程序作为自动填充服务提供商在手机中,但当测试与另一个应用程序,试图填写表格,在服务中,我得到的结果有关的表格正在得到填充和onFillRequest功能启动,但我没有得到任何东西,或者当点击按钮登录并成功登录到这个应用程序,我没有得到弹出请求保存凭据.虽然我在最后得到了fillResponse变量,但我没有得到包含应用程序视图中数据的下拉列表。下面是onFillRequest函数末尾println(fillResponse.toString())
行的结果:
的数据
我无法找到哪里可能是问题,我如何才能实现自动填充服务提供程序功能。
那么,我该如何为iOS做到这一点呢?
提前感谢您的帮助。
1条答案
按热度按时间ki1q1bka1#
问题解决了吗?我目前正在Kotlin中处理类似的问题。