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