1 /* 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 17 package com.android.systemui.qs; 18 19 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.provider.Settings; 24 import android.telephony.SubscriptionManager; 25 import android.util.AttributeSet; 26 import android.util.Log; 27 import android.view.View; 28 import android.widget.LinearLayout; 29 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.keyguard.CarrierTextController; 33 import com.android.systemui.Dependency; 34 import com.android.systemui.R; 35 import com.android.systemui.plugins.ActivityStarter; 36 import com.android.systemui.statusbar.policy.NetworkController; 37 38 import javax.inject.Inject; 39 import javax.inject.Named; 40 41 /** 42 * Displays Carrier name and network status in QS 43 */ 44 public class QSCarrierGroup extends LinearLayout implements 45 CarrierTextController.CarrierTextCallback, 46 NetworkController.SignalCallback, View.OnClickListener { 47 48 private static final String TAG = "QSCarrierGroup"; 49 /** 50 * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount} 51 */ 52 private static final int SIM_SLOTS = 3; 53 private final NetworkController mNetworkController; 54 55 private View[] mCarrierDividers = new View[SIM_SLOTS - 1]; 56 private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS]; 57 private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS]; 58 private CarrierTextController mCarrierTextController; 59 private ActivityStarter mActivityStarter; 60 61 private boolean mListening; 62 63 @Inject QSCarrierGroup(@amedVIEW_CONTEXT) Context context, AttributeSet attrs, NetworkController networkController, ActivityStarter activityStarter)64 public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, 65 NetworkController networkController, ActivityStarter activityStarter) { 66 super(context, attrs); 67 mNetworkController = networkController; 68 mActivityStarter = activityStarter; 69 } 70 71 @VisibleForTesting QSCarrierGroup(Context context, AttributeSet attrs)72 public QSCarrierGroup(Context context, AttributeSet attrs) { 73 this(context, attrs, 74 Dependency.get(NetworkController.class), 75 Dependency.get(ActivityStarter.class)); 76 } 77 78 @Override onClick(View v)79 public void onClick(View v) { 80 if (!v.isVisibleToUser()) return; 81 mActivityStarter.postStartActivityDismissingKeyguard(new Intent( 82 Settings.ACTION_WIRELESS_SETTINGS), 0); 83 } 84 85 @Override onFinishInflate()86 protected void onFinishInflate() { 87 super.onFinishInflate(); 88 89 mCarrierGroups[0] = findViewById(R.id.carrier1); 90 mCarrierGroups[1] = findViewById(R.id.carrier2); 91 mCarrierGroups[2] = findViewById(R.id.carrier3); 92 93 mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1); 94 mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2); 95 96 for (int i = 0; i < SIM_SLOTS; i++) { 97 mInfos[i] = new CellSignalState(); 98 mCarrierGroups[i].setOnClickListener(this); 99 } 100 101 CharSequence separator = mContext.getString( 102 com.android.internal.R.string.kg_text_message_separator); 103 mCarrierTextController = new CarrierTextController(mContext, separator, false, false); 104 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 105 } 106 setListening(boolean listening)107 public void setListening(boolean listening) { 108 if (listening == mListening) { 109 return; 110 } 111 mListening = listening; 112 updateListeners(); 113 } 114 115 @Override 116 @VisibleForTesting onDetachedFromWindow()117 public void onDetachedFromWindow() { 118 setListening(false); 119 super.onDetachedFromWindow(); 120 } 121 updateListeners()122 private void updateListeners() { 123 if (mListening) { 124 if (mNetworkController.hasVoiceCallingFeature()) { 125 mNetworkController.addCallback(this); 126 } 127 mCarrierTextController.setListening(this); 128 } else { 129 mNetworkController.removeCallback(this); 130 mCarrierTextController.setListening(null); 131 } 132 } 133 handleUpdateState()134 private void handleUpdateState() { 135 for (int i = 0; i < SIM_SLOTS; i++) { 136 mCarrierGroups[i].updateState(mInfos[i]); 137 } 138 139 mCarrierDividers[0].setVisibility( 140 mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE); 141 // This tackles the case of slots 2 being available as well as at least one other. 142 // In that case we show the second divider. Note that if both dividers are visible, it means 143 // all three slots are in use, and that is correct. 144 mCarrierDividers[1].setVisibility( 145 (mInfos[1].visible && mInfos[2].visible) 146 || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE); 147 } 148 149 @VisibleForTesting getSlotIndex(int subscriptionId)150 protected int getSlotIndex(int subscriptionId) { 151 return SubscriptionManager.getSlotIndex(subscriptionId); 152 } 153 154 @Override updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info)155 public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { 156 if (info.airplaneMode) { 157 setVisibility(View.GONE); 158 } else { 159 setVisibility(View.VISIBLE); 160 if (info.anySimReady) { 161 boolean[] slotSeen = new boolean[SIM_SLOTS]; 162 if (info.listOfCarriers.length == info.subscriptionIds.length) { 163 for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { 164 int slot = getSlotIndex(info.subscriptionIds[i]); 165 if (slot >= SIM_SLOTS) { 166 Log.w(TAG, "updateInfoCarrier - slot: " + slot); 167 continue; 168 } 169 if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 170 Log.e(TAG, 171 "Invalid SIM slot index for subscription: " 172 + info.subscriptionIds[i]); 173 continue; 174 } 175 mInfos[slot].visible = true; 176 slotSeen[slot] = true; 177 mCarrierGroups[slot].setCarrierText( 178 info.listOfCarriers[i].toString().trim()); 179 mCarrierGroups[slot].setVisibility(View.VISIBLE); 180 } 181 for (int i = 0; i < SIM_SLOTS; i++) { 182 if (!slotSeen[i]) { 183 mInfos[i].visible = false; 184 mCarrierGroups[i].setVisibility(View.GONE); 185 } 186 } 187 } else { 188 Log.e(TAG, "Carrier information arrays not of same length"); 189 } 190 } else { 191 mInfos[0].visible = false; 192 mCarrierGroups[0].setCarrierText(info.carrierText); 193 mCarrierGroups[0].setVisibility(View.VISIBLE); 194 for (int i = 1; i < SIM_SLOTS; i++) { 195 mInfos[i].visible = false; 196 mCarrierGroups[i].setCarrierText(""); 197 mCarrierGroups[i].setVisibility(View.GONE); 198 } 199 } 200 } 201 handleUpdateState(); 202 } 203 204 @Override 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)205 public void setMobileDataIndicators(NetworkController.IconState statusIcon, 206 NetworkController.IconState qsIcon, int statusType, 207 int qsType, boolean activityIn, boolean activityOut, 208 String typeContentDescription, 209 String description, boolean isWide, int subId, boolean roaming) { 210 int slotIndex = getSlotIndex(subId); 211 if (slotIndex >= SIM_SLOTS) { 212 Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); 213 return; 214 } 215 if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 216 Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); 217 return; 218 } 219 mInfos[slotIndex].visible = statusIcon.visible; 220 mInfos[slotIndex].mobileSignalIconId = statusIcon.icon; 221 mInfos[slotIndex].contentDescription = statusIcon.contentDescription; 222 mInfos[slotIndex].typeContentDescription = typeContentDescription; 223 mInfos[slotIndex].roaming = roaming; 224 handleUpdateState(); 225 } 226 227 @Override setNoSims(boolean hasNoSims, boolean simDetected)228 public void setNoSims(boolean hasNoSims, boolean simDetected) { 229 if (hasNoSims) { 230 for (int i = 0; i < SIM_SLOTS; i++) { 231 mInfos[i].visible = false; 232 } 233 } 234 handleUpdateState(); 235 } 236 237 static final class CellSignalState { 238 boolean visible; 239 int mobileSignalIconId; 240 String contentDescription; 241 String typeContentDescription; 242 boolean roaming; 243 } 244 } 245