2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.qs.tiles;
19 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
21 import android.app.AlertDialog;
22 import android.app.AlertDialog.Builder;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Resources;
26 import android.provider.Settings;
27 import android.service.quicksettings.Tile;
28 import android.telephony.SubscriptionManager;
29 import android.text.TextUtils;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.WindowManager.LayoutParams;
34 import android.widget.Switch;
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
38 import com.android.settingslib.net.DataUsageController;
39 import com.android.systemui.Prefs;
40 import com.android.systemui.R;
41 import com.android.systemui.plugins.ActivityStarter;
42 import com.android.systemui.plugins.qs.DetailAdapter;
43 import com.android.systemui.plugins.qs.QSTile.SignalState;
44 import com.android.systemui.qs.QSHost;
45 import com.android.systemui.qs.tileimpl.QSTileImpl;
46 import com.android.systemui.statusbar.phone.SystemUIDialog;
47 import com.android.systemui.statusbar.policy.NetworkController;
48 import com.android.systemui.statusbar.policy.NetworkController.IconState;
49 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
51 import javax.inject.Inject;
53 /** Quick settings tile: Cellular **/
54 public class CellularTile extends QSTileImpl<SignalState> {
55 private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
57 private final NetworkController mController;
58 private final DataUsageController mDataController;
59 private final CellularDetailAdapter mDetailAdapter;
61 private final CellSignalCallback mSignalCallback = new CellSignalCallback();
62 private final ActivityStarter mActivityStarter;
65 public CellularTile(QSHost host, NetworkController networkController,
66 ActivityStarter activityStarter) {
68 mController = networkController;
69 mActivityStarter = activityStarter;
70 mDataController = mController.getMobileDataController();
71 mDetailAdapter = new CellularDetailAdapter();
72 mController.observe(getLifecycle(), mSignalCallback);
76 public SignalState newTileState() {
77 return new SignalState();
81 public DetailAdapter getDetailAdapter() {
82 return mDetailAdapter;
86 public void handleSetListening(boolean listening) {
90 public Intent getLongClickIntent() {
91 return getCellularSettingIntent();
95 protected void handleClick() {
96 if (getState().state == Tile.STATE_UNAVAILABLE) {
99 if (mDataController.isMobileDataEnabled()) {
100 maybeShowDisableDialog();
102 mDataController.setMobileDataEnabled(true);
106 private void maybeShowDisableDialog() {
107 if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
108 // Directly turn off mobile data if the user has seen the dialog before.
109 mDataController.setMobileDataEnabled(false);
112 String carrierName = mController.getMobileDataNetworkName();
113 if (TextUtils.isEmpty(carrierName)) {
114 carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
116 AlertDialog dialog = new Builder(mContext)
117 .setTitle(R.string.mobile_data_disable_title)
118 .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
119 .setNegativeButton(android.R.string.cancel, null)
121 com.android.internal.R.string.alert_windows_notification_turn_off_action,
123 mDataController.setMobileDataEnabled(false);
124 Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
127 dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
128 SystemUIDialog.setShowForAllUsers(dialog, true);
129 SystemUIDialog.registerDismissListener(dialog);
130 SystemUIDialog.setWindowOnTop(dialog);
135 protected void handleSecondaryClick() {
136 if (mDataController.isMobileDataSupported()) {
140 .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
145 public CharSequence getTileLabel() {
146 return mContext.getString(R.string.quick_settings_cellular_detail_title);
150 protected void handleUpdateState(SignalState state, Object arg) {
151 CallbackInfo cb = (CallbackInfo) arg;
153 cb = mSignalCallback.mInfo;
156 final Resources r = mContext.getResources();
157 state.label = r.getString(R.string.mobile_data);
158 boolean mobileDataEnabled = mDataController.isMobileDataSupported()
159 && mDataController.isMobileDataEnabled();
160 state.value = mobileDataEnabled;
161 state.activityIn = mobileDataEnabled && cb.activityIn;
162 state.activityOut = mobileDataEnabled && cb.activityOut;
163 state.expandedAccessibilityClassName = Switch.class.getName();
165 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
167 state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
171 state.state = Tile.STATE_UNAVAILABLE;
172 state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
173 } else if (cb.airplaneModeEnabled) {
174 state.state = Tile.STATE_UNAVAILABLE;
175 state.secondaryLabel = r.getString(R.string.status_bar_airplane);
176 } else if (mobileDataEnabled) {
177 state.state = Tile.STATE_ACTIVE;
178 state.secondaryLabel = appendMobileDataType(
179 // Only show carrier name if there are more than 1 subscription
180 cb.multipleSubs ? cb.dataSubscriptionName : "",
181 getMobileDataContentName(cb));
183 state.state = Tile.STATE_INACTIVE;
184 state.secondaryLabel = r.getString(R.string.cell_data_off);
188 // TODO(b/77881974): Instead of switching out the description via a string check for
189 // we need to have two strings provided by the MobileIconGroup.
190 final CharSequence contentDescriptionSuffix;
191 if (state.state == Tile.STATE_INACTIVE) {
192 contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
194 contentDescriptionSuffix = state.secondaryLabel;
197 state.contentDescription = state.label + ", " + contentDescriptionSuffix;
200 private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
201 if (TextUtils.isEmpty(dataType)) {
204 if (TextUtils.isEmpty(current)) {
207 return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
210 private CharSequence getMobileDataContentName(CallbackInfo cb) {
211 if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
212 String roaming = mContext.getString(R.string.data_connection_roaming);
213 String dataDescription = cb.dataContentDescription.toString();
214 return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
217 return mContext.getString(R.string.data_connection_roaming);
219 return cb.dataContentDescription;
223 public int getMetricsCategory() {
224 return MetricsEvent.QS_CELLULAR;
228 public boolean isAvailable() {
229 return mController.hasMobileDataFeature();
232 private static final class CallbackInfo {
233 boolean airplaneModeEnabled;
234 CharSequence dataSubscriptionName;
235 CharSequence dataContentDescription;
240 boolean multipleSubs;
243 private final class CellSignalCallback implements SignalCallback {
244 private final CallbackInfo mInfo = new CallbackInfo();
247 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
248 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
249 String description, boolean isWide, int subId, boolean roaming) {
250 if (qsIcon == null) {
251 // Not data sim, don't display.
254 mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
255 mInfo.dataContentDescription = (description != null) ? typeContentDescription : null;
256 mInfo.activityIn = activityIn;
257 mInfo.activityOut = activityOut;
258 mInfo.roaming = roaming;
259 mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
264 public void setNoSims(boolean show, boolean simDetected) {
270 public void setIsAirplaneMode(IconState icon) {
271 mInfo.airplaneModeEnabled = icon.visible;
276 public void setMobileDataEnabled(boolean enabled) {
277 mDetailAdapter.setMobileDataEnabled(enabled);
281 static Intent getCellularSettingIntent() {
282 Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
283 int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
284 if (dataSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
285 intent.putExtra(Settings.EXTRA_SUB_ID,
286 SubscriptionManager.getDefaultDataSubscriptionId());
291 private final class CellularDetailAdapter implements DetailAdapter {
294 public CharSequence getTitle() {
295 return mContext.getString(R.string.quick_settings_cellular_detail_title);
299 public Boolean getToggleState() {
300 return mDataController.isMobileDataSupported()
301 ? mDataController.isMobileDataEnabled()
306 public Intent getSettingsIntent() {
307 return getCellularSettingIntent();
311 public void setToggleState(boolean state) {
312 MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
313 mDataController.setMobileDataEnabled(state);
317 public int getMetricsCategory() {
318 return MetricsEvent.QS_DATAUSAGEDETAIL;
322 public View createDetailView(Context context, View convertView, ViewGroup parent) {
323 final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
325 : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
326 final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
327 if (info == null) return v;
329 v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
330 ? View.VISIBLE : View.INVISIBLE);
334 public void setMobileDataEnabled(boolean enabled) {
335 fireToggleStateChanged(enabled);