blob: 5742787b39bcabaf5e3ae432b2bc45864ced398e [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
Fabian Kozynski7b926722019-09-09 12:15:24 -040019import static com.android.systemui.Dependency.BG_HANDLER;
Fabian Kozynskibeeec062019-09-24 10:34:00 -040020import static com.android.systemui.Dependency.MAIN_LOOPER;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050021import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
22
Fabian Kozynskibeeec062019-09-24 10:34:00 -040023import android.annotation.MainThread;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050024import android.content.Context;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040025import android.content.Intent;
Fabian Kozynski7b926722019-09-09 12:15:24 -040026import android.os.Handler;
Fabian Kozynskibeeec062019-09-24 10:34:00 -040027import android.os.Looper;
28import android.os.Message;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040029import android.provider.Settings;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050030import android.telephony.SubscriptionManager;
31import android.util.AttributeSet;
32import android.util.Log;
33import android.view.View;
34import android.widget.LinearLayout;
Fabian Kozynski0f49f202019-08-29 09:47:07 -040035import android.widget.TextView;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050036
37import androidx.annotation.VisibleForTesting;
38
39import com.android.keyguard.CarrierTextController;
40import com.android.systemui.Dependency;
41import com.android.systemui.R;
Dave Mankofff4736812019-10-18 17:25:50 -040042import com.android.systemui.dagger.qualifiers.BgHandler;
43import com.android.systemui.dagger.qualifiers.MainLooper;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040044import com.android.systemui.plugins.ActivityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050045import com.android.systemui.statusbar.policy.NetworkController;
46
Fabian Kozynskibeeec062019-09-24 10:34:00 -040047import java.util.function.Consumer;
48
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050049import javax.inject.Inject;
50import javax.inject.Named;
51
52/**
53 * Displays Carrier name and network status in QS
54 */
55public class QSCarrierGroup extends LinearLayout implements
Fabian Kozynskib1aee812019-04-04 14:17:48 -040056 NetworkController.SignalCallback, View.OnClickListener {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050057
58 private static final String TAG = "QSCarrierGroup";
59 /**
60 * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
61 */
62 private static final int SIM_SLOTS = 3;
63 private final NetworkController mNetworkController;
Fabian Kozynski7b926722019-09-09 12:15:24 -040064 private final Handler mBgHandler;
Fabian Kozynskibeeec062019-09-24 10:34:00 -040065 private final H mMainHandler;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050066
67 private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
68 private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
Fabian Kozynski0f49f202019-08-29 09:47:07 -040069 private TextView mNoSimTextView;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050070 private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
71 private CarrierTextController mCarrierTextController;
Fabian Kozynskibeeec062019-09-24 10:34:00 -040072 private CarrierTextController.CarrierTextCallback mCallback;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040073 private ActivityStarter mActivityStarter;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050074
75 private boolean mListening;
76
77 @Inject
78 public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
Fabian Kozynski7b926722019-09-09 12:15:24 -040079 NetworkController networkController, ActivityStarter activityStarter,
Dave Mankofff4736812019-10-18 17:25:50 -040080 @BgHandler Handler handler,
81 @MainLooper Looper looper) {
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050082 super(context, attrs);
83 mNetworkController = networkController;
Fabian Kozynskib1aee812019-04-04 14:17:48 -040084 mActivityStarter = activityStarter;
Fabian Kozynski7b926722019-09-09 12:15:24 -040085 mBgHandler = handler;
Fabian Kozynskibeeec062019-09-24 10:34:00 -040086 mMainHandler = new H(looper, this::handleUpdateCarrierInfo, this::handleUpdateState);
87 mCallback = new Callback(mMainHandler);
88 }
89
90 @VisibleForTesting
91 protected CarrierTextController.CarrierTextCallback getCallback() {
92 return mCallback;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -050093 }
94
95 @VisibleForTesting
96 public QSCarrierGroup(Context context, AttributeSet attrs) {
97 this(context, attrs,
Fabian Kozynskib1aee812019-04-04 14:17:48 -040098 Dependency.get(NetworkController.class),
Fabian Kozynski7b926722019-09-09 12:15:24 -040099 Dependency.get(ActivityStarter.class),
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400100 Dependency.get(BG_HANDLER),
101 Dependency.get(MAIN_LOOPER));
Fabian Kozynskib1aee812019-04-04 14:17:48 -0400102 }
103
104 @Override
105 public void onClick(View v) {
Fabian Kozynski1bdb0b92019-04-26 10:25:30 -0400106 if (!v.isVisibleToUser()) return;
Fabian Kozynskib1aee812019-04-04 14:17:48 -0400107 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
108 Settings.ACTION_WIRELESS_SETTINGS), 0);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500109 }
110
111 @Override
112 protected void onFinishInflate() {
113 super.onFinishInflate();
114
115 mCarrierGroups[0] = findViewById(R.id.carrier1);
116 mCarrierGroups[1] = findViewById(R.id.carrier2);
117 mCarrierGroups[2] = findViewById(R.id.carrier3);
118
119 mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1);
120 mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2);
121
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400122 mNoSimTextView = findViewById(R.id.no_carrier_text);
123
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500124 for (int i = 0; i < SIM_SLOTS; i++) {
125 mInfos[i] = new CellSignalState();
Fabian Kozynskib1aee812019-04-04 14:17:48 -0400126 mCarrierGroups[i].setOnClickListener(this);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500127 }
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400128 mNoSimTextView.setOnClickListener(this);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500129
130 CharSequence separator = mContext.getString(
131 com.android.internal.R.string.kg_text_message_separator);
132 mCarrierTextController = new CarrierTextController(mContext, separator, false, false);
133 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
134 }
135
136 public void setListening(boolean listening) {
137 if (listening == mListening) {
138 return;
139 }
140 mListening = listening;
Fabian Kozynski7b926722019-09-09 12:15:24 -0400141 mBgHandler.post(this::updateListeners);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500142 }
143
144 @Override
145 @VisibleForTesting
146 public void onDetachedFromWindow() {
147 setListening(false);
148 super.onDetachedFromWindow();
149 }
150
151 private void updateListeners() {
152 if (mListening) {
153 if (mNetworkController.hasVoiceCallingFeature()) {
154 mNetworkController.addCallback(this);
155 }
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400156 mCarrierTextController.setListening(mCallback);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500157 } else {
158 mNetworkController.removeCallback(this);
159 mCarrierTextController.setListening(null);
160 }
161 }
162
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400163 @MainThread
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500164 private void handleUpdateState() {
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400165 if (!mMainHandler.getLooper().isCurrentThread()) {
166 mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
167 return;
168 }
169
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500170 for (int i = 0; i < SIM_SLOTS; i++) {
171 mCarrierGroups[i].updateState(mInfos[i]);
172 }
Fabian Kozynski4e76d1f2019-02-25 16:30:04 -0500173
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500174 mCarrierDividers[0].setVisibility(
175 mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
176 // This tackles the case of slots 2 being available as well as at least one other.
177 // In that case we show the second divider. Note that if both dividers are visible, it means
178 // all three slots are in use, and that is correct.
179 mCarrierDividers[1].setVisibility(
180 (mInfos[1].visible && mInfos[2].visible)
181 || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
182 }
183
184 @VisibleForTesting
185 protected int getSlotIndex(int subscriptionId) {
186 return SubscriptionManager.getSlotIndex(subscriptionId);
187 }
188
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400189 @MainThread
190 private void handleUpdateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
191 if (!mMainHandler.getLooper().isCurrentThread()) {
192 mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
193 return;
194 }
195
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400196 mNoSimTextView.setVisibility(View.GONE);
197 if (!info.airplaneMode && info.anySimReady) {
198 boolean[] slotSeen = new boolean[SIM_SLOTS];
199 if (info.listOfCarriers.length == info.subscriptionIds.length) {
200 for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
201 int slot = getSlotIndex(info.subscriptionIds[i]);
202 if (slot >= SIM_SLOTS) {
203 Log.w(TAG, "updateInfoCarrier - slot: " + slot);
204 continue;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500205 }
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400206 if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
207 Log.e(TAG,
208 "Invalid SIM slot index for subscription: "
209 + info.subscriptionIds[i]);
210 continue;
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500211 }
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400212 mInfos[slot].visible = true;
213 slotSeen[slot] = true;
214 mCarrierGroups[slot].setCarrierText(
215 info.listOfCarriers[i].toString().trim());
216 mCarrierGroups[slot].setVisibility(View.VISIBLE);
217 }
218 for (int i = 0; i < SIM_SLOTS; i++) {
219 if (!slotSeen[i]) {
220 mInfos[i].visible = false;
221 mCarrierGroups[i].setVisibility(View.GONE);
222 }
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500223 }
224 } else {
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400225 Log.e(TAG, "Carrier information arrays not of same length");
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500226 }
Fabian Kozynski0f49f202019-08-29 09:47:07 -0400227 } else {
228 // No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
229 // info.carrierText in a different view.
230 for (int i = 0; i < SIM_SLOTS; i++) {
231 mInfos[i].visible = false;
232 mCarrierGroups[i].setCarrierText("");
233 mCarrierGroups[i].setVisibility(View.GONE);
234 }
235 mNoSimTextView.setText(info.carrierText);
236 mNoSimTextView.setVisibility(View.VISIBLE);
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500237 }
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400238 handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500239 }
240
241 @Override
242 public void setMobileDataIndicators(NetworkController.IconState statusIcon,
243 NetworkController.IconState qsIcon, int statusType,
244 int qsType, boolean activityIn, boolean activityOut,
245 String typeContentDescription,
246 String description, boolean isWide, int subId, boolean roaming) {
247 int slotIndex = getSlotIndex(subId);
248 if (slotIndex >= SIM_SLOTS) {
249 Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
250 return;
251 }
252 if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
253 Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
254 return;
255 }
256 mInfos[slotIndex].visible = statusIcon.visible;
257 mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
258 mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
259 mInfos[slotIndex].typeContentDescription = typeContentDescription;
260 mInfos[slotIndex].roaming = roaming;
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400261 mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500262 }
263
264 @Override
265 public void setNoSims(boolean hasNoSims, boolean simDetected) {
266 if (hasNoSims) {
267 for (int i = 0; i < SIM_SLOTS; i++) {
268 mInfos[i].visible = false;
269 }
270 }
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400271 mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500272 }
273
274 static final class CellSignalState {
275 boolean visible;
276 int mobileSignalIconId;
277 String contentDescription;
278 String typeContentDescription;
279 boolean roaming;
280 }
Fabian Kozynskibeeec062019-09-24 10:34:00 -0400281
282 private static class H extends Handler {
283 private Consumer<CarrierTextController.CarrierTextCallbackInfo> mUpdateCarrierInfo;
284 private Runnable mUpdateState;
285 static final int MSG_UPDATE_CARRIER_INFO = 0;
286 static final int MSG_UPDATE_STATE = 1;
287
288 H(Looper looper,
289 Consumer<CarrierTextController.CarrierTextCallbackInfo> updateCarrierInfo,
290 Runnable updateState) {
291 super(looper);
292 mUpdateCarrierInfo = updateCarrierInfo;
293 mUpdateState = updateState;
294 }
295
296 @Override
297 public void handleMessage(Message msg) {
298 switch (msg.what) {
299 case MSG_UPDATE_CARRIER_INFO:
300 mUpdateCarrierInfo.accept(
301 (CarrierTextController.CarrierTextCallbackInfo) msg.obj);
302 break;
303 case MSG_UPDATE_STATE:
304 mUpdateState.run();
305 break;
306 default:
307 super.handleMessage(msg);
308 }
309 }
310 }
311
312 private static class Callback implements CarrierTextController.CarrierTextCallback {
313 private H mMainHandler;
314
315 Callback(H handler) {
316 mMainHandler = handler;
317 }
318
319 @Override
320 public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
321 mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
322 }
323 }
Fabian Kozynskia48d2d02019-02-27 11:36:02 -0500324}