Home | History | Annotate | Download | only in qs
      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
     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
     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
     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
     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 
    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         }
    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
    150     protected int getSlotIndex(int subscriptionId) {
    151         return SubscriptionManager.getSlotIndex(subscriptionId);
    152     }
    153 
    154     @Override
    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
    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 }
    245