Merge changes I5e5b5bf5,I44e6f917

* changes:
  Change visuals for expanded QS
  Extracts carrier information from QSFooterImpl
This commit is contained in:
Fabian Kozynski
2019-03-07 22:18:39 +00:00
committed by Android (Google) Code Review
19 changed files with 782 additions and 511 deletions

View File

@@ -65,8 +65,8 @@
android:layout_gravity="center_vertical">
<com.android.systemui.statusbar.AnimatedImageView
android:id="@+id/mobile_signal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_height="@dimen/qs_header_mobile_icon_size"
android:layout_width="@dimen/qs_header_mobile_icon_size"
systemui:hasOverlappingRendering="false"
/>
<ImageView

View File

@@ -14,36 +14,33 @@
~ limitations under the License
-->
<LinearLayout
<com.android.systemui.qs.QSCarrier
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_footer_carrier"
android:layout_width="0dp"
android:id="@+id/linear_carrier"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:background="@android:color/transparent"
android:clickable="false"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="16dp" >
android:clipToPadding="false" >
<include
layout="@layout/mobile_signal_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginEnd="@dimen/qs_carrier_margin_width"
android:visibility="gone" />
<view class="com.android.systemui.qs.QSFooterImpl$QSCarrierText"
<view class="com.android.systemui.qs.QSCarrier$QSCarrierText"
android:id="@+id/qs_carrier_text"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="marquee"
android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.QS.Status"
android:textDirection="locale"
android:singleLine="true" />
android:singleLine="true"
android:maxEms="7"/>
</LinearLayout>
</com.android.systemui.qs.QSCarrier>

View File

@@ -0,0 +1,58 @@
<!--
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
-->
<!-- Extends LinearLayout -->
<com.android.systemui.qs.QSCarrierGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_mobile"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:orientation="horizontal">
<include
layout="@layout/qs_carrier"
android:id="@+id/carrier1"
android:layout_weight="1"/>
<View
android:id="@+id/qs_carrier_divider1"
android:layout_width="@dimen/qs_header_carrier_separator_width"
android:layout_height="match_parent"
android:visibility="gone" />
<include
layout="@layout/qs_carrier"
android:id="@+id/carrier2"
android:layout_weight="1"
android:visibility="gone"/>
<View
android:id="@+id/qs_carrier_divider2"
android:layout_width="@dimen/qs_header_carrier_separator_width"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone" />
<include
layout="@layout/qs_carrier"
android:id="@+id/carrier3"
android:layout_weight="1"
android:visibility="gone"/>
</com.android.systemui.qs.QSCarrierGroup>

View File

@@ -21,6 +21,8 @@
android:id="@+id/qs_footer"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_footer_height"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
android:elevation="4dp"
android:background="@android:color/transparent"
android:baselineAligned="false"
@@ -41,34 +43,35 @@
android:layout_gravity="center_vertical"
android:gravity="end" >
<LinearLayout
android:id="@+id/qs_mobile"
android:layout_width="0dp"
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/qs_footer_actions_edit_container"
android:layout_width="@integer/qs_footer_actions_width"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:orientation="horizontal"
android:layout_marginEnd="32dp">
android:layout_weight="@integer/qs_footer_actions_weight"
android:gravity="center_vertical|start" >
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@android:id/edit"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:clipToPadding="false"
android:contentDescription="@string/accessibility_quick_settings_edit"
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_mode_edit"
android:tint="?android:attr/colorForeground"/>
<include
layout="@layout/qs_footer_carrier"
android:id="@+id/carrier1" />
<View
android:id="@+id/qs_carrier_divider"
android:layout_width="2dp"
<TextView
android:id="@+id/build"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:background="?android:attr/dividerVertical"
android:visibility="gone" />
<include
layout="@layout/qs_footer_carrier"
android:id="@+id/carrier2"
android:clickable="true"
android:gravity="center_vertical"
android:focusable="true"
android:textAppearance="@style/TextAppearance.QS.Status"
android:visibility="gone"/>
</LinearLayout>
</com.android.keyguard.AlphaOptimizedLinearLayout>
<com.android.systemui.qs.PageIndicator
android:id="@+id/footer_page_indicator"
@@ -85,8 +88,8 @@
android:gravity="center_vertical|end" >
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_alignParentEnd="true"
android:background="@drawable/ripple_drawable"
android:focusable="true">
@@ -96,26 +99,14 @@
android:layout_width="@dimen/multi_user_avatar_expanded_size"
android:layout_height="@dimen/multi_user_avatar_expanded_size"
android:layout_gravity="center"
android:tint="?android:attr/colorAccent"
android:scaleType="centerInside"/>
</com.android.systemui.statusbar.phone.MultiUserSwitch>
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@android:id/edit"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:clipToPadding="false"
android:contentDescription="@string/accessibility_quick_settings_edit"
android:focusable="true"
android:padding="15dp"
android:src="@*android:drawable/ic_mode_edit"
android:tint="?android:attr/colorForeground"/>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:clipChildren="false"
android:clipToPadding="false">
@@ -124,10 +115,12 @@
style="@android:style/Widget.Material.Button.Borderless"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="@dimen/qs_footer_icon_padding"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_quick_settings_settings"
android:padding="15dp"
android:src="@drawable/ic_settings_16dp"
android:src="@drawable/ic_settings"
android:scaleType="centerInside"
android:tint="?android:attr/colorForeground"/>
<com.android.systemui.statusbar.AlphaOptimizedImageView
@@ -143,7 +136,6 @@
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<View
android:id="@+id/qs_drag_handle_view"
android:layout_width="24dp"

