1 /*
2  * Copyright (C) 2020 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.tiles;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.res.Resources;
24 import android.graphics.drawable.Drawable;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.UserHandle;
28 import android.provider.Settings;
29 import android.service.quicksettings.Tile;
30 import android.text.Html;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.Switch;
35 
36 import androidx.annotation.Nullable;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.internal.logging.MetricsLogger;
40 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
41 import com.android.settingslib.graph.SignalDrawable;
42 import com.android.settingslib.mobile.TelephonyIcons;
43 import com.android.settingslib.net.DataUsageController;
44 import com.android.systemui.R;
45 import com.android.systemui.dagger.qualifiers.Background;
46 import com.android.systemui.dagger.qualifiers.Main;
47 import com.android.systemui.plugins.ActivityStarter;
48 import com.android.systemui.plugins.FalsingManager;
49 import com.android.systemui.plugins.qs.QSIconView;
50 import com.android.systemui.plugins.qs.QSTile.SignalState;
51 import com.android.systemui.plugins.statusbar.StatusBarStateController;
52 import com.android.systemui.qs.AlphaControlledSignalTileView;
53 import com.android.systemui.qs.QSHost;
54 import com.android.systemui.qs.QsEventLogger;
55 import com.android.systemui.qs.logging.QSLogger;
56 import com.android.systemui.qs.tileimpl.QSTileImpl;
57 import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
58 import com.android.systemui.statusbar.connectivity.AccessPointController;
59 import com.android.systemui.statusbar.connectivity.IconState;
60 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
61 import com.android.systemui.statusbar.connectivity.NetworkController;
62 import com.android.systemui.statusbar.connectivity.SignalCallback;
63 import com.android.systemui.statusbar.connectivity.WifiIcons;
64 import com.android.systemui.statusbar.connectivity.WifiIndicators;
65 
66 import java.io.PrintWriter;
67 
68 import javax.inject.Inject;
69 
70 /** Quick settings tile: Internet **/
71 public class InternetTile extends QSTileImpl<SignalState> {
72 
73     public static final String TILE_SPEC = "internet";
74 
75     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
76     private static final int LAST_STATE_UNKNOWN = -1;
77     private static final int LAST_STATE_CELLULAR = 0;
78     private static final int LAST_STATE_WIFI = 1;
79     private static final int LAST_STATE_ETHERNET = 2;
80 
81     protected final NetworkController mController;
82     private final AccessPointController mAccessPointController;
83     private final DataUsageController mDataController;
84     // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
85     private int mLastTileState = LAST_STATE_UNKNOWN;
86 
87     protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
88     private final InternetDialogFactory mInternetDialogFactory;
89     final Handler mHandler;
90 
91     @Inject
InternetTile( QSHost host, QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, NetworkController networkController, AccessPointController accessPointController, InternetDialogFactory internetDialogFactory )92     public InternetTile(
93             QSHost host,
94             QsEventLogger uiEventLogger,
95             @Background Looper backgroundLooper,
96             @Main Handler mainHandler,
97             FalsingManager falsingManager,
98             MetricsLogger metricsLogger,
99             StatusBarStateController statusBarStateController,
100             ActivityStarter activityStarter,
101             QSLogger qsLogger,
102             NetworkController networkController,
103             AccessPointController accessPointController,
104             InternetDialogFactory internetDialogFactory
105     ) {
106         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
107                 statusBarStateController, activityStarter, qsLogger);
108         mInternetDialogFactory = internetDialogFactory;
109         mHandler = mainHandler;
110         mController = networkController;
111         mAccessPointController = accessPointController;
112         mDataController = mController.getMobileDataController();
113         mController.observe(getLifecycle(), mSignalCallback);
114     }
115 
116     @Override
newTileState()117     public SignalState newTileState() {
118         SignalState s = new SignalState();
119         s.forceExpandIcon = true;
120         return s;
121     }
122 
123     @Override
createTileView(Context context)124     public QSIconView createTileView(Context context) {
125         return new AlphaControlledSignalTileView(context);
126     }
127 
128     @Override
getLongClickIntent()129     public Intent getLongClickIntent() {
130         return WIFI_SETTINGS;
131     }
132 
133     @Override
handleClick(@ullable View view)134     protected void handleClick(@Nullable View view) {
135         mHandler.post(() -> mInternetDialogFactory.create(true,
136                 mAccessPointController.canConfigMobileData(),
137                 mAccessPointController.canConfigWifi(), view));
138     }
139 
140     @Override
getTileLabel()141     public CharSequence getTileLabel() {
142         return mContext.getString(R.string.quick_settings_internet_label);
143     }
144 
145     @Override
getMetricsCategory()146     public int getMetricsCategory() {
147         return MetricsEvent.QS_WIFI;
148     }
149 
150     @Override
isAvailable()151     public boolean isAvailable() {
152         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)
153                 || (mController.hasMobileDataFeature()
154                         && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
155     }
156 
157     @Nullable
getSecondaryLabel(boolean isTransient, @Nullable String statusLabel)158     private CharSequence getSecondaryLabel(boolean isTransient, @Nullable String statusLabel) {
159         return isTransient
160                 ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
161                 : statusLabel;
162     }
163 
164     @Nullable
removeDoubleQuotes(@ullable String string)165     private static String removeDoubleQuotes(@Nullable String string) {
166         if (string == null) return null;
167         final int length = string.length();
168         if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
169             return string.substring(1, length - 1);
170         }
171         return string;
172     }
173 
174     private static final class EthernetCallbackInfo {
175         boolean mConnected;
176         int mEthernetSignalIconId;
177         @Nullable
178         String mEthernetContentDescription;
179 
copyTo(EthernetCallbackInfo ethernetCallbackInfo)180         public void copyTo(EthernetCallbackInfo ethernetCallbackInfo) {
181             if (ethernetCallbackInfo == null) {
182                 throw new IllegalArgumentException();
183             }
184             ethernetCallbackInfo.mConnected = this.mConnected;
185             ethernetCallbackInfo.mEthernetSignalIconId = this.mEthernetSignalIconId;
186             ethernetCallbackInfo.mEthernetContentDescription = this.mEthernetContentDescription;
187         }
188 
189         @Override
toString()190         public String toString() {
191             return new StringBuilder("EthernetCallbackInfo[")
192                     .append("mConnected=").append(mConnected)
193                     .append(",mEthernetSignalIconId=").append(mEthernetSignalIconId)
194                     .append(",mEthernetContentDescription=").append(mEthernetContentDescription)
195                     .append(']').toString();
196         }
197     }
198 
199     private static final class WifiCallbackInfo {
200         boolean mAirplaneModeEnabled;
201         boolean mEnabled;
202         boolean mConnected;
203         int mWifiSignalIconId;
204         @Nullable
205         String mSsid;
206         @Nullable
207         String mWifiSignalContentDescription;
208         boolean mIsTransient;
209         @Nullable
210         public String mStatusLabel;
211         boolean mNoDefaultNetwork;
212         boolean mNoValidatedNetwork;
213         boolean mNoNetworksAvailable;
214 
copyTo(WifiCallbackInfo wifiCallbackInfo)215         public void copyTo(WifiCallbackInfo wifiCallbackInfo) {
216             if (wifiCallbackInfo == null) {
217                 throw new IllegalArgumentException();
218             }
219             wifiCallbackInfo.mAirplaneModeEnabled = this.mAirplaneModeEnabled;
220             wifiCallbackInfo.mEnabled = this.mEnabled;
221             wifiCallbackInfo.mConnected = this.mConnected;
222             wifiCallbackInfo.mWifiSignalIconId = this.mWifiSignalIconId;
223             wifiCallbackInfo.mSsid = this.mSsid;
224             wifiCallbackInfo.mWifiSignalContentDescription = this.mWifiSignalContentDescription;
225             wifiCallbackInfo.mIsTransient = this.mIsTransient;
226             wifiCallbackInfo.mStatusLabel = this.mStatusLabel;
227             wifiCallbackInfo.mNoDefaultNetwork = this.mNoDefaultNetwork;
228             wifiCallbackInfo.mNoValidatedNetwork = this.mNoValidatedNetwork;
229             wifiCallbackInfo.mNoNetworksAvailable = this.mNoNetworksAvailable;
230         }
231 
232         @Override
toString()233         public String toString() {
234             return new StringBuilder("WifiCallbackInfo[")
235                     .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled)
236                     .append(",mEnabled=").append(mEnabled)
237                     .append(",mConnected=").append(mConnected)
238                     .append(",mWifiSignalIconId=").append(mWifiSignalIconId)
239                     .append(",mSsid=").append(mSsid)
240                     .append(",mWifiSignalContentDescription=").append(mWifiSignalContentDescription)
241                     .append(",mIsTransient=").append(mIsTransient)
242                     .append(",mNoDefaultNetwork=").append(mNoDefaultNetwork)
243                     .append(",mNoValidatedNetwork=").append(mNoValidatedNetwork)
244                     .append(",mNoNetworksAvailable=").append(mNoNetworksAvailable)
245                     .append(']').toString();
246         }
247     }
248 
249     private static final class CellularCallbackInfo {
250         boolean mAirplaneModeEnabled;
251         @Nullable
252         CharSequence mDataSubscriptionName;
253         @Nullable
254         CharSequence mDataContentDescription;
255         int mMobileSignalIconId;
256         int mQsTypeIcon;
257         boolean mNoSim;
258         boolean mRoaming;
259         boolean mMultipleSubs;
260         boolean mNoDefaultNetwork;
261         boolean mNoValidatedNetwork;
262         boolean mNoNetworksAvailable;
263 
copyTo(CellularCallbackInfo cellularCallbackInfo)264         public void copyTo(CellularCallbackInfo cellularCallbackInfo) {
265             if (cellularCallbackInfo == null) {
266                 throw new IllegalArgumentException();
267             }
268             cellularCallbackInfo.mAirplaneModeEnabled = this.mAirplaneModeEnabled;
269             cellularCallbackInfo.mDataSubscriptionName = this.mDataSubscriptionName;
270             cellularCallbackInfo.mDataContentDescription = this.mDataContentDescription;
271             cellularCallbackInfo.mMobileSignalIconId = this.mMobileSignalIconId;
272             cellularCallbackInfo.mQsTypeIcon = this.mQsTypeIcon;
273             cellularCallbackInfo.mNoSim = this.mNoSim;
274             cellularCallbackInfo.mRoaming = this.mRoaming;
275             cellularCallbackInfo.mMultipleSubs = this.mMultipleSubs;
276             cellularCallbackInfo.mNoDefaultNetwork = this.mNoDefaultNetwork;
277             cellularCallbackInfo.mNoValidatedNetwork = this.mNoValidatedNetwork;
278             cellularCallbackInfo.mNoNetworksAvailable = this.mNoNetworksAvailable;
279         }
280 
281         @Override
toString()282         public String toString() {
283             return new StringBuilder("CellularCallbackInfo[")
284                 .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled)
285                 .append(",mDataSubscriptionName=").append(mDataSubscriptionName)
286                 .append(",mDataContentDescription=").append(mDataContentDescription)
287                 .append(",mMobileSignalIconId=").append(mMobileSignalIconId)
288                 .append(",mQsTypeIcon=").append(mQsTypeIcon)
289                 .append(",mNoSim=").append(mNoSim)
290                 .append(",mRoaming=").append(mRoaming)
291                 .append(",mMultipleSubs=").append(mMultipleSubs)
292                 .append(",mNoDefaultNetwork=").append(mNoDefaultNetwork)
293                 .append(",mNoValidatedNetwork=").append(mNoValidatedNetwork)
294                 .append(",mNoNetworksAvailable=").append(mNoNetworksAvailable)
295                 .append(']').toString();
296         }
297     }
298 
299     protected final class InternetSignalCallback implements SignalCallback {
300         @GuardedBy("mWifiInfo")
301         final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
302         @GuardedBy("mCellularInfo")
303         final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
304         @GuardedBy("mEthernetInfo")
305         final EthernetCallbackInfo mEthernetInfo = new EthernetCallbackInfo();
306 
307 
308         @Override
setWifiIndicators(@onNull WifiIndicators indicators)309         public void setWifiIndicators(@NonNull WifiIndicators indicators) {
310             if (DEBUG) {
311                 Log.d(TAG, "setWifiIndicators: " + indicators);
312             }
313             synchronized (mWifiInfo) {
314                 mWifiInfo.mEnabled = indicators.enabled;
315                 mWifiInfo.mSsid = indicators.description;
316                 mWifiInfo.mIsTransient = indicators.isTransient;
317                 mWifiInfo.mStatusLabel = indicators.statusLabel;
318                 if (indicators.qsIcon != null) {
319                     mWifiInfo.mConnected = indicators.qsIcon.visible;
320                     mWifiInfo.mWifiSignalIconId = indicators.qsIcon.icon;
321                     mWifiInfo.mWifiSignalContentDescription = indicators.qsIcon.contentDescription;
322                 } else {
323                     mWifiInfo.mConnected = false;
324                     mWifiInfo.mWifiSignalIconId = 0;
325                     mWifiInfo.mWifiSignalContentDescription = null;
326                 }
327             }
328             if (indicators.qsIcon != null) {
329                 refreshState(mWifiInfo);
330             }
331         }
332 
333         @Override
setMobileDataIndicators(@onNull MobileDataIndicators indicators)334         public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
335             if (DEBUG) {
336                 Log.d(TAG, "setMobileDataIndicators: " + indicators);
337             }
338             if (indicators.qsIcon == null) {
339                 // Not data sim, don't display.
340                 return;
341             }
342             synchronized (mCellularInfo) {
343                 mCellularInfo.mDataSubscriptionName = indicators.qsDescription == null
344                     ? mController.getMobileDataNetworkName() : indicators.qsDescription;
345                 mCellularInfo.mDataContentDescription = indicators.qsDescription != null
346                     ? indicators.typeContentDescriptionHtml : null;
347                 mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon;
348                 mCellularInfo.mQsTypeIcon = indicators.qsType;
349                 mCellularInfo.mRoaming = indicators.roaming;
350                 mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1;
351             }
352             refreshState(mCellularInfo);
353         }
354 
355         @Override
setEthernetIndicators(@onNull IconState icon)356         public void setEthernetIndicators(@NonNull IconState icon) {
357             if (DEBUG) {
358                 Log.d(TAG, "setEthernetIndicators: "
359                         + "icon = " + (icon == null ? "" :  icon.toString()));
360             }
361             synchronized (mEthernetInfo) {
362                 mEthernetInfo.mConnected = icon.visible;
363                 mEthernetInfo.mEthernetSignalIconId = icon.icon;
364                 mEthernetInfo.mEthernetContentDescription = icon.contentDescription;
365             }
366             if (icon.visible) {
367                 refreshState(mEthernetInfo);
368             }
369         }
370 
371         @Override
setNoSims(boolean show, boolean simDetected)372         public void setNoSims(boolean show, boolean simDetected) {
373             if (DEBUG) {
374                 Log.d(TAG, "setNoSims: "
375                         + "show = " + show + ","
376                         + "simDetected = " + simDetected);
377             }
378             synchronized (mCellularInfo) {
379                 mCellularInfo.mNoSim = show;
380                 if (mCellularInfo.mNoSim) {
381                     // Make sure signal gets cleared out when no sims.
382                     mCellularInfo.mMobileSignalIconId = 0;
383                     mCellularInfo.mQsTypeIcon = 0;
384                 }
385             }
386         }
387 
388         @Override
setIsAirplaneMode(IconState icon)389         public void setIsAirplaneMode(IconState icon) {
390             if (DEBUG) {
391                 Log.d(TAG, "setIsAirplaneMode: "
392                         + "icon = " + (icon == null ? "" : icon.toString()));
393             }
394             if (mCellularInfo.mAirplaneModeEnabled == icon.visible) {
395                 return;
396             }
397             synchronized (mCellularInfo) {
398                 mCellularInfo.mAirplaneModeEnabled = icon.visible;
399             }
400             synchronized (mWifiInfo) {
401                 mWifiInfo.mAirplaneModeEnabled = icon.visible;
402             }
403             if (!mSignalCallback.mEthernetInfo.mConnected) {
404                 // Always use mWifiInfo to refresh the Internet Tile if airplane mode is enabled,
405                 // because Internet Tile will show different information depending on whether WiFi
406                 // is enabled or not.
407                 if (mWifiInfo.mAirplaneModeEnabled) {
408                     refreshState(mWifiInfo);
409                 // If airplane mode is disabled, we will use mWifiInfo to refresh the Internet Tile
410                 // if WiFi is currently connected to avoid any icon flickering.
411                 } else if (mWifiInfo.mEnabled && (mWifiInfo.mWifiSignalIconId > 0)
412                         && (mWifiInfo.mSsid != null)) {
413                     refreshState(mWifiInfo);
414                 } else {
415                     refreshState(mCellularInfo);
416                 }
417             }
418         }
419 
420         @Override
setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable)421         public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
422                 boolean noNetworksAvailable) {
423             if (DEBUG) {
424                 Log.d(TAG, "setConnectivityStatus: "
425                         + "noDefaultNetwork = " + noDefaultNetwork + ","
426                         + "noValidatedNetwork = " + noValidatedNetwork + ","
427                         + "noNetworksAvailable = " + noNetworksAvailable);
428             }
429             synchronized (mCellularInfo) {
430                 mCellularInfo.mNoDefaultNetwork = noDefaultNetwork;
431                 mCellularInfo.mNoValidatedNetwork = noValidatedNetwork;
432                 mCellularInfo.mNoNetworksAvailable = noNetworksAvailable;
433             }
434             synchronized (mWifiInfo) {
435                 mWifiInfo.mNoDefaultNetwork = noDefaultNetwork;
436                 mWifiInfo.mNoValidatedNetwork = noValidatedNetwork;
437                 mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
438             }
439             if (!noDefaultNetwork) {
440                 return;
441             }
442             refreshState(mWifiInfo);
443         }
444 
445         @Override
toString()446         public String toString() {
447             return new StringBuilder("InternetSignalCallback[")
448                 .append("mWifiInfo=").append(mWifiInfo)
449                 .append(",mCellularInfo=").append(mCellularInfo)
450                 .append(",mEthernetInfo=").append(mEthernetInfo)
451                 .append(']').toString();
452         }
453     }
454 
455     @Override
handleUpdateState(SignalState state, Object arg)456     protected void handleUpdateState(SignalState state, Object arg) {
457         mQSLogger.logInternetTileUpdate(
458                 getTileSpec(), mLastTileState, arg == null ? "null" : arg.toString());
459         if (arg instanceof CellularCallbackInfo) {
460             mLastTileState = LAST_STATE_CELLULAR;
461             CellularCallbackInfo cb = (CellularCallbackInfo) arg;
462             CellularCallbackInfo cellularInfo = new CellularCallbackInfo();
463             synchronized (cb) {
464                 cb.copyTo(cellularInfo);
465             }
466             handleUpdateCellularState(state, cellularInfo);
467         } else if (arg instanceof WifiCallbackInfo) {
468             mLastTileState = LAST_STATE_WIFI;
469             WifiCallbackInfo cb = (WifiCallbackInfo) arg;
470             WifiCallbackInfo wifiInfo = new WifiCallbackInfo();
471             synchronized (cb) {
472                 cb.copyTo(wifiInfo);
473             }
474             handleUpdateWifiState(state, wifiInfo);
475         } else if (arg instanceof EthernetCallbackInfo) {
476             mLastTileState = LAST_STATE_ETHERNET;
477             EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
478             EthernetCallbackInfo ethernetInfo = new EthernetCallbackInfo();
479             synchronized (cb) {
480                 cb.copyTo(ethernetInfo);
481             }
482             handleUpdateEthernetState(state, ethernetInfo);
483         } else {
484             // handleUpdateState will be triggered when user expands the QuickSetting panel with
485             // arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
486             // should be used to refresh the tile.
487             if (mLastTileState == LAST_STATE_CELLULAR) {
488                 CellularCallbackInfo cellularInfo = new CellularCallbackInfo();
489                 synchronized (mSignalCallback.mCellularInfo) {
490                     mSignalCallback.mCellularInfo.copyTo(cellularInfo);
491                 }
492                 handleUpdateCellularState(state, cellularInfo);
493             } else if (mLastTileState == LAST_STATE_WIFI) {
494                 WifiCallbackInfo wifiInfo = new WifiCallbackInfo();
495                 synchronized (mSignalCallback.mWifiInfo) {
496                     mSignalCallback.mWifiInfo.copyTo(wifiInfo);
497                 }
498                 handleUpdateWifiState(state, wifiInfo);
499             } else if (mLastTileState == LAST_STATE_ETHERNET) {
500                 EthernetCallbackInfo ethernetInfo = new EthernetCallbackInfo();
501                 synchronized (mSignalCallback.mEthernetInfo) {
502                     mSignalCallback.mEthernetInfo.copyTo(ethernetInfo);
503                 }
504                 handleUpdateEthernetState(state, ethernetInfo);
505             }
506         }
507     }
508 
handleUpdateWifiState(SignalState state, Object arg)509     private void handleUpdateWifiState(SignalState state, Object arg) {
510         WifiCallbackInfo cb = (WifiCallbackInfo) arg;
511         if (DEBUG) {
512             Log.d(TAG, "handleUpdateWifiState: " + "WifiCallbackInfo = " + cb.toString());
513         }
514         boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
515         boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
516         if (state.slash == null) {
517             state.slash = new SlashState();
518             state.slash.rotation = 6;
519         }
520         state.slash.isSlashed = false;
521         state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid));
522         state.state = Tile.STATE_ACTIVE;
523         state.dualTarget = true;
524         state.value = cb.mEnabled;
525         final StringBuffer minimalContentDescription = new StringBuffer();
526         final StringBuffer minimalStateDescription = new StringBuffer();
527         final Resources r = mContext.getResources();
528         state.label = r.getString(R.string.quick_settings_internet_label);
529         if (cb.mAirplaneModeEnabled) {
530             if (!state.value) {
531                 state.state = Tile.STATE_INACTIVE;
532                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
533                 state.secondaryLabel = r.getString(R.string.status_bar_airplane);
534             } else if (!wifiConnected) {
535                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
536                 if (cb.mNoNetworksAvailable) {
537                     state.secondaryLabel =
538                             r.getString(R.string.quick_settings_networks_unavailable);
539                 } else {
540                     state.secondaryLabel =
541                             r.getString(R.string.quick_settings_networks_available);
542                 }
543             } else {
544                 state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
545             }
546         } else if (cb.mNoDefaultNetwork) {
547             if (cb.mNoNetworksAvailable || !cb.mEnabled) {
548                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
549                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
550             } else {
551                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
552                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
553             }
554         } else if (cb.mIsTransient) {
555             state.icon = ResourceIcon.get(
556                 com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
557         } else if (!state.value) {
558             state.slash.isSlashed = true;
559             state.state = Tile.STATE_INACTIVE;
560             state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
561         } else if (wifiConnected) {
562             state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
563         } else if (wifiNotConnected) {
564             state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
565         } else {
566             state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
567         }
568         minimalContentDescription.append(
569             mContext.getString(R.string.quick_settings_internet_label)).append(",");
570         if (state.value && wifiConnected) {
571             minimalStateDescription.append(cb.mWifiSignalContentDescription);
572             minimalContentDescription.append(removeDoubleQuotes(cb.mSsid));
573         } else if (!TextUtils.isEmpty(state.secondaryLabel)) {
574             minimalContentDescription.append(",").append(state.secondaryLabel);
575         }
576 
577         state.stateDescription = minimalStateDescription.toString();
578         state.contentDescription = minimalContentDescription.toString();
579         state.dualLabelContentDescription = r.getString(
580                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
581         state.expandedAccessibilityClassName = Switch.class.getName();
582         if (DEBUG) {
583             Log.d(TAG, "handleUpdateWifiState: " + "SignalState = " + state.toString());
584         }
585     }
586 
handleUpdateCellularState(SignalState state, Object arg)587     private void handleUpdateCellularState(SignalState state, Object arg) {
588         CellularCallbackInfo cb = (CellularCallbackInfo) arg;
589         if (DEBUG) {
590             Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
591         }
592         final Resources r = mContext.getResources();
593         state.label = r.getString(R.string.quick_settings_internet_label);
594         state.state = Tile.STATE_ACTIVE;
595         boolean mobileDataEnabled = mDataController.isMobileDataSupported()
596                 && mDataController.isMobileDataEnabled();
597         state.value = mobileDataEnabled;
598         state.expandedAccessibilityClassName = Switch.class.getName();
599 
600         if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) {
601             state.state = Tile.STATE_INACTIVE;
602             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
603             state.secondaryLabel = r.getString(R.string.status_bar_airplane);
604         } else if (cb.mNoDefaultNetwork) {
605             if (cb.mNoNetworksAvailable || !mSignalCallback.mWifiInfo.mEnabled) {
606                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
607                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
608             } else {
609                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
610                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
611             }
612         } else {
613             state.icon = new SignalIcon(cb.mMobileSignalIconId);
614             state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
615                     getMobileDataContentName(cb));
616         }
617 
618         state.contentDescription = state.label;
619         if (state.state == Tile.STATE_INACTIVE) {
620             // This information is appended later by converting the Tile.STATE_INACTIVE state.
621             state.stateDescription = "";
622         } else {
623             state.stateDescription = state.secondaryLabel;
624         }
625         if (DEBUG) {
626             Log.d(TAG, "handleUpdateCellularState: " + "SignalState = " + state.toString());
627         }
628     }
629 
handleUpdateEthernetState(SignalState state, Object arg)630     private void handleUpdateEthernetState(SignalState state, Object arg) {
631         EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
632         if (DEBUG) {
633             Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
634         }
635         if (!cb.mConnected) {
636             return;
637         }
638         final Resources r = mContext.getResources();
639         state.label = r.getString(R.string.quick_settings_internet_label);
640         state.state = Tile.STATE_ACTIVE;
641         state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
642         state.secondaryLabel = cb.mEthernetContentDescription;
643         if (DEBUG) {
644             Log.d(TAG, "handleUpdateEthernetState: " + "SignalState = " + state.toString());
645         }
646     }
647 
appendMobileDataType( @ullable CharSequence current, @Nullable CharSequence dataType)648     private CharSequence appendMobileDataType(
649             @Nullable CharSequence current, @Nullable CharSequence dataType) {
650         if (TextUtils.isEmpty(dataType)) {
651             return Html.fromHtml((current == null ? "" : current.toString()), 0);
652         }
653         if (TextUtils.isEmpty(current)) {
654             return Html.fromHtml((dataType == null ? "" : dataType.toString()), 0);
655         }
656         String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
657         return Html.fromHtml(concat, 0);
658     }
659 
660     @Nullable
getMobileDataContentName(CellularCallbackInfo cb)661     private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
662         if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
663             String roaming = mContext.getString(R.string.data_connection_roaming);
664             String dataDescription =
665                     cb.mDataContentDescription == null ? ""
666                             : cb.mDataContentDescription.toString();
667             return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
668         }
669         if (cb.mRoaming) {
670             return mContext.getString(R.string.data_connection_roaming);
671         }
672         return cb.mDataContentDescription;
673     }
674 
675     private static class SignalIcon extends Icon {
676         private final int mState;
SignalIcon(int state)677         SignalIcon(int state) {
678             mState = state;
679         }
getState()680         public int getState() {
681             return mState;
682         }
683 
684         @Override
685         @NonNull
getDrawable(Context context)686         public Drawable getDrawable(Context context) {
687             SignalDrawable d = new SignalDrawable(context);
688             d.setLevel(getState());
689             return d;
690         }
691         @Override
toString()692         public String toString() {
693             return String.format("SignalIcon[mState=0x%08x]", mState);
694         }
695     }
696 
697     /**
698      * Dumps the state of this tile along with its name.
699      */
700     @Override
dump(PrintWriter pw, String[] args)701     public void dump(PrintWriter pw, String[] args) {
702         pw.println(this.getClass().getSimpleName() + ":");
703         pw.print("    "); pw.println(getState().toString());
704         pw.print("    "); pw.println("mLastTileState=" + mLastTileState);
705         pw.print("    "); pw.println("mSignalCallback=" + mSignalCallback.toString());
706     }
707 
708     // For testing usage only.
getLastTileState()709     protected int getLastTileState() {
710         return mLastTileState;
711     }
712 }
713