From ceb057b6ed3f368d3dc52f5bbfebe199f9f7a925 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Fri, 17 May 2019 14:06:38 -0400 Subject: [PATCH] Fixed layout of quick_settings_header_info The layout now allocates as much as it needs to QSCarrierGroup, and then allocates leftover space to the status info Views. The containing View (QSHeaderInfoLayout) splits the width between both status (if both are visible) by allocating as much as they need. If the total amount needed is more than what is available, it splits equally unless a view requires less than half. Test: Visual, using multiple Display sizes and Font sizes and Japanese language (for some long strings) Fixes: 132742416 Change-Id: I9f8ce68fa25fbe7d00451e59503575a07c3fd5f4 --- .../res/layout/quick_settings_header_info.xml | 30 ++--- .../android/systemui/qs/QSHeaderInfoLayout.kt | 123 ++++++++++++++++++ .../systemui/qs/QuickStatusBarHeader.java | 2 + 3 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml index d0783a0478955..3582d391271c5 100644 --- a/packages/SystemUI/res/layout/quick_settings_header_info.xml +++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - - - - + android:layout_height="match_parent" + android:gravity="start" > - - - - + - - + diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt new file mode 100644 index 0000000000000..86f54a97043fe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt @@ -0,0 +1,123 @@ +/* + * 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 + + 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 alarmWidth = alarmContainer.measuredWidth + val separatorWidth = statusSeparator.measuredWidth + val ringerWidth = ringerContainer.measuredWidth + val availableSpace = (r - l) - separatorWidth + var left = l + if (alarmWidth < availableSpace / 2) { + alarmContainer.layout(left, t, left + alarmWidth, b) + left += alarmWidth + statusSeparator.layout(left, t, left + separatorWidth, b) + left += separatorWidth + ringerContainer.layout(left, t, left + Math.min(ringerWidth, r - left), b) + } else if (ringerWidth < availableSpace / 2) { + val alarmAllocation = Math.min(availableSpace - ringerWidth, alarmWidth) + alarmContainer.layout(left, t, left + alarmAllocation, b) + left += alarmWidth + statusSeparator.layout(left, t, left + separatorWidth, b) + left += separatorWidth + ringerContainer.layout(left, t, left + ringerWidth, b) + } else { + alarmContainer.layout(left, t, left + availableSpace / 2, b) + left += availableSpace / 2 + statusSeparator.layout(left, t, left + separatorWidth, b) + ringerContainer.layout(r - availableSpace / 2, t, r, b) + } + } + } + + 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) + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 42c616c054f82..6e92d540e0838 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -295,6 +295,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); + mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE); return isOriginalVisible != ringerVisible || !Objects.equals(originalRingerText, mRingerModeTextView.getText()); @@ -311,6 +312,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); + mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); return isOriginalVisible != alarmVisible || !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());