View File

@@ -58,7 +58,7 @@
android:elevation="4dp"
android:background="@android:color/transparent"
android:focusable="true"
android:accessibilityTraversalBefore="@id/qs_carrier_text"
android:accessibilityTraversalBefore="@android:id/edit"
/>
<include layout="@layout/quick_status_bar_expanded_header" />

View File

@@ -25,7 +25,7 @@
android:id="@+id/long_press_tooltip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_gravity="start|bottom"
android:alpha="0"
android:text="@string/quick_settings_header_onboarding_text"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
@@ -33,54 +33,69 @@
android:visibility="invisible" />
<LinearLayout
android:id="@+id/status_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:gravity="center_vertical"
android:alpha="0">
android:layout_gravity="start|bottom"
android:gravity="center_vertical">
<ImageView
android:id="@+id/next_alarm_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:src="@drawable/stat_sys_alarm"
android:tint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/next_alarm_text"
<LinearLayout
android:id="@+id/status_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
android:visibility="gone"/>
android:layout_gravity="start|center_vertical"
android:layout_weight="1"
android:gravity="center_vertical"
android:alpha="0">
<ImageView
android:id="@+id/next_alarm_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:src="@drawable/stat_sys_alarm"
android:tint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/next_alarm_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.Status"
android:visibility="gone"/>
<View
android:id="@+id/status_separator"
android:layout_width="@dimen/qs_header_separator_width"
android:layout_height="match_parent"
android:visibility="gone"/>
<ImageView
android:id="@+id/ringer_mode_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:tint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/ringer_mode_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.Status"
android:visibility="gone"/>
</LinearLayout>
<View
android:id="@+id/status_separator"
android:layout_width="1dp"
android:minWidth="@dimen/qs_status_separator"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="@android:color/white"
android:backgroundTint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<ImageView
android:id="@+id/ringer_mode_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:tint="?android:attr/textColorPrimary"
android:visibility="gone"/>
<TextView
android:id="@+id/ringer_mode_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
android:visibility="gone"/>
android:layout_weight="1"/>
<include layout="@layout/qs_carrier_group"
android:id="@+id/carrier_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android_layout_gravity="center vertical|end"/>
</LinearLayout>
</FrameLayout>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 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
-->
<resources>
<!-- Action footer width is set to 0 to allow it to stretch (through a weight of 1) and center
the page indicator in between the footer and the carrier text.-->
<integer name="qs_footer_actions_width">0</integer>
<integer name="qs_footer_actions_weight">1</integer>
</resources>

View File

@@ -274,7 +274,16 @@
<!-- The height of the quick settings footer that holds the user switcher, settings icon,
etc. -->
<dimen name="qs_footer_height">56dp</dimen>
<dimen name="qs_footer_height">48dp</dimen>
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">@dimen/qs_footer_height</dimen>
<!-- Margins on each side of QS Footer -->
<dimen name="qs_footer_margin">2dp</dimen>
<!-- Padding on each QS Footer Icon to make the icons 20dpx20dp -->
<dimen name="qs_footer_icon_padding">14dp</dimen>
<!-- The padding between the notifications and the quick settings container -->
<dimen name="qs_notification_padding">@dimen/notification_side_paddings</dimen>
@@ -415,11 +424,16 @@
<dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
<dimen name="qs_header_tooltip_height">18dp</dimen>
<dimen name="qs_header_alarm_icon_size">18dp</dimen>
<dimen name="qs_header_mobile_icon_size">18dp</dimen>
<dimen name="qs_header_alarm_text_margin_start">6dp</dimen>
<dimen name="qs_header_separator_width">8dp</dimen>
<dimen name="qs_header_carrier_separator_width">6dp</dimen>
<dimen name="qs_status_separator">32dp</dimen>
<dimen name="qs_carrier_margin_width">4dp</dimen>
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">16dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
<dimen name="qs_paged_tile_layout_padding_bottom">24dp</dimen>
<dimen name="qs_paged_tile_layout_padding_bottom">16dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
@@ -592,7 +606,7 @@
<dimen name="multi_user_avatar_keyguard_size">22dp</dimen>
<!-- The width of user avatar when expanded -->
<dimen name="multi_user_avatar_expanded_size">18dp</dimen>
<dimen name="multi_user_avatar_expanded_size">20dp</dimen>
<!-- The font size of the time when collapsed in QS -->
<dimen name="qs_time_collapsed_size">14sp</dimen>

