diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0687d060ceec9..f11783e5dc2d4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -839,6 +839,8 @@
New user
Wi-Fi
+
+ Internet
Not Connected
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 0d0d01249c3d3..ac3fd4b519319 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -28,6 +28,7 @@ import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import com.android.internal.logging.InstanceId;
@@ -444,6 +445,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D
final ArrayList tiles = new ArrayList();
boolean addedDefault = false;
Set addedSpecs = new ArraySet<>();
+ // TODO(b/174753536): Move it into the config file.
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ tiles.add("internet");
+ addedSpecs.add("internet");
+ }
for (String tile : tileList.split(",")) {
tile = tile.trim();
if (tile.isEmpty()) continue;
@@ -459,6 +465,12 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, D
addedDefault = true;
}
} else {
+ // TODO(b/174753536): Move it into the config file.
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ if (tile.equals("wifi") || tile.equals("cell")) {
+ continue;
+ }
+ }
if (!addedSpecs.contains(tile)) {
tiles.add(tile);
addedSpecs.add(tile);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 24c0fd76d8276..e9d481b2e7f02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -37,6 +37,7 @@ import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.InternetTile;
import com.android.systemui.qs.tiles.LocationTile;
import com.android.systemui.qs.tiles.NfcTile;
import com.android.systemui.qs.tiles.NightDisplayTile;
@@ -60,6 +61,7 @@ public class QSFactoryImpl implements QSFactory {
private static final String TAG = "QSFactory";
private final Provider mWifiTileProvider;
+ private final Provider mInternetTileProvider;
private final Provider mBluetoothTileProvider;
private final Provider mCellularTileProvider;
private final Provider mDndTileProvider;
@@ -89,6 +91,7 @@ public class QSFactoryImpl implements QSFactory {
Lazy qsHostLazy,
Provider customTileBuilderProvider,
Provider wifiTileProvider,
+ Provider internetTileProvider,
Provider bluetoothTileProvider,
Provider cellularTileProvider,
Provider dndTileProvider,
@@ -113,6 +116,7 @@ public class QSFactoryImpl implements QSFactory {
mCustomTileBuilderProvider = customTileBuilderProvider;
mWifiTileProvider = wifiTileProvider;
+ mInternetTileProvider = internetTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
mCellularTileProvider = cellularTileProvider;
mDndTileProvider = dndTileProvider;
@@ -148,6 +152,8 @@ public class QSFactoryImpl implements QSFactory {
switch (tileSpec) {
case "wifi":
return mWifiTileProvider.get();
+ case "internet":
+ return mInternetTileProvider.get();
case "bt":
return mBluetoothTileProvider.get();
case "cell":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
new file mode 100644
index 0000000000000..86524f5408c01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTile.Icon;
+import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.AlphaControlledSignalTileView;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.WifiIcons;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Internet **/
+public class InternetTile extends QSTileImpl {
+ private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+
+ protected final NetworkController mController;
+ private final DataUsageController mDataController;
+ private final QSTile.SignalState mStateBeforeClick = newTileState();
+ // The last updated tile state, 0: mobile, 1: wifi
+ private int mLastTileState = -1;
+
+ protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+
+ @Inject
+ public InternetTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
+ mController = networkController;
+ mDataController = mController.getMobileDataController();
+ mController.observe(getLifecycle(), mSignalCallback);
+ }
+
+ @Override
+ public SignalState newTileState() {
+ return new SignalState();
+ }
+
+ @Override
+ public QSIconView createTileView(Context context) {
+ return new AlphaControlledSignalTileView(context);
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return WIFI_SETTINGS;
+ }
+
+ @Override
+ protected void handleClick() {
+ mActivityStarter.postStartActivityDismissingKeyguard(WIFI_SETTINGS, 0);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_internet_label);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_WIFI;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)
+ || (mController.hasMobileDataFeature()
+ && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
+ }
+
+ private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+ return isTransient
+ ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
+ : statusLabel;
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string == null) return null;
+ final int length = string.length();
+ if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+ return string.substring(1, length - 1);
+ }
+ return string;
+ }
+
+ private static final class WifiCallbackInfo {
+ boolean mEnabled;
+ boolean mConnected;
+ int mWifiSignalIconId;
+ String mSsid;
+ boolean mActivityIn;
+ boolean mActivityOut;
+ String mWifiSignalContentDescription;
+ boolean mIsTransient;
+ public String mStatusLabel;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("WifiCallbackInfo[")
+ .append("mEnabled=").append(mEnabled)
+ .append(",mConnected=").append(mConnected)
+ .append(",mWifiSignalIconId=").append(mWifiSignalIconId)
+ .append(",mSsid=").append(mSsid)
+ .append(",mActivityIn=").append(mActivityIn)
+ .append(",mActivityOut=").append(mActivityOut)
+ .append(",mWifiSignalContentDescription=").append(mWifiSignalContentDescription)
+ .append(",mIsTransient=").append(mIsTransient)
+ .append(']').toString();
+ }
+ }
+
+ private static final class CellularCallbackInfo {
+ boolean mAirplaneModeEnabled;
+ CharSequence mDataSubscriptionName;
+ CharSequence mDataContentDescription;
+ int mMobileSignalIconId;
+ boolean mActivityIn;
+ boolean mActivityOut;
+ boolean mNoSim;
+ boolean mRoaming;
+ boolean mMultipleSubs;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("CellularCallbackInfo[")
+ .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled)
+ .append(",mDataSubscriptionName=").append(mDataSubscriptionName)
+ .append(",mDataContentDescription=").append(mDataContentDescription)
+ .append(",mMobileSignalIconId=").append(mMobileSignalIconId)
+ .append(",mActivityIn=").append(mActivityIn)
+ .append(",mActivityOut=").append(mActivityOut)
+ .append(",mNoSim=").append(mNoSim)
+ .append(",mRoaming=").append(mRoaming)
+ .append(",mMultipleSubs=").append(mMultipleSubs)
+ .append(']').toString();
+ }
+ }
+
+ protected final class InternetSignalCallback implements SignalCallback {
+ final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
+ final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+
+ @Override
+ public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
+ boolean activityIn, boolean activityOut, String description, boolean isTransient,
+ String statusLabel) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "setWifiIndicators: "
+ + "enabled = " + enabled + ","
+ + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+ + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+ + "activityIn = " + activityIn + ","
+ + "activityOut = " + activityOut + ","
+ + "description = " + description + ","
+ + "isTransient = " + isTransient + ","
+ + "statusLabel = " + statusLabel);
+ }
+ mWifiInfo.mEnabled = enabled;
+ mWifiInfo.mConnected = qsIcon.visible;
+ mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+ mWifiInfo.mSsid = description;
+ mWifiInfo.mActivityIn = activityIn;
+ mWifiInfo.mActivityOut = activityOut;
+ mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
+ mWifiInfo.mIsTransient = isTransient;
+ mWifiInfo.mStatusLabel = statusLabel;
+ refreshState(mWifiInfo);
+ }
+
+ @Override
+ public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+ int qsType, boolean activityIn, boolean activityOut,
+ CharSequence typeContentDescription,
+ CharSequence typeContentDescriptionHtml, CharSequence description,
+ boolean isWide, int subId, boolean roaming) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "setMobileDataIndicators: "
+ + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+ + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+ + "statusType = " + statusType + ","
+ + "qsType = " + qsType + ","
+ + "activityIn = " + activityIn + ","
+ + "activityOut = " + activityOut + ","
+ + "typeContentDescription = " + typeContentDescription + ","
+ + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + ","
+ + "description = " + description + ","
+ + "isWide = " + isWide + ","
+ + "subId = " + subId + ","
+ + "roaming = " + roaming);
+ }
+ if (qsIcon == null) {
+ // Not data sim, don't display.
+ return;
+ }
+ mCellularInfo.mDataSubscriptionName = mController.getMobileDataNetworkName();
+ mCellularInfo.mDataContentDescription =
+ (description != null) ? typeContentDescriptionHtml : null;
+ mCellularInfo.mMobileSignalIconId = qsIcon.icon;
+ mCellularInfo.mActivityIn = activityIn;
+ mCellularInfo.mActivityOut = activityOut;
+ mCellularInfo.mRoaming = roaming;
+ mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1;
+ refreshState(mCellularInfo);
+ }
+
+ @Override
+ public void setNoSims(boolean show, boolean simDetected) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "setNoSims: "
+ + "show = " + show + ","
+ + "simDetected = " + simDetected);
+ }
+ mCellularInfo.mNoSim = show;
+ if (mCellularInfo.mNoSim) {
+ // Make sure signal gets cleared out when no sims.
+ mCellularInfo.mMobileSignalIconId = 0;
+ }
+ refreshState(mCellularInfo);
+ }
+
+ @Override
+ public void setIsAirplaneMode(IconState icon) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "InternetTile-setIsAirplaneMode: "
+ + "icon = " + (icon == null ? "" : icon.toString()));
+ }
+ mCellularInfo.mAirplaneModeEnabled = icon.visible;
+ refreshState(mCellularInfo);
+ }
+ }
+
+ @Override
+ protected void handleUpdateState(SignalState state, Object arg) {
+ Log.d(TAG, "handleUpdateState: " + "arg = " + arg);
+ if (arg instanceof CellularCallbackInfo) {
+ mLastTileState = 0;
+ handleUpdateCellularState(state, arg);
+ } else if (arg instanceof WifiCallbackInfo) {
+ mLastTileState = 1;
+ handleUpdateWifiState(state, arg);
+ } else {
+ // handleUpdateState will be triggered when user expands the QuickSetting panel with
+ // arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
+ // should be used to refresh the tile.
+ if (mLastTileState == 0) {
+ handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
+ } else if (mLastTileState == 1) {
+ handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+ }
+ }
+ }
+
+ private void handleUpdateWifiState(SignalState state, Object arg) {
+ WifiCallbackInfo cb = (WifiCallbackInfo) arg;
+ boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
+ boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
+ boolean enabledChanging = state.value != cb.mEnabled;
+ if (enabledChanging) {
+ fireToggleStateChanged(cb.mEnabled);
+ }
+ if (state.slash == null) {
+ state.slash = new SlashState();
+ state.slash.rotation = 6;
+ }
+ state.slash.isSlashed = false;
+ state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid));
+ state.state = Tile.STATE_ACTIVE;
+ state.dualTarget = true;
+ state.value = cb.mEnabled;
+ state.activityIn = cb.mEnabled && cb.mActivityIn;
+ state.activityOut = cb.mEnabled && cb.mActivityOut;
+ final StringBuffer minimalContentDescription = new StringBuffer();
+ final StringBuffer minimalStateDescription = new StringBuffer();
+ final Resources r = mContext.getResources();
+ // TODO(b/174753536): Use the new "Internet" string as state.label once available.
+ if (cb.mIsTransient) {
+ state.icon = ResourceIcon.get(
+ com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ } else if (!state.value) {
+ state.slash.isSlashed = true;
+ state.state = Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ } else if (wifiConnected) {
+ state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ } else if (wifiNotConnected) {
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ } else {
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ }
+ minimalContentDescription.append(
+ mContext.getString(R.string.quick_settings_internet_label)).append(",");
+ if (state.value) {
+ if (wifiConnected) {
+ minimalStateDescription.append(cb.mWifiSignalContentDescription);
+ minimalContentDescription.append(removeDoubleQuotes(cb.mSsid));
+ if (!TextUtils.isEmpty(state.secondaryLabel)) {
+ minimalContentDescription.append(",").append(state.secondaryLabel);
+ }
+ }
+ }
+ state.stateDescription = minimalStateDescription.toString();
+ state.contentDescription = minimalContentDescription.toString();
+ state.dualLabelContentDescription = r.getString(
+ R.string.accessibility_quick_settings_open_settings, getTileLabel());
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ }
+
+ private void handleUpdateCellularState(SignalState state, Object arg) {
+ CellularCallbackInfo cb = (CellularCallbackInfo) arg;
+ final Resources r = mContext.getResources();
+ // TODO(b/174753536): Use the new "Internet" string as state.label once available.
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ boolean mobileDataEnabled = mDataController.isMobileDataSupported()
+ && mDataController.isMobileDataEnabled();
+ state.value = mobileDataEnabled;
+ state.activityIn = mobileDataEnabled && cb.mActivityIn;
+ state.activityOut = mobileDataEnabled && cb.mActivityOut;
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ if (cb.mNoSim) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
+ } else {
+ state.icon = new SignalIcon(cb.mMobileSignalIconId);
+ }
+
+ if (cb.mNoSim) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
+ } else if (cb.mAirplaneModeEnabled) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ state.secondaryLabel = r.getString(R.string.status_bar_airplane);
+ } else if (mobileDataEnabled) {
+ state.state = Tile.STATE_ACTIVE;
+ state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
+ getMobileDataContentName(cb));
+ } else {
+ state.state = Tile.STATE_INACTIVE;
+ state.secondaryLabel = r.getString(R.string.cell_data_off);
+ }
+
+ state.contentDescription = state.label;
+ if (state.state == Tile.STATE_INACTIVE) {
+ // This information is appended later by converting the Tile.STATE_INACTIVE state.
+ state.stateDescription = "";
+ } else {
+ state.stateDescription = state.secondaryLabel;
+ }
+ }
+
+ private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+ if (TextUtils.isEmpty(dataType)) {
+ return Html.fromHtml((current == null ? "" : current.toString()), 0);
+ }
+ if (TextUtils.isEmpty(current)) {
+ return Html.fromHtml((dataType == null ? "" : dataType.toString()), 0);
+ }
+ String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
+ return Html.fromHtml(concat, 0);
+ }
+
+ private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
+ if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
+ String roaming = mContext.getString(R.string.data_connection_roaming);
+ String dataDescription =
+ cb.mDataContentDescription == null ? ""
+ : cb.mDataContentDescription.toString();
+ return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
+ }
+ if (cb.mRoaming) {
+ return mContext.getString(R.string.data_connection_roaming);
+ }
+ return cb.mDataContentDescription;
+ }
+
+ private static class SignalIcon extends Icon {
+ private final int mState;
+ SignalIcon(int state) {
+ mState = state;
+ }
+ public int getState() {
+ return mState;
+ }
+
+ @Override
+ public Drawable getDrawable(Context context) {
+ SignalDrawable d = new SignalDrawable(context);
+ d.setLevel(getState());
+ return d;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 8722fecdad964..f92860b701168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -104,6 +104,15 @@ public interface NetworkController extends CallbackController, D
Context context) {
this(visible, icon, context.getString(contentDescription));
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ return builder.append("[visible=").append(visible).append(',')
+ .append("icon=").append(icon).append(',')
+ .append("contentDescription=").append(contentDescription).append(']')
+ .toString();
+ }
}
/**