blob: cca889caf5da6f8902af2a9a2e25b43383e51229 [file] [log] [blame]
Fabian Kozynskia48d2d02019-02-27 11:36:02 -05001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.qs;
18
19import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
20
21import android.content.Context;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040022import android.content.Intent;
23import android.provider.Settings;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050024import android.telephony.SubscriptionManager;
25import android.util.AttributeSet;
26import android.util.Log;
27import android.view.View;
28import android.widget.LinearLayout;
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -040029import android.widget.TextView;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050030
31import androidx.annotation.VisibleForTesting;
32
33import com.android.keyguard.CarrierTextController;
34import com.android.systemui.Dependency;
35import com.android.systemui.R;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040036import com.android.systemui.plugins.ActivityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050037import com.android.systemui.statusbar.policy.NetworkController;
38
39import javax.inject.Inject;
40import javax.inject.Named;
41
42/**
43 * Displays Carrier name and network status in QS
44 */
45public class QSCarrierGroup extends LinearLayout implements
46 CarrierTextController.CarrierTextCallback,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040047 NetworkController.SignalCallback, View.OnClickListener {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050048
49 private static final String TAG = "QSCarrierGroup";
50 /**
51 * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
52 */
53 private static final int SIM_SLOTS = 3;
54 private final NetworkController mNetworkController;
55
56 private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
57 private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -040058 private TextView mNoSimTextView;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050059 private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
60 private CarrierTextController mCarrierTextController;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040061 private ActivityStarter mActivityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050062
63 private boolean mListening;
64
65 @Inject
66 public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040067 NetworkController networkController, ActivityStarter activityStarter) {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050068 super(context, attrs);
69 mNetworkController = networkController;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040070 mActivityStarter = activityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050071 }
72
73 @VisibleForTesting
74 public QSCarrierGroup(Context context, AttributeSet attrs) {
75 this(context, attrs,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040076 Dependency.get(NetworkController.class),
77 Dependency.get(ActivityStarter.class));
78 }
79
80 @Override
81 public void onClick(View v) {
Fabian Kozynski1bdb0b92019-04-26 10:25:30 -040082 if (!v.isVisibleToUser()) return;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040083 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
84 Settings.ACTION_WIRELESS_SETTINGS), 0);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050085 }
86
87 @Override
88 protected void onFinishInflate() {
89 super.onFinishInflate();
90
91 mCarrierGroups[0] = findViewById(R.id.carrier1);
92 mCarrierGroups[1] = findViewById(R.id.carrier2);
93 mCarrierGroups[2] = findViewById(R.id.carrier3);
94
95 mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1);
96 mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2);
97
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -040098 mNoSimTextView = findViewById(R.id.no_carrier_text);
99
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500100 for (int i = 0; i < SIM_SLOTS; i++) {
101 mInfos[i] = new CellSignalState();
Fabian Kozynskib1aee812019-04-04 14:17:48 -0400102 mCarrierGroups[i].setOnClickListener(this);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500103 }
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400104 mNoSimTextView.setOnClickListener(this);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500105
106 CharSequence separator = mContext.getString(
107 com.android.internal.R.string.kg_text_message_separator);
108 mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
109 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
110 }
111
112 public void setListening(boolean listening) {
113 if (listening == mListening) {
114 return;
115 }
116 mListening = listening;
117 updateListeners();
118 }
119
120 @Override
121 @VisibleForTesting
122 public void onDetachedFromWindow() {
123 setListening(false);
124 super.onDetachedFromWindow();
125 }
126
127 private void updateListeners() {
128 if (mListening) {
129 if (mNetworkController.hasVoiceCallingFeature()) {
130 mNetworkController.addCallback(this);
131 }
132 mCarrierTextController.setListening(this);
133 } else {
134 mNetworkController.removeCallback(this);
135 mCarrierTextController.setListening(null);
136 }
137 }
138
139 private void handleUpdateState() {
140 for (int i = 0; i < SIM_SLOTS; i++) {
141 mCarrierGroups[i].updateState(mInfos[i]);
142 }
Fabian Kozynski4e76d1f2019-02-25 16:30:04 -0500143
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500144 mCarrierDividers[0].setVisibility(
145 mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
146 // This tackles the case of slots 2 being available as well as at least one other.
147 // In that case we show the second divider. Note that if both dividers are visible, it means
148 // all three slots are in use, and that is correct.
149 mCarrierDividers[1].setVisibility(
150 (mInfos[1].visible && mInfos[2].visible)
151 || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
152 }
153
154 @VisibleForTesting
155 protected int getSlotIndex(int subscriptionId) {
156 return SubscriptionManager.getSlotIndex(subscriptionId);
157 }
158
159 @Override
160 public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400161 mNoSimTextView.setVisibility(View.GONE);
162 if (!info.airplaneMode && info.anySimReady) {
163 boolean[] slotSeen = new boolean[SIM_SLOTS];
164 if (info.listOfCarriers.length == info.subscriptionIds.length) {
165 for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
166 int slot = getSlotIndex(info.subscriptionIds[i]);
167 if (slot >= SIM_SLOTS) {
168 Log.w(TAG, "updateInfoCarrier - slot: " + slot);
169 continue;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500170 }
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400171 if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
172 Log.e(TAG,
173 "Invalid SIM slot index for subscription: "
174 + info.subscriptionIds[i]);
175 continue;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500176 }
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400177 mInfos[slot].visible = true;
178 slotSeen[slot] = true;
179 mCarrierGroups[slot].setCarrierText(
180 info.listOfCarriers[i].toString().trim());
181 mCarrierGroups[slot].setVisibility(View.VISIBLE);
182 }
183 for (int i = 0; i < SIM_SLOTS; i++) {
184 if (!slotSeen[i]) {
185 mInfos[i].visible = false;
186 mCarrierGroups[i].setVisibility(View.GONE);
187 }
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500188 }
189 } else {
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400190 Log.e(TAG, "Carrier information arrays not of same length");
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500191 }
Fabian Kozynski4e63d9e2019-08-29 09:47:07 -0400192 } else {
193 // No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
194 // info.carrierText in a different view.
195 for (int i = 0; i < SIM_SLOTS; i++) {
196 mInfos[i].visible = false;
197 mCarrierGroups[i].setCarrierText("");
198 mCarrierGroups[i].setVisibility(View.GONE);
199 }
200 mNoSimTextView.setText(info.carrierText);
201 mNoSimTextView.setVisibility(View.VISIBLE);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500202 }
203 handleUpdateState();
204 }
205
206 @Override
207 public void setMobileDataIndicators(NetworkController.IconState statusIcon,
208 NetworkController.IconState qsIcon, int statusType,
209 int qsType, boolean activityIn, boolean activityOut,
Evan Laird83c87e52019-09-24 19:14:05 -0400210 CharSequence typeContentDescription,
211 CharSequence typeContentDescriptionHtml, CharSequence description,
212 boolean isWide, int subId, boolean roaming) {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500213 int slotIndex = getSlotIndex(subId);
214 if (slotIndex >= SIM_SLOTS) {
215 Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
216 return;
217 }
218 if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
219 Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
220 return;
221 }
222 mInfos[slotIndex].visible = statusIcon.visible;
223 mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
224 mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
Evan Laird83c87e52019-09-24 19:14:05 -0400225 mInfos[slotIndex].typeContentDescription = typeContentDescription.toString();
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500226 mInfos[slotIndex].roaming = roaming;
227 handleUpdateState();
228 }
229
230 @Override
231 public void setNoSims(boolean hasNoSims, boolean simDetected) {
232 if (hasNoSims) {
233 for (int i = 0; i < SIM_SLOTS; i++) {
234 mInfos[i].visible = false;
235 }
236 }
237 handleUpdateState();
238 }
239
240 static final class CellSignalState {
241 boolean visible;
242 int mobileSignalIconId;
243 String contentDescription;
244 String typeContentDescription;
245 boolean roaming;
246 }
247}