View File

@@ -19,8 +19,8 @@
<!-- Action footer width used for layout_width to indicate WRAP_CONTENT (along with a weight of
0) as we can allow the carrier text to stretch as far as needed in the QS footer. -->
<integer name="qs_footer_actions_width">-2</integer>
<integer name="qs_footer_actions_weight">0</integer>
<integer name="qs_footer_actions_width">0</integer>
<integer name="qs_footer_actions_weight">1</integer>
<!-- Maximum number of bubbles to render and animate at one time. While the animations used are
lightweight translation animations, this number can be reduced on lower end devices if any

View File

@@ -227,6 +227,8 @@
<item name="android:textSize">@dimen/qs_carrier_info_text_size</item>
</style>
<style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary" />
<style name="TextAppearance.AppOpsDialog" />
<style name="TextAppearance.AppOpsDialog.Title">

View File

@@ -261,11 +261,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// Fade in the tiles/labels as we reach the final position.
mFirstPageDelayedAnimator = new TouchAnimator.Builder()
.setStartDelay(EXPANDED_TILE_DELAY)
.addFloat(mQsPanel.getPageIndicator(), "alpha", 0, 1)
.addFloat(tileLayout, "alpha", 0, 1)
.addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
.addFloat(mQsPanel.getFooter().getView(), "alpha", 0, 1).build();
mAllViews.add(mQsPanel.getPageIndicator());
mAllViews.add(mQsPanel.getDivider());
mAllViews.add(mQsPanel.getFooter().getView());
float px = 0;
@@ -283,7 +281,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
.addFloat(mQsPanel.getPageIndicator(), "alpha", 0, 1)
.addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
.setListener(mNonFirstPageListener)
.setEndDelay(.5f)

View File

@@ -0,0 +1,141 @@
/*
* 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.content.res.ColorStateList;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.R;
public class QSCarrier extends LinearLayout {
private View mMobileGroup;
private QSCarrierText mCarrierText;
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
private ColorStateList mColorForegroundStateList;
private float mColorForegroundIntensity;
public QSCarrier(Context context) {
super(context);
}
public QSCarrier(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QSCarrier(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public QSCarrier(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMobileGroup = findViewById(R.id.mobile_combo);
mMobileSignal = findViewById(R.id.mobile_signal);
mMobileRoaming = findViewById(R.id.mobile_roaming);
mCarrierText = findViewById(R.id.qs_carrier_text);
int colorForeground = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorForeground);
mColorForegroundStateList = ColorStateList.valueOf(colorForeground);
mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
}
public void updateState(QSCarrierGroup.CellSignalState state) {
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mMobileRoaming.setImageTintList(mColorForegroundStateList);
SignalDrawable d = new SignalDrawable(mContext);
d.setDarkIntensity(mColorForegroundIntensity);
mMobileSignal.setImageDrawable(d);
mMobileSignal.setImageLevel(state.mobileSignalIconId);
StringBuilder contentDescription = new StringBuilder();
if (state.contentDescription != null) {
contentDescription.append(state.contentDescription).append(", ");
}
if (state.roaming) {
contentDescription
.append(mContext.getString(R.string.data_connection_roaming))
.append(", ");
}
// TODO: show mobile data off/no internet text for 5 seconds before carrier text
if (TextUtils.equals(state.typeContentDescription,
mContext.getString(R.string.data_connection_no_internet))
|| TextUtils.equals(state.typeContentDescription,
mContext.getString(R.string.cell_data_off_content_description))) {
contentDescription.append(state.typeContentDescription);
}
mMobileSignal.setContentDescription(contentDescription);
}
}
public void setCarrierText(CharSequence text) {
mCarrierText.setText(text);
}
/**
* TextView that changes its ellipsize value with its visibility.
*/
public static class QSCarrierText extends TextView {
public QSCarrierText(Context context) {
super(context);
}
public QSCarrierText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
// Only show marquee when visible
if (visibility == VISIBLE) {
setEllipsize(TextUtils.TruncateAt.MARQUEE);
setSelected(true);
} else {
setEllipsize(TextUtils.TruncateAt.END);
setSelected(false);
}
}
}
}

View File

