Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2018 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.statusbar.phone;
     18 
     19 import android.content.Context;
     20 import android.content.res.ColorStateList;
     21 import android.graphics.Rect;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.telephony.SubscriptionInfo;
     25 import android.util.ArraySet;
     26 import android.util.Log;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.view.accessibility.AccessibilityEvent;
     31 import android.widget.ImageView;
     32 import com.android.settingslib.graph.SignalDrawable;
     33 import com.android.systemui.Dependency;
     34 import com.android.systemui.R;
     35 import com.android.systemui.statusbar.phone.StatusBarIconController;
     36 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
     37 import com.android.systemui.statusbar.policy.NetworkController;
     38 import com.android.systemui.statusbar.policy.NetworkController.IconState;
     39 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
     40 import com.android.systemui.statusbar.policy.SecurityController;
     41 import com.android.systemui.tuner.TunerService.Tunable;
     42 import java.util.ArrayList;
     43 import java.util.List;
     44 import java.util.Objects;
     45 
     46 
     47 public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
     48         SecurityController.SecurityControllerCallback, Tunable {
     49     private static final String TAG = "StatusBarSignalPolicy";
     50 
     51     private final String mSlotAirplane;
     52     private final String mSlotMobile;
     53     private final String mSlotWifi;
     54     private final String mSlotEthernet;
     55     private final String mSlotVpn;
     56 
     57     private final Context mContext;
     58     private final StatusBarIconController mIconController;
     59     private final NetworkController mNetworkController;
     60     private final SecurityController mSecurityController;
     61     private final Handler mHandler = Handler.getMain();
     62 
     63     private boolean mBlockAirplane;
     64     private boolean mBlockMobile;
     65     private boolean mBlockWifi;
     66     private boolean mBlockEthernet;
     67     private boolean mActivityEnabled;
     68     private boolean mForceBlockWifi;
     69 
     70     // Track as little state as possible, and only for padding purposes
     71     private boolean mIsAirplaneMode = false;
     72     private boolean mWifiVisible = false;
     73 
     74     private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
     75     private WifiIconState mWifiIconState = new WifiIconState();
     76 
     77     public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
     78         mContext = context;
     79 
     80         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
     81         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
     82         mSlotWifi     = mContext.getString(com.android.internal.R.string.status_bar_wifi);
     83         mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
     84         mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
     85         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
     86 
     87         mIconController = iconController;
     88         mNetworkController = Dependency.get(NetworkController.class);
     89         mSecurityController = Dependency.get(SecurityController.class);
     90 
     91         mNetworkController.addCallback(this);
     92         mSecurityController.addCallback(this);
     93     }
     94 
     95     public void destroy() {
     96         mNetworkController.removeCallback(this);
     97         mSecurityController.removeCallback(this);
     98     }
     99 
    100     private void updateVpn() {
    101         boolean vpnVisible = mSecurityController.isVpnEnabled();
    102         int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
    103 
    104         mIconController.setIcon(mSlotVpn, vpnIconId, null);
    105         mIconController.setIconVisibility(mSlotVpn, vpnVisible);
    106     }
    107 
    108     private int currentVpnIconId(boolean isBranded) {
    109         return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
    110     }
    111 
    112     /**
    113      * From SecurityController
    114      */
    115     @Override
    116     public void onStateChanged() {
    117         mHandler.post(this::updateVpn);
    118     }
    119 
    120     @Override
    121     public void onTuningChanged(String key, String newValue) {
    122         if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
    123             return;
    124         }
    125         ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
    126         boolean blockAirplane = blockList.contains(mSlotAirplane);
    127         boolean blockMobile = blockList.contains(mSlotMobile);
    128         boolean blockWifi = blockList.contains(mSlotWifi);
    129         boolean blockEthernet = blockList.contains(mSlotEthernet);
    130 
    131         if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
    132                 || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
    133             mBlockAirplane = blockAirplane;
    134             mBlockMobile = blockMobile;
    135             mBlockEthernet = blockEthernet;
    136             mBlockWifi = blockWifi || mForceBlockWifi;
    137             // Re-register to get new callbacks.
    138             mNetworkController.removeCallback(this);
    139         }
    140     }
    141 
    142     @Override
    143     public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
    144             boolean activityIn, boolean activityOut, String description, boolean isTransient,
    145             String statusLabel) {
    146 
    147         boolean visible = statusIcon.visible && !mBlockWifi;
    148         boolean in = activityIn && mActivityEnabled && visible;
    149         boolean out = activityOut && mActivityEnabled && visible;
    150 
    151         WifiIconState newState = mWifiIconState.copy();
    152 
    153         newState.visible = visible;
    154         newState.resId = statusIcon.icon;
    155         newState.activityIn = in;
    156         newState.activityOut = out;
    157         newState.slot = mSlotWifi;
    158         newState.airplaneSpacerVisible = mIsAirplaneMode;
    159         newState.contentDescription = statusIcon.contentDescription;
    160 
    161         MobileIconState first = getFirstMobileState();
    162         newState.signalSpacerVisible = first != null && first.typeId != 0;
    163 
    164         updateWifiIconWithState(newState);
    165         mWifiIconState = newState;
    166     }
    167 
    168     private void updateShowWifiSignalSpacer(WifiIconState state) {
    169         MobileIconState first = getFirstMobileState();
    170         state.signalSpacerVisible = first != null && first.typeId != 0;
    171     }
    172 
    173     private void updateWifiIconWithState(WifiIconState state) {
    174         if (state.visible && state.resId > 0) {
    175             mIconController.setSignalIcon(mSlotWifi, state);
    176             mIconController.setIconVisibility(mSlotWifi, true);
    177         } else {
    178             mIconController.setIconVisibility(mSlotWifi, false);
    179         }
    180     }
    181 
    182     @Override
    183     public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
    184             int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
    185             String description, boolean isWide, int subId, boolean roaming) {
    186         MobileIconState state = getState(subId);
    187         if (state == null) {
    188             return;
    189         }
    190 
    191         // Visibility of the data type indicator changed
    192         boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
    193 
    194         state.visible = statusIcon.visible && !mBlockMobile;
    195         state.strengthId = statusIcon.icon;
    196         state.typeId = statusType;
    197         state.contentDescription = statusIcon.contentDescription;
    198         state.typeContentDescription = typeContentDescription;
    199         state.roaming = roaming;
    200         state.activityIn = activityIn && mActivityEnabled;
    201         state.activityOut = activityOut && mActivityEnabled;
    202 
    203         // Always send a copy to maintain value type semantics
    204         mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
    205 
    206         if (typeChanged) {
    207             WifiIconState wifiCopy = mWifiIconState.copy();
    208             updateShowWifiSignalSpacer(wifiCopy);
    209             if (!Objects.equals(wifiCopy, mWifiIconState)) {
    210                 updateWifiIconWithState(wifiCopy);
    211                 mWifiIconState = wifiCopy;
    212             }
    213         }
    214     }
    215 
    216     private MobileIconState getState(int subId) {
    217         for (MobileIconState state : mMobileStates) {
    218             if (state.subId == subId) {
    219                 return state;
    220             }
    221         }
    222         Log.e(TAG, "Unexpected subscription " + subId);
    223         return null;
    224     }
    225 
    226     private MobileIconState getFirstMobileState() {
    227         if (mMobileStates.size() > 0) {
    228             return mMobileStates.get(0);
    229         }
    230 
    231         return null;
    232     }
    233 
    234 
    235     /**
    236      * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
    237      * so we don't have to update the icon manager at this point, just remove the old ones
    238      * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
    239      */
    240     @Override
    241     public void setSubs(List<SubscriptionInfo> subs) {
    242         if (hasCorrectSubs(subs)) {
    243             return;
    244         }
    245 
    246         mIconController.removeAllIconsForSlot(mSlotMobile);
    247         mMobileStates.clear();
    248         final int n = subs.size();
    249         for (int i = 0; i < n; i++) {
    250             mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
    251         }
    252     }
    253 
    254     private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
    255         final int N = subs.size();
    256         if (N != mMobileStates.size()) {
    257             return false;
    258         }
    259         for (int i = 0; i < N; i++) {
    260             if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
    261                 return false;
    262             }
    263         }
    264         return true;
    265     }
    266 
    267     @Override
    268     public void setNoSims(boolean show, boolean simDetected) {
    269         // Noop yay!
    270     }
    271 
    272 
    273     @Override
    274     public void setEthernetIndicators(IconState state) {
    275         boolean visible = state.visible && !mBlockEthernet;
    276         int resId = state.icon;
    277         String description = state.contentDescription;
    278 
    279         if (resId > 0) {
    280             mIconController.setIcon(mSlotEthernet, resId, description);
    281             mIconController.setIconVisibility(mSlotEthernet, true);
    282         } else {
    283             mIconController.setIconVisibility(mSlotEthernet, false);
    284         }
    285     }
    286 
    287     @Override
    288     public void setIsAirplaneMode(IconState icon) {
    289         mIsAirplaneMode = icon.visible && !mBlockAirplane;
    290         int resId = icon.icon;
    291         String description = icon.contentDescription;
    292 
    293         if (mIsAirplaneMode && resId > 0) {
    294             mIconController.setIcon(mSlotAirplane, resId, description);
    295             mIconController.setIconVisibility(mSlotAirplane, true);
    296         } else {
    297             mIconController.setIconVisibility(mSlotAirplane, false);
    298         }
    299     }
    300 
    301     @Override
    302     public void setMobileDataEnabled(boolean enabled) {
    303         // Don't care.
    304     }
    305 
    306     private static abstract class SignalIconState {
    307         public boolean visible;
    308         public boolean activityOut;
    309         public boolean activityIn;
    310         public String slot;
    311         public String contentDescription;
    312 
    313         @Override
    314         public boolean equals(Object o) {
    315             // Skipping reference equality bc this should be more of a value type
    316             if (o == null || getClass() != o.getClass()) {
    317                 return false;
    318             }
    319             SignalIconState that = (SignalIconState) o;
    320             return visible == that.visible &&
    321                     activityOut == that.activityOut &&
    322                     activityIn == that.activityIn &&
    323                     Objects.equals(contentDescription, that.contentDescription) &&
    324                     Objects.equals(slot, that.slot);
    325         }
    326 
    327         @Override
    328         public int hashCode() {
    329             return Objects.hash(visible, activityOut, slot);
    330         }
    331 
    332         protected void copyTo(SignalIconState other) {
    333             other.visible = visible;
    334             other.activityIn = activityIn;
    335             other.activityOut = activityOut;
    336             other.slot = slot;
    337             other.contentDescription = contentDescription;
    338         }
    339     }
    340 
    341     public static class WifiIconState extends SignalIconState{
    342         public int resId;
    343         public boolean airplaneSpacerVisible;
    344         public boolean signalSpacerVisible;
    345 
    346         @Override
    347         public boolean equals(Object o) {
    348             // Skipping reference equality bc this should be more of a value type
    349             if (o == null || getClass() != o.getClass()) {
    350                 return false;
    351             }
    352             if (!super.equals(o)) {
    353                 return false;
    354             }
    355             WifiIconState that = (WifiIconState) o;
    356             return resId == that.resId &&
    357                     airplaneSpacerVisible == that.airplaneSpacerVisible &&
    358                     signalSpacerVisible == that.signalSpacerVisible;
    359         }
    360 
    361         public void copyTo(WifiIconState other) {
    362             super.copyTo(other);
    363             other.resId = resId;
    364             other.airplaneSpacerVisible = airplaneSpacerVisible;
    365             other.signalSpacerVisible = signalSpacerVisible;
    366         }
    367 
    368         public WifiIconState copy() {
    369             WifiIconState newState = new WifiIconState();
    370             copyTo(newState);
    371             return newState;
    372         }
    373 
    374         @Override
    375         public int hashCode() {
    376             return Objects.hash(super.hashCode(),
    377                     resId, airplaneSpacerVisible, signalSpacerVisible);
    378         }
    379 
    380         @Override public String toString() {
    381             return "WifiIconState(resId=" + resId + ", visible=" + visible + ")";
    382         }
    383     }
    384 
    385     /**
    386      * A little different. This one delegates to SignalDrawable instead of a specific resId
    387      */
    388     public static class MobileIconState extends SignalIconState {
    389         public int subId;
    390         public int strengthId;
    391         public int typeId;
    392         public boolean roaming;
    393         public boolean needsLeadingPadding;
    394         public String typeContentDescription;
    395 
    396         private MobileIconState(int subId) {
    397             super();
    398             this.subId = subId;
    399         }
    400 
    401         @Override
    402         public boolean equals(Object o) {
    403             if (o == null || getClass() != o.getClass()) {
    404                 return false;
    405             }
    406             if (!super.equals(o)) {
    407                 return false;
    408             }
    409             MobileIconState that = (MobileIconState) o;
    410             return subId == that.subId &&
    411                     strengthId == that.strengthId &&
    412                     typeId == that.typeId &&
    413                     roaming == that.roaming &&
    414                     needsLeadingPadding == that.needsLeadingPadding &&
    415                     Objects.equals(typeContentDescription, that.typeContentDescription);
    416         }
    417 
    418         @Override
    419         public int hashCode() {
    420 
    421             return Objects
    422                     .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
    423                             typeContentDescription);
    424         }
    425 
    426         public MobileIconState copy() {
    427             MobileIconState copy = new MobileIconState(this.subId);
    428             copyTo(copy);
    429             return copy;
    430         }
    431 
    432         public void copyTo(MobileIconState other) {
    433             super.copyTo(other);
    434             other.subId = subId;
    435             other.strengthId = strengthId;
    436             other.typeId = typeId;
    437             other.roaming = roaming;
    438             other.needsLeadingPadding = needsLeadingPadding;
    439             other.typeContentDescription = typeContentDescription;
    440         }
    441 
    442         private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
    443             ArrayList<MobileIconState> outStates = new ArrayList<>();
    444             for (MobileIconState state : inStates) {
    445                 MobileIconState copy = new MobileIconState(state.subId);
    446                 state.copyTo(copy);
    447                 outStates.add(copy);
    448             }
    449 
    450             return outStates;
    451         }
    452 
    453         @Override public String toString() {
    454             return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
    455                     + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
    456         }
    457     }
    458 }
    459