blob: 8c21dde8ad032152744eba359a61a02f77c16975 [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
Lucas Dupin8968f6a2019-08-09 17:41:15 -070019import static com.android.systemui.DejankUtils.whitelistIpcs;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050020import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
21
22import android.content.Context;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040023import android.content.Intent;
24import android.provider.Settings;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050025import android.telephony.SubscriptionManager;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.view.View;
29import android.widget.LinearLayout;
30
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];
58 private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
59 private CarrierTextController mCarrierTextController;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040060 private ActivityStarter mActivityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050061
62 private boolean mListening;
63
64 @Inject
65 public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040066 NetworkController networkController, ActivityStarter activityStarter) {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050067 super(context, attrs);
68 mNetworkController = networkController;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040069 mActivityStarter = activityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050070 }
71
72 @VisibleForTesting
73 public QSCarrierGroup(Context context, AttributeSet attrs) {
74 this(context, attrs,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040075 Dependency.get(NetworkController.class),
76 Dependency.get(ActivityStarter.class));
77 }
78
79 @Override
80 public void onClick(View v) {
Fabian Kozynski1bdb0b92019-04-26 10:25:30 -040081 if (!v.isVisibleToUser()) return;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040082 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
83 Settings.ACTION_WIRELESS_SETTINGS), 0);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050084 }
85
86 @Override
87 protected void onFinishInflate() {
88 super.onFinishInflate();
89
90 mCarrierGroups[0] = findViewById(R.id.carrier1);
91 mCarrierGroups[1] = findViewById(R.id.carrier2);
92 mCarrierGroups[2] = findViewById(R.id.carrier3);
93
94 mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1);
95 mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2);
96
97 for (int i = 0; i < SIM_SLOTS; i++) {
98 mInfos[i] = new CellSignalState();
Fabian Kozynskib1aee812019-04-04 14:17:48 -040099 mCarrierGroups[i].setOnClickListener(this);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500100 }
101
102 CharSequence separator = mContext.getString(
103 com.android.internal.R.string.kg_text_message_separator);
104 mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
105 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
106 }
107
108 public void setListening(boolean listening) {
109 if (listening == mListening) {
110 return;
111 }
112 mListening = listening;
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700113 // TODO(b/140053526)
114 whitelistIpcs(this::updateListeners);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500115 }
116
117 @Override
118 @VisibleForTesting
119 public void onDetachedFromWindow() {
120 setListening(false);
121 super.onDetachedFromWindow();
122 }
123
124 private void updateListeners() {
125 if (mListening) {
126 if (mNetworkController.hasVoiceCallingFeature()) {
127 mNetworkController.addCallback(this);
128 }
129 mCarrierTextController.setListening(this);
130 } else {
131 mNetworkController.removeCallback(this);
132 mCarrierTextController.setListening(null);
133 }
134 }
135
136 private void handleUpdateState() {
137 for (int i = 0; i < SIM_SLOTS; i++) {
138 mCarrierGroups[i].updateState(mInfos[i]);
139 }
Fabian Kozynski4e76d1f2019-02-25 16:30:04 -0500140
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500141 mCarrierDividers[0].setVisibility(
142 mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
143 // This tackles the case of slots 2 being available as well as at least one other.
144 // In that case we show the second divider. Note that if both dividers are visible, it means
145 // all three slots are in use, and that is correct.
146 mCarrierDividers[1].setVisibility(
147 (mInfos[1].visible && mInfos[2].visible)
148 || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
149 }
150
151 @VisibleForTesting
152 protected int getSlotIndex(int subscriptionId) {
153 return SubscriptionManager.getSlotIndex(subscriptionId);
154 }
155
156 @Override
157 public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400158 if (info.airplaneMode) {
159 setVisibility(View.GONE);
160 } else {
161 setVisibility(View.VISIBLE);
162 if (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;
170 }
171 if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
172 Log.e(TAG,
173 "Invalid SIM slot index for subscription: "
174 + info.subscriptionIds[i]);
175 continue;
176 }
177 mInfos[slot].visible = true;
178 slotSeen[slot] = true;
179 mCarrierGroups[slot].setCarrierText(
180 info.listOfCarriers[i].toString().trim());
181 mCarrierGroups[slot].setVisibility(View.VISIBLE);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500182 }
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400183 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 }
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400189 } else {
190 Log.e(TAG, "Carrier information arrays not of same length");
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500191 }
192 } else {
Fabian Kozynskib38edbb2019-04-12 12:20:13 -0400193 mInfos[0].visible = false;
194 mCarrierGroups[0].setCarrierText(info.carrierText);
195 mCarrierGroups[0].setVisibility(View.VISIBLE);
196 for (int i = 1; i < SIM_SLOTS; i++) {
197 mInfos[i].visible = false;
198 mCarrierGroups[i].setCarrierText("");
199 mCarrierGroups[i].setVisibility(View.GONE);
200 }
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500201 }
202 }
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,
210 String typeContentDescription,
211 String description, boolean isWide, int subId, boolean roaming) {
212 int slotIndex = getSlotIndex(subId);
213 if (slotIndex >= SIM_SLOTS) {
214 Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
215 return;
216 }
217 if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
218 Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
219 return;
220 }
221 mInfos[slotIndex].visible = statusIcon.visible;
222 mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
223 mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
224 mInfos[slotIndex].typeContentDescription = typeContentDescription;
225 mInfos[slotIndex].roaming = roaming;
226 handleUpdateState();
227 }
228
229 @Override
230 public void setNoSims(boolean hasNoSims, boolean simDetected) {
231 if (hasNoSims) {
232 for (int i = 0; i < SIM_SLOTS; i++) {
233 mInfos[i].visible = false;
234 }
235 }
236 handleUpdateState();
237 }
238
239 static final class CellSignalState {
240 boolean visible;
241 int mobileSignalIconId;
242 String contentDescription;
243 String typeContentDescription;
244 boolean roaming;
245 }
246}