@@ -0,0 +1,224 @@
/*
* 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 static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
import android.telephony.SubscriptionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController;
import javax.inject.Inject;
import javax.inject.Named;
/**
* Displays Carrier name and network status in QS
*/
public class QSCarrierGroup extends LinearLayout implements
CarrierTextController.CarrierTextCallback,
NetworkController.SignalCallback {
private static final String TAG = "QSCarrierGroup";
/**
* Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
*/
private static final int SIM_SLOTS = 3;
private final NetworkController mNetworkController;
private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
private CarrierTextController mCarrierTextController;
private boolean mListening;
@Inject
public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NetworkController networkController) {
super(context, attrs);
mNetworkController = networkController;
}
@VisibleForTesting
public QSCarrierGroup(Context context, AttributeSet attrs) {
this(context, attrs,
Dependency.get(NetworkController.class));
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mCarrierGroups[0] = findViewById(R.id.carrier1);
mCarrierGroups[1] = findViewById(R.id.carrier2);
mCarrierGroups[2] = findViewById(R.id.carrier3);
mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1);
mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2);
for (int i = 0; i < SIM_SLOTS; i++) {
mInfos[i] = new CellSignalState();
}
CharSequence separator = mContext.getString(
com.android.internal.R.string.kg_text_message_separator);
mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
public void setListening(boolean listening) {
if (listening == mListening) {
return;
}
mListening = listening;
updateListeners();
}
@Override
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
super.onDetachedFromWindow();
}
private void updateListeners() {
if (mListening) {
if (mNetworkController.hasVoiceCallingFeature()) {
mNetworkController.addCallback(this);
}
mCarrierTextController.setListening(this);
} else {
mNetworkController.removeCallback(this);
mCarrierTextController.setListening(null);
}
}
private void handleUpdateState() {
for (int i = 0; i < SIM_SLOTS; i++) {
mCarrierGroups[i].updateState(mInfos[i]);
}
mCarrierDividers[0].setVisibility(
mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
// This tackles the case of slots 2 being available as well as at least one other.
// In that case we show the second divider. Note that if both dividers are visible, it means
// all three slots are in use, and that is correct.
mCarrierDividers[1].setVisibility(
(mInfos[1].visible && mInfos[2].visible)
|| (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
}
@VisibleForTesting
protected int getSlotIndex(int subscriptionId) {
return SubscriptionManager.getSlotIndex(subscriptionId);
}
@Override
public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
if (info.anySimReady) {
boolean[] slotSeen = new boolean[SIM_SLOTS];
if (info.listOfCarriers.length == info.subscriptionIds.length) {
for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
int slot = getSlotIndex(info.subscriptionIds[i]);
if (slot >= SIM_SLOTS) {
Log.w(TAG, "updateInfoCarrier - slot: " + slot);
continue;
}
if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
Log.e(TAG,
"Invalid SIM slot index for subscription: "
+ info.subscriptionIds[i]);
continue;
}
mInfos[slot].visible = true;
slotSeen[slot] = true;
mCarrierGroups[slot].setCarrierText(info.listOfCarriers[i].toString().trim());
mCarrierGroups[slot].setVisibility(View.VISIBLE);
}
for (int i = 0; i < SIM_SLOTS; i++) {
if (!slotSeen[i]) {
mInfos[i].visible = false;
mCarrierGroups[i].setVisibility(View.GONE);
}
}
} else {
Log.e(TAG, "Carrier information arrays not of same length");
}
} else {
mInfos[0].visible = false;
mCarrierGroups[0].setCarrierText(info.carrierText);
mCarrierGroups[0].setVisibility(View.VISIBLE);
for (int i = 1; i < SIM_SLOTS; i++) {
mInfos[i].visible = false;
mCarrierGroups[i].setCarrierText("");
mCarrierGroups[i].setVisibility(View.GONE);
}
}
handleUpdateState();
}
@Override
public void setMobileDataIndicators(NetworkController.IconState statusIcon,
NetworkController.IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {
int slotIndex = getSlotIndex(subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
return;
}
if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
return;
}
mInfos[slotIndex].visible = statusIcon.visible;
mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
mInfos[slotIndex].typeContentDescription = typeContentDescription;
mInfos[slotIndex].roaming = roaming;
handleUpdateState();
}
@Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
mInfos[i].visible = false;
}
}
handleUpdateState();
}
static final class CellSignalState {
boolean visible;
int mobileSignalIconId;
String contentDescription;
String typeContentDescription;
boolean roaming;
}
}

View File

