| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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; | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 22 | import android.content.Intent; | 
|  | 23 | import android.provider.Settings; | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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; | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 35 | import com.android.systemui.plugins.ActivityStarter; | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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, | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 46 | NetworkController.SignalCallback, View.OnClickListener { | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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; | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 59 | private ActivityStarter mActivityStarter; | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 60 |  | 
|  | 61 | private boolean mListening; | 
|  | 62 |  | 
|  | 63 | @Inject | 
|  | 64 | public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 65 | NetworkController networkController, ActivityStarter activityStarter) { | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 66 | super(context, attrs); | 
|  | 67 | mNetworkController = networkController; | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 68 | mActivityStarter = activityStarter; | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 69 | } | 
|  | 70 |  | 
|  | 71 | @VisibleForTesting | 
|  | 72 | public QSCarrierGroup(Context context, AttributeSet attrs) { | 
|  | 73 | this(context, attrs, | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 74 | Dependency.get(NetworkController.class), | 
|  | 75 | Dependency.get(ActivityStarter.class)); | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | @Override | 
|  | 79 | public void onClick(View v) { | 
| Fabian Kozynski | 1bdb0b9 | 2019-04-26 10:25:30 -0400 | [diff] [blame] | 80 | if (!v.isVisibleToUser()) return; | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 81 | mActivityStarter.postStartActivityDismissingKeyguard(new Intent( | 
|  | 82 | Settings.ACTION_WIRELESS_SETTINGS), 0); | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
|  | 85 | @Override | 
|  | 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(); | 
| Fabian Kozynski | b1aee81 | 2019-04-04 14:17:48 -0400 | [diff] [blame] | 98 | mCarrierGroups[i].setOnClickListener(this); | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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 |  | 
|  | 107 | public void setListening(boolean listening) { | 
|  | 108 | if (listening == mListening) { | 
|  | 109 | return; | 
|  | 110 | } | 
|  | 111 | mListening = listening; | 
|  | 112 | updateListeners(); | 
|  | 113 | } | 
|  | 114 |  | 
|  | 115 | @Override | 
|  | 116 | @VisibleForTesting | 
|  | 117 | public void onDetachedFromWindow() { | 
|  | 118 | setListening(false); | 
|  | 119 | super.onDetachedFromWindow(); | 
|  | 120 | } | 
|  | 121 |  | 
|  | 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 |  | 
|  | 134 | private void handleUpdateState() { | 
|  | 135 | for (int i = 0; i < SIM_SLOTS; i++) { | 
|  | 136 | mCarrierGroups[i].updateState(mInfos[i]); | 
|  | 137 | } | 
| Fabian Kozynski | 4e76d1f | 2019-02-25 16:30:04 -0500 | [diff] [blame] | 138 |  | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 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 | 
|  | 150 | protected int getSlotIndex(int subscriptionId) { | 
|  | 151 | return SubscriptionManager.getSlotIndex(subscriptionId); | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | @Override | 
|  | 155 | public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { | 
| Fabian Kozynski | b38edbb | 2019-04-12 12:20:13 -0400 | [diff] [blame] | 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); | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 180 | } | 
| Fabian Kozynski | b38edbb | 2019-04-12 12:20:13 -0400 | [diff] [blame] | 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 | } | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 186 | } | 
| Fabian Kozynski | b38edbb | 2019-04-12 12:20:13 -0400 | [diff] [blame] | 187 | } else { | 
|  | 188 | Log.e(TAG, "Carrier information arrays not of same length"); | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 189 | } | 
|  | 190 | } else { | 
| Fabian Kozynski | b38edbb | 2019-04-12 12:20:13 -0400 | [diff] [blame] | 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 | } | 
| Fabian Kozynski | a48d2d0 | 2019-02-27 11:36:02 -0500 | [diff] [blame] | 199 | } | 
|  | 200 | } | 
|  | 201 | handleUpdateState(); | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | @Override | 
|  | 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 | 
|  | 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 | } |