<lambda>null1 package com.android.systemui.biometrics.ui.binder
2
3 import android.os.UserHandle
4 import android.view.KeyEvent
5 import android.view.View
6 import android.view.inputmethod.EditorInfo
7 import android.view.inputmethod.InputMethodManager
8 import android.widget.ImeAwareEditText
9 import android.widget.TextView
10 import android.window.OnBackInvokedCallback
11 import android.window.OnBackInvokedDispatcher
12 import androidx.lifecycle.Lifecycle
13 import androidx.lifecycle.lifecycleScope
14 import androidx.lifecycle.repeatOnLifecycle
15 import com.android.systemui.R
16 import com.android.systemui.biometrics.ui.CredentialPasswordView
17 import com.android.systemui.biometrics.ui.CredentialView
18 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
19 import com.android.systemui.lifecycle.repeatWhenAttached
20 import kotlinx.coroutines.awaitCancellation
21 import kotlinx.coroutines.flow.first
22 import kotlinx.coroutines.flow.firstOrNull
23 import kotlinx.coroutines.launch
24
25 /** Sub-binder for the [CredentialPasswordView]. */
26 object CredentialPasswordViewBinder {
27
28 /** Bind the view. */
29 fun bind(
30 view: CredentialPasswordView,
31 host: CredentialView.Host,
32 viewModel: CredentialViewModel,
33 requestFocusForInput: Boolean,
34 ) {
35 val imeManager = view.context.getSystemService(InputMethodManager::class.java)!!
36
37 val passwordField: ImeAwareEditText = view.requireViewById(R.id.lockPassword)
38
39 val onBackInvokedCallback = OnBackInvokedCallback { host.onCredentialAborted() }
40
41 view.repeatWhenAttached {
42 // the header info never changes - do it early
43 val header = viewModel.header.first()
44 passwordField.setTextOperationUser(UserHandle.of(header.user.deviceCredentialOwnerId))
45 viewModel.inputFlags.firstOrNull()?.let { flags -> passwordField.inputType = flags }
46 if (requestFocusForInput) {
47 passwordField.requestFocus()
48 passwordField.scheduleShowSoftInput()
49 }
50 passwordField.setOnEditorActionListener(
51 OnImeSubmitListener { text ->
52 lifecycleScope.launch { viewModel.checkCredential(text, header) }
53 }
54 )
55 passwordField.setOnKeyListener(OnBackButtonListener(onBackInvokedCallback))
56
57 repeatOnLifecycle(Lifecycle.State.STARTED) {
58 // dismiss on a valid credential check
59 launch {
60 viewModel.validatedAttestation.collect { attestation ->
61 if (attestation != null) {
62 imeManager.hideSoftInputFromWindow(view.windowToken, 0 /* flags */)
63 host.onCredentialMatched(attestation)
64 } else {
65 passwordField.setText("")
66 }
67 }
68 }
69
70 val onBackInvokedDispatcher = view.findOnBackInvokedDispatcher()
71 if (onBackInvokedDispatcher != null) {
72 launch {
73 onBackInvokedDispatcher.registerOnBackInvokedCallback(
74 OnBackInvokedDispatcher.PRIORITY_DEFAULT,
75 onBackInvokedCallback
76 )
77 awaitCancellation()
78 }
79 .invokeOnCompletion {
80 onBackInvokedDispatcher.unregisterOnBackInvokedCallback(
81 onBackInvokedCallback
82 )
83 }
84 }
85 }
86 }
87 }
88 }
89
90 private class OnBackButtonListener(private val onBackInvokedCallback: OnBackInvokedCallback) :
91 View.OnKeyListener {
onKeynull92 override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
93 if (keyCode != KeyEvent.KEYCODE_BACK) {
94 return false
95 }
96 if (event.action == KeyEvent.ACTION_UP) {
97 onBackInvokedCallback.onBackInvoked()
98 }
99 return true
100 }
101 }
102
103 private class OnImeSubmitListener(private val onSubmit: (text: CharSequence) -> Unit) :
104 TextView.OnEditorActionListener {
onEditorActionnull105 override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent?): Boolean {
106 val isSoftImeEvent =
107 event == null &&
108 (actionId == EditorInfo.IME_NULL ||
109 actionId == EditorInfo.IME_ACTION_DONE ||
110 actionId == EditorInfo.IME_ACTION_NEXT)
111 val isKeyboardEnterKey =
112 event != null &&
113 KeyEvent.isConfirmKey(event.keyCode) &&
114 event.action == KeyEvent.ACTION_DOWN
115 if (isSoftImeEvent || isKeyboardEnterKey) {
116 onSubmit(v.text)
117 return true
118 }
119 return false
120 }
121 }
122