@@ -23,24 +23,24 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -49,11 +49,10 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.R.dimen;
@@ -62,9 +61,6 @@ import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import com.android.systemui.tuner.TunerService;
@@ -73,15 +69,12 @@ import javax.inject.Inject;
import javax.inject.Named;
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback,
CarrierTextController.CarrierTextCallback {
OnClickListener, OnUserInfoChangedListener {
private static final int SIM_SLOTS = 2;
private static final String TAG = "QSFooterImpl";
private final ActivityStarter mActivityStarter;
private final UserInfoController mUserInfoController;
private final NetworkController mNetworkController;
private final DeviceProvisionedController mDeviceProvisionedController;
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
@@ -94,7 +87,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private boolean mListening;
private boolean mShowEmergencyCallsOnly;
private View mDivider;
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
@@ -103,35 +95,30 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private float mExpansionAmount;
protected View mEdit;
protected View mEditContainer;
private TouchAnimator mSettingsCogAnimator;
private View mActionsContainer;
private View mDragHandle;
private View mCarrierDivider;
private ViewGroup mMobileFooter;
private View[] mMobileGroups = new View[SIM_SLOTS];
private ViewGroup[] mCarrierGroups = new ViewGroup[SIM_SLOTS];
private TextView[] mCarrierTexts = new TextView[SIM_SLOTS];
private ImageView[] mMobileSignals = new ImageView[SIM_SLOTS];
private ImageView[] mMobileRoamings = new ImageView[SIM_SLOTS];
private final CellSignalState[] mInfos =
new CellSignalState[]{new CellSignalState(), new CellSignalState()};
private final int mColorForeground;
private OnClickListener mExpandClickListener;
private CarrierTextController mCarrierTextController;
private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
new Handler(mContext.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
setBuildText();
}
};
@Inject
public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
ActivityStarter activityStarter, UserInfoController userInfoController,
NetworkController networkController,
DeviceProvisionedController deviceProvisionedController) {
super(context, attrs);
mColorForeground = Utils.getColorAttrDefaultColor(context, android.R.attr.colorForeground);
mActivityStarter = activityStarter;
mUserInfoController = userInfoController;
mNetworkController = networkController;
mDeviceProvisionedController = deviceProvisionedController;
}
@@ -140,7 +127,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
this(context, attrs,
Dependency.get(ActivityStarter.class),
Dependency.get(UserInfoController.class),
Dependency.get(NetworkController.class),
Dependency.get(DeviceProvisionedController.class));
}
@@ -159,26 +145,12 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
mMobileFooter = findViewById(R.id.qs_mobile);
mCarrierGroups[0] = findViewById(R.id.carrier1);
mCarrierGroups[1] = findViewById(R.id.carrier2);
for (int i = 0; i < SIM_SLOTS; i++) {
mMobileGroups[i] = mCarrierGroups[i].findViewById(R.id.mobile_combo);
mMobileSignals[i] = mCarrierGroups[i].findViewById(R.id.mobile_signal);
mMobileRoamings[i] = mCarrierGroups[i].findViewById(R.id.mobile_roaming);
mCarrierTexts[i] = mCarrierGroups[i].findViewById(R.id.qs_carrier_text);
}
mCarrierDivider = findViewById(R.id.qs_carrier_divider);
CharSequence separator = mContext.getString(
com.android.internal.R.string.kg_text_message_separator);
mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
mDragHandle = findViewById(R.id.qs_drag_handle_view);
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
@@ -190,6 +162,21 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
oldBottom) -> updateAnimator(right - left));
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
updateEverything();
setBuildText();
}
private void setBuildText() {
TextView v = findViewById(R.id.build);
if (v == null) return;
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
v.setText(mContext.getString(
com.android.internal.R.string.bugreport_status,
Build.VERSION.RELEASE,
Build.ID));
v.setVisibility(View.VISIBLE);
} else {
v.setVisibility(View.GONE);
}
}
private void updateAnimator(int width) {
@@ -222,14 +209,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private void updateResources() {
updateFooterAnimator();
// Update the width and weight of the actions container as the page indicator can sometimes
// show and the layout needs to center it between the carrier text and actions container.
LinearLayout.LayoutParams params =
(LinearLayout.LayoutParams) mActionsContainer.getLayoutParams();
params.width = mContext.getResources().getInteger(R.integer.qs_footer_actions_width);
params.weight = mContext.getResources().getInteger(R.integer.qs_footer_actions_weight);
mActionsContainer.setLayoutParams(params);
}
private void updateFooterAnimator() {
@@ -240,9 +219,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
.addFloat(mDivider, "alpha", 0, 1)
.addFloat(mMobileFooter, "alpha", 0, 0, 1)
.addFloat(mCarrierDivider, "alpha", 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mEditContainer, "alpha", 0, 1)
.addFloat(mDragHandle, "alpha", 1, 0, 0)
.addFloat(mPageIndicator, "alpha", 0, 1)
.setStartDelay(0.15f)
@@ -276,10 +254,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED), false,
mDeveloperSettingsObserver, UserHandle.USER_ALL);
}
@Override
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);
super.onDetachedFromWindow();
}
@@ -337,7 +324,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
mMultiUserSwitch.setVisibility(showUserSwitcher(isDemo) ? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
}
@@ -364,16 +351,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private void updateListeners() {
if (mListening) {
mUserInfoController.addCallback(this);
if (mNetworkController.hasVoiceCallingFeature()) {
mNetworkController.addEmergencyListener(this);
mNetworkController.addCallback(this);
}
mCarrierTextController.setListening(this);
} else {
mUserInfoController.removeCallback(this);
mNetworkController.removeEmergencyListener(this);
mNetworkController.removeCallback(this);
mCarrierTextController.setListening(null);
}
}
@@ -429,17 +408,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
true /* dismissShade */);
}
@Override
public void setEmergencyCallsOnly(boolean show) {
boolean changed = show != mShowEmergencyCallsOnly;
if (changed) {
mShowEmergencyCallsOnly = show;
if (mExpanded) {
updateEverything();
}
}
}
@Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
if (picture != null &&
@@ -452,162 +420,4 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
mMultiUserAvatar.setImageDrawable(picture);
}
private void handleUpdateState() {
for (int i = 0; i < SIM_SLOTS; i++) {
mMobileGroups[i].setVisibility(mInfos[i].visible ? View.VISIBLE : View.GONE);
if (mInfos[i].visible) {
mMobileRoamings[i].setVisibility(mInfos[i].roaming ? View.VISIBLE : View.GONE);
mMobileRoamings[i].setImageTintList(ColorStateList.valueOf(mColorForeground));
SignalDrawable d = new SignalDrawable(mContext);
d.setDarkIntensity(QuickStatusBarHeader.getColorIntensity(mColorForeground));
mMobileSignals[i].setImageDrawable(d);
mMobileSignals[i].setImageLevel(mInfos[i].mobileSignalIconId);
StringBuilder contentDescription = new StringBuilder();
if (mInfos[i].contentDescription != null) {
contentDescription.append(mInfos[i].contentDescription).append(", ");
}
if (mInfos[i].roaming) {
contentDescription
.append(mContext.getString(R.string.data_connection_roaming))
.append(", ");
}
// TODO: show mobile data off/no internet text for 5 seconds before carrier text
if (TextUtils.equals(mInfos[i].typeContentDescription,
mContext.getString(R.string.data_connection_no_internet))
|| TextUtils.equals(mInfos[i].typeContentDescription,
mContext.getString(R.string.cell_data_off_content_description))) {
contentDescription.append(mInfos[i].typeContentDescription);
}
mMobileSignals[i].setContentDescription(contentDescription);
}
}
mCarrierDivider.setVisibility(
mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
}
@VisibleForTesting
protected int getSlotIndex(int subscriptionId) {
return SubscriptionManager.getSlotIndex(subscriptionId);
}
@Override
public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
if (info.anySimReady) {
boolean[] slotSeen = new boolean[SIM_SLOTS];
if (info.listOfCarriers.length == info.subscriptionIds.length) {
for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
int slot = getSlotIndex(info.subscriptionIds[i]);
if (slot >= SIM_SLOTS) {
Log.w(TAG, "updateInfoCarrier - slot: " + slot);
continue;
}
if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
Log.e(TAG,
"Invalid SIM slot index for subscription: "
+ info.subscriptionIds[i]);
continue;
}
mInfos[slot].visible = true;
slotSeen[slot] = true;
mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim());
mCarrierGroups[slot].setVisibility(View.VISIBLE);
}
for (int i = 0; i < SIM_SLOTS; i++) {
if (!slotSeen[i]) {
mInfos[i].visible = false;
mCarrierGroups[i].setVisibility(View.GONE);
}
}
} else {
Log.e(TAG, "Carrier information arrays not of same length");
}
} else {
mInfos[0].visible = false;
mCarrierTexts[0].setText(info.carrierText);
mCarrierGroups[0].setVisibility(View.VISIBLE);
for (int i = 1; i < SIM_SLOTS; i++) {
mInfos[i].visible = false;
mCarrierTexts[i].setText("");
mCarrierGroups[i].setVisibility(View.GONE);
}
}
handleUpdateState();
}
@Override
public void setMobileDataIndicators(NetworkController.IconState statusIcon,
NetworkController.IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {
int slotIndex = getSlotIndex(subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
return;
}
if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
return;
}
mInfos[slotIndex].visible = statusIcon.visible;
mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
mInfos[slotIndex].typeContentDescription = typeContentDescription;
mInfos[slotIndex].roaming = roaming;
handleUpdateState();
}
@Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
mInfos[0].visible = false;
mInfos[1].visible = false;
}
handleUpdateState();
}
private final class CellSignalState {
boolean visible;
int mobileSignalIconId;
public String contentDescription;
String typeContentDescription;
boolean roaming;
}
/**
* TextView that changes its ellipsize value with its visibility.
*/
public static class QSCarrierText extends TextView {
public QSCarrierText(Context context) {
super(context);
}
public QSCarrierText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public QSCarrierText(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
// Only show marquee when visible
if (visibility == VISIBLE) {
setEllipsize(TextUtils.TruncateAt.MARQUEE);
setSelected(true);
} else {
setEllipsize(TextUtils.TruncateAt.END);
setSelected(false);
}
}
}
}

