/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.ui.binder import android.content.Context import android.content.res.Resources import android.hardware.biometrics.PromptContentItem import android.hardware.biometrics.PromptContentItemBulletedText import android.hardware.biometrics.PromptContentItemPlainText import android.hardware.biometrics.PromptContentView import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptVerticalListContentView import android.text.SpannableString import android.text.Spanned import android.text.TextPaint import android.text.style.BulletSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewTreeObserver import android.widget.Button import android.widget.LinearLayout import android.widget.Space import android.widget.TextView import com.android.settingslib.Utils import com.android.systemui.biometrics.ui.BiometricPromptLayout import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import kotlin.math.ceil private const val TAG = "BiometricCustomizedViewBinder" /** Sub-binder for [BiometricPromptLayout.customized_view_container]. */ object BiometricCustomizedViewBinder { fun bind( customizedViewContainer: LinearLayout, contentView: PromptContentView?, legacyCallback: Spaghetti.Callback ) { customizedViewContainer.repeatWhenAttached { containerView -> if (contentView == null) { containerView.visibility = View.GONE return@repeatWhenAttached } containerView.width { containerWidth -> if (containerWidth == 0) { return@width } (containerView as LinearLayout).addView( contentView.toView(containerView.context, containerWidth, legacyCallback), LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) ) containerView.visibility = View.VISIBLE } } } } private fun PromptContentView.toView( context: Context, containerViewWidth: Int, legacyCallback: Spaghetti.Callback ): View { return when (this) { is PromptVerticalListContentView -> initLayout(context, containerViewWidth) is PromptContentViewWithMoreOptionsButton -> initLayout(context, legacyCallback) else -> { throw IllegalStateException("No such PromptContentView: $this") } } } private fun LayoutInflater.inflateContentView(id: Int, description: String?): LinearLayout { val contentView = inflate(id, null) as LinearLayout val descriptionView = contentView.requireViewById(R.id.customized_view_description) if (!description.isNullOrEmpty()) { descriptionView.text = description } else { descriptionView.visibility = View.GONE } return contentView } private fun PromptContentViewWithMoreOptionsButton.initLayout( context: Context, legacyCallback: Spaghetti.Callback ): View { val inflater = LayoutInflater.from(context) val contentView = inflater.inflateContentView( R.layout.biometric_prompt_content_with_button_layout, description ) val buttonView = contentView.requireViewById