blob: cfceefa2006c4a9c1063cbde346c91821fede9ff [file] [log] [blame]
/*
* Copyright (C) 2021 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.util
import android.content.Context
import android.content.res.Configuration
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.R
/**
* Horizontal [LinearLayout] to contain some text.
*
* The height of this container can alternate between two different heights, depending on whether
* the text takes one line or more.
*
* When the text takes multiple lines, it will use the values in the regular attributes (`padding`,
* `layout_height`). The single line behavior must be set in XML.
*
* XML attributes for single line behavior:
* * `systemui:textViewId`: set the id for the [TextView] that determines the height of the
* container
* * `systemui:singleLineHeight`: sets the height of the view when the text takes up only one line.
* By default, it will use [getMinimumHeight].
* * `systemui:singleLineVerticalPadding`: sets the padding (top and bottom) when then text takes up
* only one line. By default, it is 0.
*
* All dimensions are updated when configuration changes.
*/
class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttrs: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
private val singleLineHeightValue: TypedValue?
private var singleLineHeightPx = 0
private val singleLineVerticalPaddingValue: TypedValue?
private var singleLineVerticalPaddingPx = 0
private val textViewId: Int
private var textView: TextView? = null
private val displayMetrics: DisplayMetrics
get() = context.resources.displayMetrics
private var initialPadding = mPaddingTop // All vertical padding is the same
private var originalMaxLines = 1
var alwaysSingleLine: Boolean = false
set(value) {
field = value
if (field) {
textView?.setSingleLine()
} else {
textView?.maxLines = originalMaxLines
}
}
init {
if (orientation != HORIZONTAL) {
throw IllegalStateException("This view should always have horizontal orientation")
}
val ta = context.obtainStyledAttributes(
attrs,
R.styleable.DualHeightHorizontalLinearLayout, defStyleAttrs, defStyleRes
)
val tempHeight = TypedValue()
singleLineHeightValue = if (
ta.hasValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineHeight)
) {
ta.getValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineHeight, tempHeight)
tempHeight
} else {
null
}
val tempPadding = TypedValue()
singleLineVerticalPaddingValue = if (
ta.hasValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineVerticalPadding)
) {
ta.getValue(
R.styleable.DualHeightHorizontalLinearLayout_singleLineVerticalPadding,
tempPadding
)
tempPadding
} else {
null
}
textViewId = ta.getResourceId(R.styleable.DualHeightHorizontalLinearLayout_textViewId, 0)
ta.recycle()
}
init {
updateResources()
}
override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
super.setPadding(left, top, right, bottom)
initialPadding = top
}
override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
super.setPaddingRelative(start, top, end, bottom)
initialPadding = top
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
textView?.let { tv ->
if (tv.lineCount < 2 || alwaysSingleLine) {
setMeasuredDimension(measuredWidth, singleLineHeightPx)
mPaddingBottom = 0
mPaddingTop = 0
} else {
mPaddingBottom = initialPadding
mPaddingTop = initialPadding
}
}
}
override fun onFinishInflate() {
super.onFinishInflate()
textView = findViewById<TextView>(textViewId)?.also {
originalMaxLines = it.maxLines
}
}
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
updateResources()
}
override fun setOrientation(orientation: Int) {
if (orientation == VERTICAL) {
throw IllegalStateException("This view should always have horizontal orientation")
}
super.setOrientation(orientation)
}
private fun updateResources() {
updateDimensionValue(singleLineHeightValue, minimumHeight, ::singleLineHeightPx::set)
updateDimensionValue(singleLineVerticalPaddingValue, 0, ::singleLineVerticalPaddingPx::set)
}
private inline fun updateDimensionValue(
tv: TypedValue?,
defaultValue: Int,
propertySetter: (Int) -> Unit
) {
val value = tv?.let {
if (it.resourceId != 0) {
context.resources.getDimensionPixelSize(it.resourceId)
} else {
it.getDimension(displayMetrics).toInt()
}
} ?: defaultValue
propertySetter(value)
}
}