Extracts carrier information from QSFooterImpl
QSCarrierGroup performs the same logic and visuals that were inside
QSFooterImpl
Extracted to QSCarrier
Supports up to 3 sims
Test: atest QSFooterImpl
Test: atest QSCarrierGroup
Test: manual
Change-Id: I44e6f917e176af7f305a1de235e1a5be52da579c
diff --git a/packages/SystemUI/res/layout/qs_footer_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
similarity index 90%
rename from packages/SystemUI/res/layout/qs_footer_carrier.xml
rename to packages/SystemUI/res/layout/qs_carrier.xml
index bd492b0..089a7e7 100644
--- a/packages/SystemUI/res/layout/qs_footer_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -14,9 +14,9 @@
~ 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:id="@+id/linear_carrier"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="horizontal"
@@ -35,7 +35,7 @@
android:layout_marginEnd="8dp"
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_height="wrap_content"
@@ -46,4 +46,4 @@
android:textDirection="locale"
android:singleLine="true" />
-</LinearLayout>
\ No newline at end of file
+</com.android.systemui.qs.QSCarrier>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
new file mode 100644
index 0000000..1303562
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -0,0 +1,60 @@
+<!--
+ 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="center_vertical|start"
+ android:orientation="horizontal"
+ android:layout_marginEnd="32dp">
+
+ <include
+ layout="@layout/qs_carrier"
+ android:id="@+id/carrier1" />
+
+ <View
+ android:id="@+id/qs_carrier_divider1"
+ android:layout_width="2dp"
+ 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_carrier"
+ android:id="@+id/carrier2"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/qs_carrier_divider2"
+ android:layout_width="2dp"
+ 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_carrier"
+ android:id="@+id/carrier3"
+ android:visibility="gone"/>
+
+</com.android.systemui.qs.QSCarrierGroup>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 67e31ac..fb2a343 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -41,34 +41,8 @@
android:layout_gravity="center_vertical"
android:gravity="end" >
- <LinearLayout
- android:id="@+id/qs_mobile"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical|start"
- android:orientation="horizontal"
- android:layout_marginEnd="32dp">
-
- <include
- layout="@layout/qs_footer_carrier"
- android:id="@+id/carrier1" />
-
- <View
- android:id="@+id/qs_carrier_divider"
- android:layout_width="2dp"
- 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:visibility="gone"/>
-
- </LinearLayout>
+ <include layout="@layout/qs_carrier_group"
+ android:id="@+id/carrier_group"/>
<com.android.systemui.qs.PageIndicator
android:id="@+id/footer_page_indicator"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
new file mode 100644
index 0000000..ec842f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
@@ -0,0 +1,140 @@
+/*
+ * 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 TextView 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);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
new file mode 100644
index 0000000..9b625ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -0,0 +1,223 @@
+/*
+ * 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;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index bbd8de3..dcb33e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -23,25 +23,19 @@
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.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Bundle;
import android.os.UserManager;
-import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
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;
import androidx.annotation.Nullable;
@@ -49,11 +43,9 @@
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.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 +54,6 @@
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 +62,12 @@
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 +80,7 @@
private boolean mListening;
- private boolean mShowEmergencyCallsOnly;
+ private QSCarrierGroup mCarrierGroup;
private View mDivider;
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
@@ -108,30 +94,15 @@
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;
@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 +111,6 @@
this(context, attrs,
Dependency.get(ActivityStarter.class),
Dependency.get(UserInfoController.class),
- Dependency.get(NetworkController.class),
Dependency.get(DeviceProvisionedController.class));
}
@@ -159,20 +129,7 @@
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);
+ mCarrierGroup = findViewById(R.id.carrier_group);
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
@@ -240,8 +197,7 @@
private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
.addFloat(mDivider, "alpha", 0, 1)
- .addFloat(mMobileFooter, "alpha", 0, 0, 1)
- .addFloat(mCarrierDivider, "alpha", 0, 1)
+ .addFloat(mCarrierGroup, "alpha", 0, 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mDragHandle, "alpha", 1, 0, 0)
.addFloat(mPageIndicator, "alpha", 0, 1)
@@ -364,17 +320,10 @@
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);
}
+ mCarrierGroup.setListening(mListening);
}
@Override
@@ -430,17 +379,6 @@
}
@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 &&
UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
@@ -452,162 +390,4 @@
}
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);
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index c6636b4..e6b6672 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -24,6 +24,7 @@
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 @@
* Creates the NotificationPanelView.
*/
NotificationPanelView createPanelView();
+
+ /**
+ * Creates the QSCarrierGroup
+ */
+ QSCarrierGroup createQSCarrierGroup();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
new file mode 100644
index 0000000..f29392b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
@@ -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);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
index a8b169c..d4688d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
@@ -16,13 +16,10 @@
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 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 @@
// 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);
- }
-
}