1 /* 2 * Copyright (C) 2021 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.dialog; 18 19 import static com.android.settingslib.mobile.MobileMappings.getIconKey; 20 import static com.android.settingslib.mobile.MobileMappings.mapIconSets; 21 import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED; 22 23 import android.animation.Animator; 24 import android.animation.AnimatorListenerAdapter; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.res.Resources; 30 import android.graphics.Color; 31 import android.graphics.PixelFormat; 32 import android.graphics.drawable.ColorDrawable; 33 import android.graphics.drawable.Drawable; 34 import android.graphics.drawable.LayerDrawable; 35 import android.net.ConnectivityManager; 36 import android.net.Network; 37 import android.net.NetworkCapabilities; 38 import android.net.wifi.WifiManager; 39 import android.os.Handler; 40 import android.os.UserHandle; 41 import android.provider.Settings; 42 import android.telephony.AccessNetworkConstants; 43 import android.telephony.NetworkRegistrationInfo; 44 import android.telephony.ServiceState; 45 import android.telephony.SignalStrength; 46 import android.telephony.SubscriptionInfo; 47 import android.telephony.SubscriptionManager; 48 import android.telephony.TelephonyCallback; 49 import android.telephony.TelephonyDisplayInfo; 50 import android.telephony.TelephonyManager; 51 import android.text.TextUtils; 52 import android.util.Log; 53 import android.view.Gravity; 54 import android.view.View; 55 import android.view.WindowManager; 56 57 import androidx.annotation.MainThread; 58 import androidx.annotation.NonNull; 59 import androidx.annotation.Nullable; 60 import androidx.annotation.VisibleForTesting; 61 import androidx.annotation.WorkerThread; 62 63 import com.android.internal.logging.UiEventLogger; 64 import com.android.keyguard.KeyguardUpdateMonitor; 65 import com.android.keyguard.KeyguardUpdateMonitorCallback; 66 import com.android.settingslib.DeviceInfoUtils; 67 import com.android.settingslib.SignalIcon; 68 import com.android.settingslib.Utils; 69 import com.android.settingslib.graph.SignalDrawable; 70 import com.android.settingslib.mobile.MobileMappings; 71 import com.android.settingslib.mobile.TelephonyIcons; 72 import com.android.settingslib.net.SignalStrengthUtil; 73 import com.android.settingslib.wifi.WifiUtils; 74 import com.android.systemui.R; 75 import com.android.systemui.animation.DialogLaunchAnimator; 76 import com.android.systemui.broadcast.BroadcastDispatcher; 77 import com.android.systemui.dagger.qualifiers.Background; 78 import com.android.systemui.dagger.qualifiers.Main; 79 import com.android.systemui.plugins.ActivityStarter; 80 import com.android.systemui.statusbar.connectivity.AccessPointController; 81 import com.android.systemui.statusbar.policy.KeyguardStateController; 82 import com.android.systemui.statusbar.policy.LocationController; 83 import com.android.systemui.toast.SystemUIToast; 84 import com.android.systemui.toast.ToastFactory; 85 import com.android.systemui.util.CarrierConfigTracker; 86 import com.android.systemui.util.settings.GlobalSettings; 87 import com.android.wifitrackerlib.MergedCarrierEntry; 88 import com.android.wifitrackerlib.WifiEntry; 89 90 import java.util.ArrayList; 91 import java.util.HashSet; 92 import java.util.List; 93 import java.util.Map; 94 import java.util.Set; 95 import java.util.concurrent.Executor; 96 import java.util.concurrent.atomic.AtomicReference; 97 import java.util.function.Supplier; 98 import java.util.stream.Collectors; 99 import java.util.stream.Stream; 100 101 import javax.inject.Inject; 102 103 /** 104 * Controller for Internet Dialog. 105 */ 106 public class InternetDialogController implements AccessPointController.AccessPointCallback { 107 108 private static final String TAG = "InternetDialogController"; 109 private static final String ACTION_NETWORK_PROVIDER_SETTINGS = 110 "android.settings.NETWORK_PROVIDER_SETTINGS"; 111 private static final String ACTION_WIFI_SCANNING_SETTINGS = 112 "android.settings.WIFI_SCANNING_SETTINGS"; 113 private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key"; 114 public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); 115 public static final int NO_CELL_DATA_TYPE_ICON = 0; 116 private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off; 117 private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT = 118 R.string.tap_a_network_to_connect; 119 private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS = 120 R.string.unlock_to_view_networks; 121 private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS = 122 R.string.wifi_empty_list_wifi_on; 123 private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE = 124 R.string.non_carrier_network_unavailable; 125 private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE = 126 R.string.all_network_unavailable; 127 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 128 129 static final int MAX_WIFI_ENTRY_COUNT = 3; 130 131 private WifiManager mWifiManager; 132 private Context mContext; 133 private SubscriptionManager mSubscriptionManager; 134 private TelephonyManager mTelephonyManager; 135 private ConnectivityManager mConnectivityManager; 136 private CarrierConfigTracker mCarrierConfigTracker; 137 private TelephonyDisplayInfo mTelephonyDisplayInfo = 138 new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, 139 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); 140 private Handler mHandler; 141 private Handler mWorkerHandler; 142 private MobileMappings.Config mConfig = null; 143 private Executor mExecutor; 144 private AccessPointController mAccessPointController; 145 private IntentFilter mConnectionStateFilter; 146 private InternetDialogCallback mCallback; 147 private UiEventLogger mUiEventLogger; 148 private BroadcastDispatcher mBroadcastDispatcher; 149 private KeyguardUpdateMonitor mKeyguardUpdateMonitor; 150 private GlobalSettings mGlobalSettings; 151 private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 152 private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback; 153 private WindowManager mWindowManager; 154 private ToastFactory mToastFactory; 155 private SignalDrawable mSignalDrawable; 156 private LocationController mLocationController; 157 private DialogLaunchAnimator mDialogLaunchAnimator; 158 private boolean mHasWifiEntries; 159 160 @VisibleForTesting 161 static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f; 162 @VisibleForTesting 163 static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f; 164 @VisibleForTesting 165 static final long SHORT_DURATION_TIMEOUT = 4000; 166 @VisibleForTesting 167 protected ActivityStarter mActivityStarter; 168 @VisibleForTesting 169 protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener; 170 @VisibleForTesting 171 protected InternetTelephonyCallback mInternetTelephonyCallback; 172 @VisibleForTesting 173 protected WifiUtils.InternetIconInjector mWifiIconInjector; 174 @VisibleForTesting 175 protected boolean mCanConfigWifi; 176 @VisibleForTesting 177 protected KeyguardStateController mKeyguardStateController; 178 @VisibleForTesting 179 protected boolean mHasEthernet = false; 180 @VisibleForTesting 181 protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor; 182 183 private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = 184 new KeyguardUpdateMonitorCallback() { 185 @Override 186 public void onRefreshCarrierInfo() { 187 mCallback.onRefreshCarrierInfo(); 188 } 189 190 @Override 191 public void onSimStateChanged(int subId, int slotId, int simState) { 192 mCallback.onSimStateChanged(); 193 } 194 }; 195 getSubscriptionInfo()196 protected List<SubscriptionInfo> getSubscriptionInfo() { 197 return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); 198 } 199 200 @Inject InternetDialogController(@onNull Context context, UiEventLogger uiEventLogger, ActivityStarter starter, AccessPointController accessPointController, SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, @Main Handler handler, @Main Executor mainExecutor, BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings, KeyguardStateController keyguardStateController, WindowManager windowManager, ToastFactory toastFactory, @Background Handler workerHandler, CarrierConfigTracker carrierConfigTracker, LocationController locationController, DialogLaunchAnimator dialogLaunchAnimator)201 public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger, 202 ActivityStarter starter, AccessPointController accessPointController, 203 SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, 204 @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, 205 @Main Handler handler, @Main Executor mainExecutor, 206 BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, 207 GlobalSettings globalSettings, KeyguardStateController keyguardStateController, 208 WindowManager windowManager, ToastFactory toastFactory, 209 @Background Handler workerHandler, 210 CarrierConfigTracker carrierConfigTracker, 211 LocationController locationController, 212 DialogLaunchAnimator dialogLaunchAnimator) { 213 if (DEBUG) { 214 Log.d(TAG, "Init InternetDialogController"); 215 } 216 mHandler = handler; 217 mWorkerHandler = workerHandler; 218 mExecutor = mainExecutor; 219 mContext = context; 220 mGlobalSettings = globalSettings; 221 mWifiManager = wifiManager; 222 mTelephonyManager = telephonyManager; 223 mConnectivityManager = connectivityManager; 224 mSubscriptionManager = subscriptionManager; 225 mCarrierConfigTracker = carrierConfigTracker; 226 mBroadcastDispatcher = broadcastDispatcher; 227 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 228 mKeyguardStateController = keyguardStateController; 229 mConnectionStateFilter = new IntentFilter(); 230 mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 231 mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 232 mUiEventLogger = uiEventLogger; 233 mActivityStarter = starter; 234 mAccessPointController = accessPointController; 235 mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext); 236 mConnectivityManagerNetworkCallback = new DataConnectivityListener(); 237 mWindowManager = windowManager; 238 mToastFactory = toastFactory; 239 mSignalDrawable = new SignalDrawable(mContext); 240 mLocationController = locationController; 241 mDialogLaunchAnimator = dialogLaunchAnimator; 242 mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor(); 243 } 244 onStart(@onNull InternetDialogCallback callback, boolean canConfigWifi)245 void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) { 246 if (DEBUG) { 247 Log.d(TAG, "onStart"); 248 } 249 mCallback = callback; 250 mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); 251 mAccessPointController.addAccessPointCallback(this); 252 mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter, 253 mExecutor); 254 // Listen the subscription changes 255 mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener(); 256 mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, 257 mOnSubscriptionsChangedListener); 258 mDefaultDataSubId = getDefaultDataSubscriptionId(); 259 if (DEBUG) { 260 Log.d(TAG, "Init, SubId: " + mDefaultDataSubId); 261 } 262 mConfig = MobileMappings.Config.readConfig(mContext); 263 mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); 264 mInternetTelephonyCallback = new InternetTelephonyCallback(); 265 mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback); 266 // Listen the connectivity changes 267 mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback); 268 mCanConfigWifi = canConfigWifi; 269 scanWifiAccessPoints(); 270 } 271 onStop()272 void onStop() { 273 if (DEBUG) { 274 Log.d(TAG, "onStop"); 275 } 276 mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver); 277 mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback); 278 mSubscriptionManager.removeOnSubscriptionsChangedListener( 279 mOnSubscriptionsChangedListener); 280 mAccessPointController.removeAccessPointCallback(this); 281 mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); 282 mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback); 283 mConnectedWifiInternetMonitor.unregisterCallback(); 284 } 285 286 @VisibleForTesting isAirplaneModeEnabled()287 boolean isAirplaneModeEnabled() { 288 return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0; 289 } 290 setAirplaneModeDisabled()291 void setAirplaneModeDisabled() { 292 mConnectivityManager.setAirplaneMode(false); 293 } 294 295 @VisibleForTesting getDefaultDataSubscriptionId()296 protected int getDefaultDataSubscriptionId() { 297 return mSubscriptionManager.getDefaultDataSubscriptionId(); 298 } 299 300 @VisibleForTesting getSettingsIntent()301 protected Intent getSettingsIntent() { 302 return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 303 } 304 getWifiDetailsSettingsIntent(String key)305 protected Intent getWifiDetailsSettingsIntent(String key) { 306 if (TextUtils.isEmpty(key)) { 307 if (DEBUG) { 308 Log.d(TAG, "connected entry's key is empty"); 309 } 310 return null; 311 } 312 return WifiUtils.getWifiDetailsSettingsIntent(key); 313 } 314 getDialogTitleText()315 CharSequence getDialogTitleText() { 316 if (isAirplaneModeEnabled()) { 317 return mContext.getText(R.string.airplane_mode); 318 } 319 return mContext.getText(R.string.quick_settings_internet_label); 320 } 321 getSubtitleText(boolean isProgressBarVisible)322 CharSequence getSubtitleText(boolean isProgressBarVisible) { 323 if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) { 324 // When Wi-Fi is disabled. 325 // Sub-Title: Wi-Fi is off 326 if (DEBUG) { 327 Log.d(TAG, "Wi-Fi off."); 328 } 329 return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF); 330 } 331 332 if (isDeviceLocked()) { 333 // When the device is locked. 334 // Sub-Title: Unlock to view networks 335 if (DEBUG) { 336 Log.d(TAG, "The device is locked."); 337 } 338 return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS); 339 } 340 341 if (mHasWifiEntries) { 342 return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null; 343 } 344 345 if (mCanConfigWifi && isProgressBarVisible) { 346 // When the Wi-Fi scan result callback is received 347 // Sub-Title: Searching for networks... 348 return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS); 349 } 350 351 if (isCarrierNetworkActive()) { 352 return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); 353 } 354 355 // Sub-Title: 356 // show non_carrier_network_unavailable 357 // - while Wi-Fi on + no Wi-Fi item 358 // - while Wi-Fi on + no Wi-Fi item + mobile data off 359 // show all_network_unavailable: 360 // - while Wi-Fi on + no Wi-Fi item + no carrier item 361 // - while Wi-Fi on + no Wi-Fi item + service is out of service 362 // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data. 363 if (DEBUG) { 364 Log.d(TAG, "No Wi-Fi item."); 365 } 366 if (!hasActiveSubId() || (!isVoiceStateInService() && !isDataStateInService())) { 367 if (DEBUG) { 368 Log.d(TAG, "No carrier or service is out of service."); 369 } 370 return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); 371 } 372 373 if (mCanConfigWifi && !isMobileDataEnabled()) { 374 if (DEBUG) { 375 Log.d(TAG, "Mobile data off"); 376 } 377 return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); 378 } 379 380 if (!activeNetworkIsCellular()) { 381 if (DEBUG) { 382 Log.d(TAG, "No carrier data."); 383 } 384 return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); 385 } 386 387 if (mCanConfigWifi) { 388 return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); 389 } 390 return null; 391 } 392 getInternetWifiDrawable(@onNull WifiEntry wifiEntry)393 Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) { 394 if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) { 395 return null; 396 } 397 final Drawable drawable = 398 mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel()); 399 if (drawable == null) { 400 return null; 401 } 402 drawable.setTint(mContext.getColor(R.color.connected_network_primary_color)); 403 return drawable; 404 } 405 getSignalStrengthDrawable()406 Drawable getSignalStrengthDrawable() { 407 Drawable drawable = mContext.getDrawable( 408 R.drawable.ic_signal_strength_zero_bar_no_internet); 409 try { 410 if (mTelephonyManager == null) { 411 if (DEBUG) { 412 Log.d(TAG, "TelephonyManager is null"); 413 } 414 return drawable; 415 } 416 417 boolean isCarrierNetworkActive = isCarrierNetworkActive(); 418 if (isDataStateInService() || isVoiceStateInService() || isCarrierNetworkActive) { 419 AtomicReference<Drawable> shared = new AtomicReference<>(); 420 shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive)); 421 drawable = shared.get(); 422 } 423 424 int tintColor = Utils.getColorAttrDefaultColor(mContext, 425 android.R.attr.textColorTertiary); 426 if (activeNetworkIsCellular() || isCarrierNetworkActive) { 427 tintColor = mContext.getColor(R.color.connected_network_primary_color); 428 } 429 drawable.setTint(tintColor); 430 } catch (Throwable e) { 431 e.printStackTrace(); 432 } 433 return drawable; 434 } 435 436 /** 437 * To get the signal bar icon with level. 438 * 439 * @return The Drawable which is a signal bar icon with level. 440 */ getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive)441 Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive) { 442 final SignalStrength strength = mTelephonyManager.getSignalStrength(); 443 int level = (strength == null) ? 0 : strength.getLevel(); 444 int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 445 if ((mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) 446 || isCarrierNetworkActive) { 447 level = isCarrierNetworkActive 448 ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS 449 : (level + 1); 450 numLevels += 1; 451 } 452 return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, 453 !isMobileDataEnabled()); 454 } 455 getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut)456 Drawable getSignalStrengthIcon(Context context, int level, int numLevels, 457 int iconType, boolean cutOut) { 458 mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut)); 459 460 // Make the network type drawable 461 final Drawable networkDrawable = 462 iconType == NO_CELL_DATA_TYPE_ICON 463 ? EMPTY_DRAWABLE 464 : context.getResources().getDrawable(iconType, context.getTheme()); 465 466 // Overlay the two drawables 467 final Drawable[] layers = {networkDrawable, mSignalDrawable}; 468 final int iconSize = 469 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); 470 471 final LayerDrawable icons = new LayerDrawable(layers); 472 // Set the network type icon at the top left 473 icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); 474 // Set the signal strength icon at the bottom right 475 icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); 476 icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); 477 icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary)); 478 return icons; 479 } 480 shouldInflateSignalStrength(int subId)481 private boolean shouldInflateSignalStrength(int subId) { 482 return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); 483 } 484 getUniqueSubscriptionDisplayName(int subscriptionId, Context context)485 private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) { 486 final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context); 487 return displayNames.getOrDefault(subscriptionId, ""); 488 } 489 getUniqueSubscriptionDisplayNames(Context context)490 private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) { 491 class DisplayInfo { 492 public SubscriptionInfo subscriptionInfo; 493 public CharSequence originalName; 494 public CharSequence uniqueName; 495 } 496 497 // Map of SubscriptionId to DisplayName 498 final Supplier<Stream<DisplayInfo>> originalInfos = 499 () -> getSubscriptionInfo() 500 .stream() 501 .filter(i -> { 502 // Filter out null values. 503 return (i != null && i.getDisplayName() != null); 504 }) 505 .map(i -> { 506 DisplayInfo info = new DisplayInfo(); 507 info.subscriptionInfo = i; 508 info.originalName = i.getDisplayName().toString().trim(); 509 return info; 510 }); 511 512 // A Unique set of display names 513 Set<CharSequence> uniqueNames = new HashSet<>(); 514 // Return the set of duplicate names 515 final Set<CharSequence> duplicateOriginalNames = originalInfos.get() 516 .filter(info -> !uniqueNames.add(info.originalName)) 517 .map(info -> info.originalName) 518 .collect(Collectors.toSet()); 519 520 // If a display name is duplicate, append the final 4 digits of the phone number. 521 // Creates a mapping of Subscription id to original display name + phone number display name 522 final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> { 523 if (duplicateOriginalNames.contains(info.originalName)) { 524 // This may return null, if the user cannot view the phone number itself. 525 final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context, 526 info.subscriptionInfo); 527 String lastFourDigits = ""; 528 if (phoneNumber != null) { 529 lastFourDigits = (phoneNumber.length() > 4) 530 ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber; 531 } 532 533 if (TextUtils.isEmpty(lastFourDigits)) { 534 info.uniqueName = info.originalName; 535 } else { 536 info.uniqueName = info.originalName + " " + lastFourDigits; 537 } 538 539 } else { 540 info.uniqueName = info.originalName; 541 } 542 return info; 543 }); 544 545 // Check uniqueness a second time. 546 // We might not have had permission to view the phone numbers. 547 // There might also be multiple phone numbers whose last 4 digits the same. 548 uniqueNames.clear(); 549 final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get() 550 .filter(info -> !uniqueNames.add(info.uniqueName)) 551 .map(info -> info.uniqueName) 552 .collect(Collectors.toSet()); 553 554 return uniqueInfos.get().map(info -> { 555 if (duplicatePhoneNames.contains(info.uniqueName)) { 556 info.uniqueName = info.originalName + " " 557 + info.subscriptionInfo.getSubscriptionId(); 558 } 559 return info; 560 }).collect(Collectors.toMap( 561 info -> info.subscriptionInfo.getSubscriptionId(), 562 info -> info.uniqueName)); 563 } 564 565 CharSequence getMobileNetworkTitle() { 566 return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext); 567 } 568 569 String getMobileNetworkSummary() { 570 String description = getNetworkTypeDescription(mContext, mConfig, 571 mTelephonyDisplayInfo, mDefaultDataSubId); 572 return getMobileSummary(mContext, description); 573 } 574 575 /** 576 * Get currently description of mobile network type. 577 */ 578 private String getNetworkTypeDescription(Context context, MobileMappings.Config config, 579 TelephonyDisplayInfo telephonyDisplayInfo, int subId) { 580 String iconKey = getIconKey(telephonyDisplayInfo); 581 582 if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) { 583 if (DEBUG) { 584 Log.d(TAG, "The description of network type is empty."); 585 } 586 return ""; 587 } 588 589 int resId = mapIconSets(config).get(iconKey).dataContentDescription; 590 if (isCarrierNetworkActive()) { 591 SignalIcon.MobileIconGroup carrierMergedWifiIconGroup = 592 TelephonyIcons.CARRIER_MERGED_WIFI; 593 resId = carrierMergedWifiIconGroup.dataContentDescription; 594 } 595 596 return resId != 0 597 ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : ""; 598 } 599 600 private String getMobileSummary(Context context, String networkTypeDescription) { 601 if (!isMobileDataEnabled()) { 602 return context.getString(R.string.mobile_data_off_summary); 603 } 604 605 String summary = networkTypeDescription; 606 // Set network description for the carrier network when connecting to the carrier network 607 // under the airplane mode ON. 608 if (activeNetworkIsCellular() || isCarrierNetworkActive()) { 609 summary = context.getString(R.string.preference_summary_default_combination, 610 context.getString(R.string.mobile_data_connection_active), 611 networkTypeDescription); 612 } else if (!isDataStateInService()) { 613 summary = context.getString(R.string.mobile_data_no_connection); 614 } 615 616 return summary; 617 } 618 619 void launchNetworkSetting() { 620 // Dismissing a dialog into its touch surface and starting an activity at the same time 621 // looks bad, so let's make sure the dialog just fades out quickly. 622 mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); 623 mCallback.dismissDialog(); 624 625 mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0); 626 } 627 628 void launchWifiNetworkDetailsSetting(String key) { 629 Intent intent = getWifiDetailsSettingsIntent(key); 630 if (intent != null) { 631 // Dismissing a dialog into its touch surface and starting an activity at the same time 632 // looks bad, so let's make sure the dialog just fades out quickly. 633 mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); 634 mCallback.dismissDialog(); 635 636 mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); 637 } 638 } 639 640 void launchWifiScanningSetting() { 641 // Dismissing a dialog into its touch surface and starting an activity at the same time 642 // looks bad, so let's make sure the dialog just fades out quickly. 643 mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); 644 mCallback.dismissDialog(); 645 646 final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS); 647 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 648 mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); 649 } 650 651 void connectCarrierNetwork() { 652 final MergedCarrierEntry mergedCarrierEntry = 653 mAccessPointController.getMergedCarrierEntry(); 654 if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) { 655 mergedCarrierEntry.connect(null /* ConnectCallback */, false); 656 makeOverlayToast(R.string.wifi_wont_autoconnect_for_now); 657 } 658 } 659 660 boolean isCarrierNetworkActive() { 661 final MergedCarrierEntry mergedCarrierEntry = 662 mAccessPointController.getMergedCarrierEntry(); 663 return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork(); 664 } 665 666 @WorkerThread 667 void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) { 668 // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier 669 // Wi-Fi state together. 670 if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) { 671 return; 672 } 673 674 final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry(); 675 if (entry == null) { 676 if (DEBUG) { 677 Log.d(TAG, "MergedCarrierEntry is null, can not set the status."); 678 } 679 return; 680 } 681 entry.setEnabled(enabled); 682 } 683 684 WifiManager getWifiManager() { 685 return mWifiManager; 686 } 687 688 TelephonyManager getTelephonyManager() { 689 return mTelephonyManager; 690 } 691 692 SubscriptionManager getSubscriptionManager() { 693 return mSubscriptionManager; 694 } 695 696 /** 697 * @return whether there is the carrier item in the slice. 698 */ 699 boolean hasActiveSubId() { 700 if (mSubscriptionManager == null) { 701 if (DEBUG) { 702 Log.d(TAG, "SubscriptionManager is null, can not check carrier."); 703 } 704 return false; 705 } 706 707 if (isAirplaneModeEnabled() || mTelephonyManager == null 708 || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) { 709 return false; 710 } 711 return true; 712 } 713 714 /** 715 * Return {@code true} if mobile data is enabled 716 */ 717 boolean isMobileDataEnabled() { 718 if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) { 719 return false; 720 } 721 return true; 722 } 723 724 /** 725 * Set whether to enable data for {@code subId}, also whether to disable data for other 726 * subscription 727 */ 728 void setMobileDataEnabled(Context context, int subId, boolean enabled, 729 boolean disableOtherSubscriptions) { 730 if (mTelephonyManager == null) { 731 if (DEBUG) { 732 Log.d(TAG, "TelephonyManager is null, can not set mobile data."); 733 } 734 return; 735 } 736 737 if (mSubscriptionManager == null) { 738 if (DEBUG) { 739 Log.d(TAG, "SubscriptionManager is null, can not set mobile data."); 740 } 741 return; 742 } 743 744 mTelephonyManager.setDataEnabled(enabled); 745 if (disableOtherSubscriptions) { 746 final List<SubscriptionInfo> subInfoList = 747 mSubscriptionManager.getActiveSubscriptionInfoList(); 748 if (subInfoList != null) { 749 for (SubscriptionInfo subInfo : subInfoList) { 750 // We never disable mobile data for opportunistic subscriptions. 751 if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) { 752 context.getSystemService(TelephonyManager.class).createForSubscriptionId( 753 subInfo.getSubscriptionId()).setDataEnabled(false); 754 } 755 } 756 } 757 } 758 mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled)); 759 } 760 761 boolean isDataStateInService() { 762 final ServiceState serviceState = mTelephonyManager.getServiceState(); 763 NetworkRegistrationInfo regInfo = 764 (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo( 765 NetworkRegistrationInfo.DOMAIN_PS, 766 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 767 return (regInfo == null) ? false : regInfo.isRegistered(); 768 } 769 770 boolean isVoiceStateInService() { 771 if (mTelephonyManager == null) { 772 if (DEBUG) { 773 Log.d(TAG, "TelephonyManager is null, can not detect voice state."); 774 } 775 return false; 776 } 777 778 final ServiceState serviceState = mTelephonyManager.getServiceState(); 779 return serviceState != null 780 && serviceState.getState() == serviceState.STATE_IN_SERVICE; 781 } 782 783 public boolean isDeviceLocked() { 784 return !mKeyguardStateController.isUnlocked(); 785 } 786 787 boolean activeNetworkIsCellular() { 788 if (mConnectivityManager == null) { 789 if (DEBUG) { 790 Log.d(TAG, "ConnectivityManager is null, can not check active network."); 791 } 792 return false; 793 } 794 795 final Network activeNetwork = mConnectivityManager.getActiveNetwork(); 796 if (activeNetwork == null) { 797 return false; 798 } 799 final NetworkCapabilities networkCapabilities = 800 mConnectivityManager.getNetworkCapabilities(activeNetwork); 801 if (networkCapabilities == null) { 802 return false; 803 } 804 return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); 805 } 806 807 boolean connect(WifiEntry ap) { 808 if (ap == null) { 809 if (DEBUG) { 810 Log.d(TAG, "No Wi-Fi ap to connect."); 811 } 812 return false; 813 } 814 815 if (ap.getWifiConfiguration() != null) { 816 if (DEBUG) { 817 Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); 818 } 819 } else { 820 if (DEBUG) { 821 Log.d(TAG, "connect to unsaved network " + ap.getTitle()); 822 } 823 } 824 ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this)); 825 return false; 826 } 827 828 @WorkerThread 829 boolean isWifiScanEnabled() { 830 if (!mLocationController.isLocationEnabled()) { 831 return false; 832 } 833 return mWifiManager.isScanAlwaysAvailable(); 834 } 835 836 static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback { 837 final ActivityStarter mActivityStarter; 838 final WifiEntry mWifiEntry; 839 final InternetDialogController mInternetDialogController; 840 841 WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry, 842 InternetDialogController internetDialogController) { 843 mActivityStarter = activityStarter; 844 mWifiEntry = connectWifiEntry; 845 mInternetDialogController = internetDialogController; 846 } 847 848 @Override 849 public void onConnectResult(@ConnectStatus int status) { 850 if (DEBUG) { 851 Log.d(TAG, "onConnectResult " + status); 852 } 853 854 if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) { 855 final Intent intent = new Intent("com.android.settings.WIFI_DIALOG") 856 .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey()); 857 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 858 mActivityStarter.startActivity(intent, false /* dismissShade */); 859 } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) { 860 mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message); 861 } else { 862 if (DEBUG) { 863 Log.d(TAG, "connect failure reason=" + status); 864 } 865 } 866 } 867 } 868 869 private void scanWifiAccessPoints() { 870 if (mCanConfigWifi) { 871 mAccessPointController.scanForAccessPoints(); 872 } 873 } 874 875 @Override 876 @WorkerThread 877 public void onAccessPointsChanged(List<WifiEntry> accessPoints) { 878 if (!mCanConfigWifi) { 879 return; 880 } 881 882 WifiEntry connectedEntry = null; 883 List<WifiEntry> wifiEntries = null; 884 final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size(); 885 final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT); 886 if (accessPointsSize > 0) { 887 wifiEntries = new ArrayList<>(); 888 final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize; 889 mConnectedWifiInternetMonitor.unregisterCallback(); 890 for (int i = 0; i < count; i++) { 891 WifiEntry entry = accessPoints.get(i); 892 mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry); 893 if (connectedEntry == null && entry.isDefaultNetwork() 894 && entry.hasInternetAccess()) { 895 connectedEntry = entry; 896 } else { 897 wifiEntries.add(entry); 898 } 899 } 900 mHasWifiEntries = true; 901 } else { 902 mHasWifiEntries = false; 903 } 904 905 mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); 906 } 907 908 @Override 909 public void onSettingsActivityTriggered(Intent settingsIntent) { 910 } 911 912 private class InternetTelephonyCallback extends TelephonyCallback implements 913 TelephonyCallback.DataConnectionStateListener, 914 TelephonyCallback.DisplayInfoListener, 915 TelephonyCallback.ServiceStateListener, 916 TelephonyCallback.SignalStrengthsListener, 917 TelephonyCallback.UserMobileDataStateListener { 918 919 @Override 920 public void onServiceStateChanged(@NonNull ServiceState serviceState) { 921 mCallback.onServiceStateChanged(serviceState); 922 } 923 924 @Override 925 public void onDataConnectionStateChanged(int state, int networkType) { 926 mCallback.onDataConnectionStateChanged(state, networkType); 927 } 928 929 @Override 930 public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { 931 mCallback.onSignalStrengthsChanged(signalStrength); 932 } 933 934 @Override 935 public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { 936 mTelephonyDisplayInfo = telephonyDisplayInfo; 937 mCallback.onDisplayInfoChanged(telephonyDisplayInfo); 938 } 939 940 @Override 941 public void onUserMobileDataStateChanged(boolean enabled) { 942 mCallback.onUserMobileDataStateChanged(enabled); 943 } 944 } 945 946 private class InternetOnSubscriptionChangedListener 947 extends SubscriptionManager.OnSubscriptionsChangedListener { 948 InternetOnSubscriptionChangedListener() { 949 super(); 950 } 951 952 @Override 953 public void onSubscriptionsChanged() { 954 updateListener(); 955 } 956 } 957 958 private class DataConnectivityListener extends ConnectivityManager.NetworkCallback { 959 @Override 960 @WorkerThread 961 public void onCapabilitiesChanged(@NonNull Network network, 962 @NonNull NetworkCapabilities capabilities) { 963 mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET); 964 if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport( 965 NetworkCapabilities.TRANSPORT_WIFI))) { 966 scanWifiAccessPoints(); 967 } 968 // update UI 969 mCallback.onCapabilitiesChanged(network, capabilities); 970 } 971 972 @Override 973 @WorkerThread 974 public void onLost(@NonNull Network network) { 975 mHasEthernet = false; 976 mCallback.onLost(network); 977 } 978 } 979 980 /** 981 * Helper class for monitoring the Internet access of the connected WifiEntry. 982 */ 983 @VisibleForTesting 984 protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback { 985 986 private WifiEntry mWifiEntry; 987 988 public void registerCallbackIfNeed(WifiEntry entry) { 989 if (entry == null || mWifiEntry != null) { 990 return; 991 } 992 // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet 993 // access. Then we don't need to listen to the callback to update the Wi-Fi entries. 994 if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED 995 || (entry.isDefaultNetwork() && entry.hasInternetAccess())) { 996 return; 997 } 998 mWifiEntry = entry; 999 entry.setListener(this); 1000 } 1001 1002 public void unregisterCallback() { 1003 if (mWifiEntry == null) { 1004 return; 1005 } 1006 mWifiEntry.setListener(null); 1007 mWifiEntry = null; 1008 } 1009 1010 @MainThread 1011 @Override 1012 public void onUpdated() { 1013 if (mWifiEntry == null) { 1014 return; 1015 } 1016 WifiEntry entry = mWifiEntry; 1017 if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) { 1018 unregisterCallback(); 1019 return; 1020 } 1021 if (entry.isDefaultNetwork() && entry.hasInternetAccess()) { 1022 unregisterCallback(); 1023 // Trigger onAccessPointsChanged() to update the Wi-Fi entries. 1024 scanWifiAccessPoints(); 1025 } 1026 } 1027 } 1028 1029 /** 1030 * Return {@code true} If the Ethernet exists 1031 */ 1032 @MainThread 1033 public boolean hasEthernet() { 1034 return mHasEthernet; 1035 } 1036 1037 private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() { 1038 @Override 1039 public void onReceive(Context context, Intent intent) { 1040 final String action = intent.getAction(); 1041 if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) { 1042 if (DEBUG) { 1043 Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED"); 1044 } 1045 mConfig = MobileMappings.Config.readConfig(context); 1046 updateListener(); 1047 } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) { 1048 updateListener(); 1049 } 1050 } 1051 }; 1052 1053 private void updateListener() { 1054 int defaultDataSubId = getDefaultDataSubscriptionId(); 1055 if (mDefaultDataSubId == getDefaultDataSubscriptionId()) { 1056 if (DEBUG) { 1057 Log.d(TAG, "DDS: no change"); 1058 } 1059 return; 1060 } 1061 1062 mDefaultDataSubId = defaultDataSubId; 1063 if (DEBUG) { 1064 Log.d(TAG, "DDS: defaultDataSubId:" + mDefaultDataSubId); 1065 } 1066 if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) { 1067 mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback); 1068 mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); 1069 mTelephonyManager.registerTelephonyCallback(mHandler::post, 1070 mInternetTelephonyCallback); 1071 mCallback.onSubscriptionsChanged(mDefaultDataSubId); 1072 } 1073 } 1074 1075 public WifiUtils.InternetIconInjector getWifiIconInjector() { 1076 return mWifiIconInjector; 1077 } 1078 1079 interface InternetDialogCallback { 1080 1081 void onRefreshCarrierInfo(); 1082 1083 void onSimStateChanged(); 1084 1085 void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities); 1086 1087 void onLost(@NonNull Network network); 1088 1089 void onSubscriptionsChanged(int defaultDataSubId); 1090 1091 void onServiceStateChanged(ServiceState serviceState); 1092 1093 void onDataConnectionStateChanged(int state, int networkType); 1094 1095 void onSignalStrengthsChanged(SignalStrength signalStrength); 1096 1097 void onUserMobileDataStateChanged(boolean enabled); 1098 1099 void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo); 1100 1101 void dismissDialog(); 1102 1103 void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries, 1104 @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries); 1105 } 1106 1107 void makeOverlayToast(int stringId) { 1108 final Resources res = mContext.getResources(); 1109 1110 final SystemUIToast systemUIToast = mToastFactory.createToast(mContext, 1111 res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(), 1112 res.getConfiguration().orientation); 1113 if (systemUIToast == null) { 1114 return; 1115 } 1116 1117 View toastView = systemUIToast.getView(); 1118 1119 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 1120 params.height = WindowManager.LayoutParams.WRAP_CONTENT; 1121 params.width = WindowManager.LayoutParams.WRAP_CONTENT; 1122 params.format = PixelFormat.TRANSLUCENT; 1123 params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; 1124 params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 1125 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1126 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 1127 params.y = systemUIToast.getYOffset(); 1128 1129 int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(), 1130 res.getConfiguration().getLayoutDirection()); 1131 params.gravity = absGravity; 1132 if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { 1133 params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT; 1134 } 1135 if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { 1136 params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT; 1137 } 1138 1139 mWindowManager.addView(toastView, params); 1140 1141 Animator inAnimator = systemUIToast.getInAnimation(); 1142 if (inAnimator != null) { 1143 inAnimator.start(); 1144 } 1145 1146 mHandler.postDelayed(new Runnable() { 1147 @Override 1148 public void run() { 1149 Animator outAnimator = systemUIToast.getOutAnimation(); 1150 if (outAnimator != null) { 1151 outAnimator.start(); 1152 outAnimator.addListener(new AnimatorListenerAdapter() { 1153 @Override 1154 public void onAnimationEnd(Animator animator) { 1155 mWindowManager.removeViewImmediate(toastView); 1156 } 1157 }); 1158 } 1159 } 1160 }, SHORT_DURATION_TIMEOUT); 1161 } 1162 } 1163