summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
blob: c65462193758ff536b93385a38fc0db8f1d40be8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * 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
            }
        }
    }
}