View File

@@ -16,8 +16,6 @@
package com.android.systemui.qs;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import android.annotation.Nullable;
@@ -79,7 +77,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected QSTileHost mHost;
protected QSSecurityFooter mFooter;
private PageIndicator mPanelPageIndicator;
private PageIndicator mFooterPageIndicator;
private boolean mGridContentVisible = true;
@@ -110,11 +107,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mTileLayout.setListening(mListening);
addView((View) mTileLayout);
mPanelPageIndicator = (PageIndicator) LayoutInflater.from(context).inflate(
R.layout.qs_page_indicator, this, false);
addView(mPanelPageIndicator);
((PagedTileLayout) mTileLayout).setPageIndicator(mPanelPageIndicator);
mQsTileRevealController = new QSTileRevealController(mContext, this,
(PagedTileLayout) mTileLayout);
@@ -141,10 +133,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return mDivider;
}
public View getPageIndicator() {
return mPanelPageIndicator;
}
public QSTileRevealController getQsTileRevealController() {
return mQsTileRevealController;
}
@@ -265,21 +253,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private void updatePageIndicator() {
if (mTileLayout instanceof PagedTileLayout) {
// If we're in landscape, and we have the footer page indicator (which we should if the
// footer has been initialized & linked), then we'll show the footer page indicator to
// save space in the main QS tile area. Otherwise, we'll use the default one under the
// tiles/above the footer.
boolean shouldUseFooterPageIndicator =
getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE
&& mFooterPageIndicator != null;
mPanelPageIndicator.setVisibility(View.GONE);
if (mFooterPageIndicator != null) {
mFooterPageIndicator.setVisibility(View.GONE);
}
((PagedTileLayout) mTileLayout).setPageIndicator(
shouldUseFooterPageIndicator ? mFooterPageIndicator : mPanelPageIndicator);
((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator);
}
}
}

