| /* |
| * Copyright (C) 2019 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.qs |
| |
| import android.content.Context |
| import android.util.AttributeSet |
| import android.view.View |
| import android.widget.FrameLayout |
| import com.android.systemui.R |
| |
| /** |
| * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader]. |
| * |
| * If both elements are visible, it splits the available space according to the following rules: |
| * * If both views add up to less than the total space, they take all the space they need. |
| * * If both views are larger than half the space, each view takes half the space. |
| * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining |
| * space. |
| */ |
| class QSHeaderInfoLayout @JvmOverloads constructor( |
| context: Context, |
| attrs: AttributeSet? = null, |
| defStyle: Int = 0, |
| defStyleRes: Int = 0 |
| ) : FrameLayout(context, attrs, defStyle, defStyleRes) { |
| |
| private lateinit var alarmContainer: View |
| private lateinit var ringerContainer: View |
| private lateinit var statusSeparator: View |
| private val location = Location(0, 0) |
| |
| override fun onFinishInflate() { |
| super.onFinishInflate() |
| alarmContainer = findViewById(R.id.alarm_container) |
| ringerContainer = findViewById(R.id.ringer_container) |
| statusSeparator = findViewById(R.id.status_separator) |
| } |
| |
| override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { |
| // At most one view is there |
| if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b) |
| else { |
| val layoutRTL = isLayoutRtl |
| val width = r - l |
| val height = b - t |
| var offset = 0 |
| |
| offset += alarmContainer.layoutView(width, height, offset, layoutRTL) |
| offset += statusSeparator.layoutView(width, height, offset, layoutRTL) |
| ringerContainer.layoutView(width, height, offset, layoutRTL) |
| } |
| } |
| |
| private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int { |
| location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL) |
| layout(location.left, 0, location.right, pHeight) |
| return this.measuredWidth |
| } |
| |
| override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { |
| super.onMeasure( |
| MeasureSpec.makeMeasureSpec( |
| MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST), |
| heightMeasureSpec) |
| val width = MeasureSpec.getSize(widthMeasureSpec) |
| // Once we measure the views, using as much space as they need, we need to remeasure them |
| // assigning them their final width. This is because TextViews decide whether to MARQUEE |
| // after onMeasure. |
| if (statusSeparator.visibility != View.GONE) { |
| val alarmWidth = alarmContainer.measuredWidth |
| val separatorWidth = statusSeparator.measuredWidth |
| val ringerWidth = ringerContainer.measuredWidth |
| val availableSpace = MeasureSpec.getSize(width) - separatorWidth |
| if (alarmWidth < availableSpace / 2) { |
| measureChild( |
| ringerContainer, |
| MeasureSpec.makeMeasureSpec( |
| Math.min(ringerWidth, availableSpace - alarmWidth), |
| MeasureSpec.AT_MOST), |
| heightMeasureSpec) |
| } else if (ringerWidth < availableSpace / 2) { |
| measureChild(alarmContainer, |
| MeasureSpec.makeMeasureSpec( |
| Math.min(alarmWidth, availableSpace - ringerWidth), |
| MeasureSpec.AT_MOST), |
| heightMeasureSpec) |
| } else { |
| measureChild( |
| alarmContainer, |
| MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST), |
| heightMeasureSpec) |
| measureChild( |
| ringerContainer, |
| MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST), |
| heightMeasureSpec) |
| } |
| } |
| setMeasuredDimension(width, measuredHeight) |
| } |
| |
| private data class Location(var left: Int, var right: Int) { |
| /** |
| * Sets the [left] and [right] with the correct values for laying out the child, respecting |
| * RTL. Only set the variable through here to prevent concurrency issues. |
| * This is done to prevent allocation of [Pair] in [onLayout]. |
| */ |
| fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) { |
| if (RTL) { |
| left = parentWidth - offset - width |
| right = parentWidth - offset |
| } else { |
| left = offset |
| right = offset + width |
| } |
| } |
| } |
| } |