View File

@@ -113,6 +113,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private boolean mListening;
private boolean mQsDisabled;
private QSCarrierGroup mCarrierGroup;
protected QuickQSPanel mHeaderQsPanel;
protected QSTileHost mHost;
private TintedIconManager mIconManager;
@@ -207,6 +208,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mRingerModeTextView = findViewById(R.id.ringer_mode_text);
mPrivacyChip = findViewById(R.id.privacy_chip);
mPrivacyChip.setOnClickListener(this);
mCarrierGroup = findViewById(R.id.carrier_group);
updateResources();
@@ -519,6 +521,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
mHeaderQsPanel.setListening(listening);
mListening = listening;
mCarrierGroup.setListening(mListening);
if (listening) {
mZenController.addCallback(this);

View File

@@ -24,6 +24,7 @@ import android.view.LayoutInflater;
import android.view.View;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.QSCarrierGroup;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -124,6 +125,11 @@ public class InjectionInflationController {
* Creates the NotificationPanelView.
*/
NotificationPanelView createPanelView();
/**
* Creates the QSCarrierGroup
*/
QSCarrierGroup createQSCarrierGroup();
}
/**

View File

@@ -0,0 +1,177 @@
/*
* 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 static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.telephony.SubscriptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextController;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class QSCarrierGroupTest extends LeakCheckedTest {
private QSCarrierGroup mCarrierGroup;
@Before
public void setup() throws Exception {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
TestableLooper.get(this).runWithLooper(
() -> mCarrierGroup = (QSCarrierGroup) LayoutInflater.from(mContext).inflate(
R.layout.qs_carrier_group, null));
}
@Test // throws no Exception
public void testUpdateCarrierText_sameLengths() {
QSCarrierGroup spiedCarrierGroup = Mockito.spy(mCarrierGroup);
when(spiedCarrierGroup.getSlotIndex(anyInt())).thenAnswer(
new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
return invocationOnMock.getArgument(0);
}
});
// listOfCarriers length 1, subscriptionIds length 1, anySims false
CarrierTextController.CarrierTextCallbackInfo
c1 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
false,
new int[]{0});
spiedCarrierGroup.updateCarrierInfo(c1);
// listOfCarriers length 1, subscriptionIds length 1, anySims true
CarrierTextController.CarrierTextCallbackInfo
c2 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
true,
new int[]{0});
spiedCarrierGroup.updateCarrierInfo(c2);
// listOfCarriers length 2, subscriptionIds length 2, anySims false
CarrierTextController.CarrierTextCallbackInfo
c3 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
false,
new int[]{0, 1});
spiedCarrierGroup.updateCarrierInfo(c3);
// listOfCarriers length 2, subscriptionIds length 2, anySims true
CarrierTextController.CarrierTextCallbackInfo
c4 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
spiedCarrierGroup.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testUpdateCarrierText_differentLength() {
QSCarrierGroup spiedCarrierGroup = Mockito.spy(mCarrierGroup);
when(spiedCarrierGroup.getSlotIndex(anyInt())).thenAnswer(
new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
return invocationOnMock.getArgument(0);
}
});
// listOfCarriers length 2, subscriptionIds length 1, anySims false
CarrierTextController.CarrierTextCallbackInfo
c1 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
false,
new int[]{0});
spiedCarrierGroup.updateCarrierInfo(c1);
// listOfCarriers length 2, subscriptionIds length 1, anySims true
CarrierTextController.CarrierTextCallbackInfo
c2 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0});
spiedCarrierGroup.updateCarrierInfo(c2);
// listOfCarriers length 1, subscriptionIds length 2, anySims false
CarrierTextController.CarrierTextCallbackInfo
c3 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
false,
new int[]{0, 1});
spiedCarrierGroup.updateCarrierInfo(c3);
// listOfCarriers length 1, subscriptionIds length 2, anySims true
CarrierTextController.CarrierTextCallbackInfo
c4 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
true,
new int[]{0, 1});
spiedCarrierGroup.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testUpdateCarrierText_invalidSim() {
QSCarrierGroup spiedCarrierGroup = Mockito.spy(mCarrierGroup);
when(spiedCarrierGroup.getSlotIndex(anyInt())).thenReturn(
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
CarrierTextController.CarrierTextCallbackInfo
c4 = new CarrierTextController.CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
spiedCarrierGroup.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testSetMobileDataIndicators_invalidSim() {
QSCarrierGroup spiedCarrierGroup = Mockito.spy(mCarrierGroup);
when(spiedCarrierGroup.getSlotIndex(anyInt())).thenReturn(
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
spiedCarrierGroup.setMobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
0, 0, true, true, "", "", true, 0, true);
}
}

View File

@@ -16,13 +16,10 @@ package com.android.systemui.qs;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.telephony.SubscriptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -31,21 +28,16 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextController.CarrierTextCallbackInfo;
import com.android.systemui.R;
import com.android.systemui.R.id;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -77,117 +69,4 @@ public class QSFooterImplTest extends LeakCheckedTest {
// Verify Settings wasn't launched.
verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
}
@Test // throws no Exception
public void testUpdateCarrierText_sameLengts() {
QSFooterImpl spiedFooter = Mockito.spy(mFooter);
when(spiedFooter.getSlotIndex(anyInt())).thenAnswer(
new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
return invocationOnMock.getArgument(0);
}
});
// listOfCarriers length 1, subscriptionIds length 1, anySims false
CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
false,
new int[]{0});
spiedFooter.updateCarrierInfo(c1);
// listOfCarriers length 1, subscriptionIds length 1, anySims true
CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
true,
new int[]{0});
spiedFooter.updateCarrierInfo(c2);
// listOfCarriers length 2, subscriptionIds length 2, anySims false
CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
false,
new int[]{0, 1});
spiedFooter.updateCarrierInfo(c3);
// listOfCarriers length 2, subscriptionIds length 2, anySims true
CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
spiedFooter.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testUpdateCarrierText_differentLength() {
QSFooterImpl spiedFooter = Mockito.spy(mFooter);
when(spiedFooter.getSlotIndex(anyInt())).thenAnswer(
new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
return invocationOnMock.getArgument(0);
}
});
// listOfCarriers length 2, subscriptionIds length 1, anySims false
CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
false,
new int[]{0});
spiedFooter.updateCarrierInfo(c1);
// listOfCarriers length 2, subscriptionIds length 1, anySims true
CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0});
spiedFooter.updateCarrierInfo(c2);
// listOfCarriers length 1, subscriptionIds length 2, anySims false
CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
false,
new int[]{0, 1});
spiedFooter.updateCarrierInfo(c3);
// listOfCarriers length 1, subscriptionIds length 2, anySims true
CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
true,
new int[]{0, 1});
spiedFooter.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testUpdateCarrierText_invalidSim() {
QSFooterImpl spiedFooter = Mockito.spy(mFooter);
when(spiedFooter.getSlotIndex(anyInt())).thenReturn(
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
"",
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
spiedFooter.updateCarrierInfo(c4);
}
@Test // throws no Exception
public void testSetMobileDataIndicators_invalidSim() {
QSFooterImpl spiedFooter = Mockito.spy(mFooter);
when(spiedFooter.getSlotIndex(anyInt())).thenReturn(
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
spiedFooter.setMobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
0, 0, true, true, "", "", true, 0, true);
}
}