blob: cb87f599f5a0df8251d77ed490ae829f65fa8b92 [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.qs.tiles;
18
Amin Shaikh329c8282018-03-09 14:50:59 -050019import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
20
Jason Monk54ff0ef2017-07-25 14:44:28 -040021import android.app.AlertDialog;
Jason Monk9ca18482017-08-01 10:38:02 -040022import android.app.AlertDialog.Builder;
John Spurlockaf8d6c42014-05-07 17:49:08 -040023import android.content.Context;
24import android.content.Intent;
25import android.content.res.Resources;
Rohan Shah53f3f152018-02-28 15:13:31 -080026import android.provider.Settings;
Jason Monk32508852017-01-18 09:17:13 -050027import android.service.quicksettings.Tile;
Fabian Kozynski2594b932019-04-11 12:22:39 -040028import android.telephony.SubscriptionManager;
Evan Laird83c87e52019-09-24 19:14:05 -040029import android.text.Html;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -050030import android.text.TextUtils;
John Spurlockb98f7472014-07-08 17:09:42 -040031import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
Jason Monk9ca18482017-08-01 10:38:02 -040034import android.view.WindowManager.LayoutParams;
Jason Monke5b770e2017-03-03 21:49:29 -050035import android.widget.Switch;
Amin Shaikh0e003312018-03-08 11:39:01 -050036
Chris Wren457a21c2015-05-06 17:50:34 -040037import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010038import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkf668d7c2016-01-14 10:38:41 -050039import com.android.settingslib.net.DataUsageController;
Amin Shaikh329c8282018-03-09 14:50:59 -050040import com.android.systemui.Prefs;
John Spurlockaf8d6c42014-05-07 17:49:08 -040041import com.android.systemui.R;
Jason Monkec34da82017-02-24 15:57:05 -050042import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050043import com.android.systemui.plugins.qs.DetailAdapter;
Fabian Kozynski29140f72019-07-22 12:26:51 -040044import com.android.systemui.plugins.qs.QSIconView;
Jason Monk702e2eb2017-03-03 16:53:44 -050045import com.android.systemui.plugins.qs.QSTile.SignalState;
Jason Monke5b770e2017-03-03 21:49:29 -050046import com.android.systemui.qs.QSHost;
Fabian Kozynski29140f72019-07-22 12:26:51 -040047import com.android.systemui.qs.SignalTileView;
Jason Monk702e2eb2017-03-03 16:53:44 -050048import com.android.systemui.qs.tileimpl.QSTileImpl;
Jason Monk54ff0ef2017-07-25 14:44:28 -040049import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlockaf8d6c42014-05-07 17:49:08 -040050import com.android.systemui.statusbar.policy.NetworkController;
Jason Monk07b75fe2015-05-14 16:47:03 -040051import com.android.systemui.statusbar.policy.NetworkController.IconState;
Jason Monk0f0de132016-12-19 15:36:13 -050052import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
John Spurlockaf8d6c42014-05-07 17:49:08 -040053
Jason Monk5d577202018-12-26 15:43:06 -050054import javax.inject.Inject;
55
John Spurlockaf8d6c42014-05-07 17:49:08 -040056/** Quick settings tile: Cellular **/
Jason Monk702e2eb2017-03-03 16:53:44 -050057public class CellularTile extends QSTileImpl<SignalState> {
Rajeev Kumara8981f32017-07-19 18:12:47 -070058 private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
John Spurlockaf8d6c42014-05-07 17:49:08 -040059
60 private final NetworkController mController;
Jason Monkf668d7c2016-01-14 10:38:41 -050061 private final DataUsageController mDataController;
John Spurlockb98f7472014-07-08 17:09:42 -040062 private final CellularDetailAdapter mDetailAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040063
Jason Monk878c0932015-07-21 10:18:01 -040064 private final CellSignalCallback mSignalCallback = new CellSignalCallback();
Jason Monk9c7844c2017-01-18 15:21:53 -050065 private final ActivityStarter mActivityStarter;
Jason Monk878c0932015-07-21 10:18:01 -040066
Jason Monk5d577202018-12-26 15:43:06 -050067 @Inject
Jason Monkc0e0e2b2018-12-26 16:47:41 -050068 public CellularTile(QSHost host, NetworkController networkController,
Fabian Kozynski88a1fe52019-03-25 14:58:55 -040069 ActivityStarter activityStarter) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040070 super(host);
Jason Monkc0e0e2b2018-12-26 16:47:41 -050071 mController = networkController;
72 mActivityStarter = activityStarter;
Jason Monkd2263cd2014-11-10 14:22:56 -050073 mDataController = mController.getMobileDataController();
John Spurlockb98f7472014-07-08 17:09:42 -040074 mDetailAdapter = new CellularDetailAdapter();
Jason Monkfa452ef2018-12-26 17:26:10 -050075 mController.observe(getLifecycle(), mSignalCallback);
John Spurlockaf8d6c42014-05-07 17:49:08 -040076 }
77
78 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050079 public SignalState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040080 return new SignalState();
81 }
82
83 @Override
Fabian Kozynski29140f72019-07-22 12:26:51 -040084 public QSIconView createTileView(Context context) {
85 return new SignalTileView(context);
86 }
87
88 @Override
John Spurlockb98f7472014-07-08 17:09:42 -040089 public DetailAdapter getDetailAdapter() {
90 return mDetailAdapter;
91 }
92
93 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -040094 public void handleSetListening(boolean listening) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040095 }
96
97 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -050098 public Intent getLongClickIntent() {
Rohan Shah53f3f152018-02-28 15:13:31 -080099 return getCellularSettingIntent();
Jason Monk76c67aa2016-02-19 14:49:42 -0500100 }
101
102 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400103 protected void handleClick() {
Evan Lairdb3f974f2017-09-07 19:02:23 -0400104 if (getState().state == Tile.STATE_UNAVAILABLE) {
105 return;
106 }
Jason Monk54ff0ef2017-07-25 14:44:28 -0400107 if (mDataController.isMobileDataEnabled()) {
Fabian Kozynski88a1fe52019-03-25 14:58:55 -0400108 maybeShowDisableDialog();
Jason Monk54ff0ef2017-07-25 14:44:28 -0400109 } else {
110 mDataController.setMobileDataEnabled(true);
111 }
112 }
113
Amin Shaikh26142a62018-04-11 15:20:32 -0400114 private void maybeShowDisableDialog() {
115 if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
116 // Directly turn off mobile data if the user has seen the dialog before.
117 mDataController.setMobileDataEnabled(false);
118 return;
119 }
Amin Shaikh329c8282018-03-09 14:50:59 -0500120 String carrierName = mController.getMobileDataNetworkName();
121 if (TextUtils.isEmpty(carrierName)) {
122 carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
123 }
Jason Monk9ca18482017-08-01 10:38:02 -0400124 AlertDialog dialog = new Builder(mContext)
Amin Shaikh329c8282018-03-09 14:50:59 -0500125 .setTitle(R.string.mobile_data_disable_title)
126 .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
Jason Monk54ff0ef2017-07-25 14:44:28 -0400127 .setNegativeButton(android.R.string.cancel, null)
128 .setPositiveButton(
129 com.android.internal.R.string.alert_windows_notification_turn_off_action,
Amin Shaikh329c8282018-03-09 14:50:59 -0500130 (d, w) -> {
131 mDataController.setMobileDataEnabled(false);
132 Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
133 })
Jason Monk9ca18482017-08-01 10:38:02 -0400134 .create();
135 dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
136 SystemUIDialog.setShowForAllUsers(dialog, true);
137 SystemUIDialog.registerDismissListener(dialog);
138 SystemUIDialog.setWindowOnTop(dialog);
139 dialog.show();
Jason Monk32508852017-01-18 09:17:13 -0500140 }
141
142 @Override
143 protected void handleSecondaryClick() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500144 if (mDataController.isMobileDataSupported()) {
John Spurlockb98f7472014-07-08 17:09:42 -0400145 showDetail(true);
146 } else {
Rajeev Kumara8981f32017-07-19 18:12:47 -0700147 mActivityStarter
Rohan Shah53f3f152018-02-28 15:13:31 -0800148 .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
John Spurlockb98f7472014-07-08 17:09:42 -0400149 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400150 }
151
152 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400153 public CharSequence getTileLabel() {
154 return mContext.getString(R.string.quick_settings_cellular_detail_title);
155 }
156
157 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400158 protected void handleUpdateState(SignalState state, Object arg) {
Jason Monk878c0932015-07-21 10:18:01 -0400159 CallbackInfo cb = (CallbackInfo) arg;
160 if (cb == null) {
161 cb = mSignalCallback.mInfo;
162 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400163
164 final Resources r = mContext.getResources();
Jason Monke5b770e2017-03-03 21:49:29 -0500165 state.label = r.getString(R.string.mobile_data);
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500166 boolean mobileDataEnabled = mDataController.isMobileDataSupported()
167 && mDataController.isMobileDataEnabled();
168 state.value = mobileDataEnabled;
Amin Shaikh0a8155e02018-09-06 17:23:32 -0400169 state.activityIn = mobileDataEnabled && cb.activityIn;
170 state.activityOut = mobileDataEnabled && cb.activityOut;
Jason Monke5b770e2017-03-03 21:49:29 -0500171 state.expandedAccessibilityClassName = Switch.class.getName();
Evan Laird4a7db952017-09-06 16:43:24 -0400172 if (cb.noSim) {
173 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
174 } else {
Amin Shaikh0e003312018-03-08 11:39:01 -0500175 state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
Evan Laird4a7db952017-09-06 16:43:24 -0400176 }
177
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500178 if (cb.noSim) {
Evan Lairdb3f974f2017-09-07 19:02:23 -0400179 state.state = Tile.STATE_UNAVAILABLE;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500180 state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
181 } else if (cb.airplaneModeEnabled) {
182 state.state = Tile.STATE_UNAVAILABLE;
183 state.secondaryLabel = r.getString(R.string.status_bar_airplane);
184 } else if (mobileDataEnabled) {
Evan Laird3d1aad62017-06-20 16:10:44 -0400185 state.state = Tile.STATE_ACTIVE;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400186 state.secondaryLabel = appendMobileDataType(
187 // Only show carrier name if there are more than 1 subscription
188 cb.multipleSubs ? cb.dataSubscriptionName : "",
189 getMobileDataContentName(cb));
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500190 } else {
191 state.state = Tile.STATE_INACTIVE;
192 state.secondaryLabel = r.getString(R.string.cell_data_off);
Evan Laird3d1aad62017-06-20 16:10:44 -0400193 }
Rohan Shah7bc37442018-04-10 00:34:50 -0400194
195
196 // TODO(b/77881974): Instead of switching out the description via a string check for
197 // we need to have two strings provided by the MobileIconGroup.
198 final CharSequence contentDescriptionSuffix;
199 if (state.state == Tile.STATE_INACTIVE) {
200 contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
201 } else {
202 contentDescriptionSuffix = state.secondaryLabel;
203 }
204
205 state.contentDescription = state.label + ", " + contentDescriptionSuffix;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400206 }
207
Fabian Kozynskic651e642019-02-21 13:20:34 -0500208 private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
209 if (TextUtils.isEmpty(dataType)) {
Evan Laird83c87e52019-09-24 19:14:05 -0400210 return Html.fromHtml(current.toString(), 0);
Fabian Kozynskic651e642019-02-21 13:20:34 -0500211 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400212 if (TextUtils.isEmpty(current)) {
Evan Laird83c87e52019-09-24 19:14:05 -0400213 return Html.fromHtml(dataType.toString(), 0);
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400214 }
Evan Laird83c87e52019-09-24 19:14:05 -0400215 String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
216 return Html.fromHtml(concat, 0);
Fabian Kozynskic651e642019-02-21 13:20:34 -0500217 }
218
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400219 private CharSequence getMobileDataContentName(CallbackInfo cb) {
220 if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500221 String roaming = mContext.getString(R.string.data_connection_roaming);
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400222 String dataDescription = cb.dataContentDescription.toString();
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500223 return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
224 }
225 if (cb.roaming) {
226 return mContext.getString(R.string.data_connection_roaming);
227 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400228 return cb.dataContentDescription;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500229 }
230
Chris Wren457a21c2015-05-06 17:50:34 -0400231 @Override
232 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500233 return MetricsEvent.QS_CELLULAR;
Chris Wren457a21c2015-05-06 17:50:34 -0400234 }
235
Jason Monkc3f42c12016-02-05 12:33:13 -0500236 @Override
237 public boolean isAvailable() {
238 return mController.hasMobileDataFeature();
239 }
240
John Spurlockaf8d6c42014-05-07 17:49:08 -0400241 private static final class CallbackInfo {
John Spurlockd5cd2fd2014-07-27 11:36:33 -0400242 boolean airplaneModeEnabled;
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500243 CharSequence dataSubscriptionName;
Fabian Kozynskic651e642019-02-21 13:20:34 -0500244 CharSequence dataContentDescription;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400245 boolean activityIn;
246 boolean activityOut;
John Spurlock899f4392014-06-11 10:59:11 -0400247 boolean noSim;
Jason Monk0f0de132016-12-19 15:36:13 -0500248 boolean roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400249 boolean multipleSubs;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400250 }
251
Jason Monk0f0de132016-12-19 15:36:13 -0500252 private final class CellSignalCallback implements SignalCallback {
Jason Monkb5746272014-11-12 16:50:31 -0500253 private final CallbackInfo mInfo = new CallbackInfo();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400254
255 @Override
Jason Monk7e6c83c2017-04-26 14:35:24 -0400256 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
Evan Laird83c87e52019-09-24 19:14:05 -0400257 int qsType, boolean activityIn, boolean activityOut,
258 CharSequence typeContentDescription,
259 CharSequence typeContentDescriptionHtml, CharSequence description,
260 boolean isWide, int subId, boolean roaming) {
Jason Monk7e6c83c2017-04-26 14:35:24 -0400261 if (qsIcon == null) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400262 // Not data sim, don't display.
263 return;
264 }
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500265 mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
Evan Laird83c87e52019-09-24 19:14:05 -0400266 mInfo.dataContentDescription =
267 (description != null) ? typeContentDescriptionHtml : null;
Jason Monkb5746272014-11-12 16:50:31 -0500268 mInfo.activityIn = activityIn;
269 mInfo.activityOut = activityOut;
Jason Monk0f0de132016-12-19 15:36:13 -0500270 mInfo.roaming = roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400271 mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
Jason Monkb5746272014-11-12 16:50:31 -0500272 refreshState(mInfo);
273 }
274
275 @Override
Jason Monk1ff77662017-09-29 11:17:05 -0400276 public void setNoSims(boolean show, boolean simDetected) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400277 mInfo.noSim = show;
Jason Monkb5746272014-11-12 16:50:31 -0500278 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400279 }
280
281 @Override
Jason Monk07b75fe2015-05-14 16:47:03 -0400282 public void setIsAirplaneMode(IconState icon) {
283 mInfo.airplaneModeEnabled = icon.visible;
Jason Monkb5746272014-11-12 16:50:31 -0500284 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400285 }
John Spurlockb98f7472014-07-08 17:09:42 -0400286
Jason Monk07b75fe2015-05-14 16:47:03 -0400287 @Override
288 public void setMobileDataEnabled(boolean enabled) {
John Spurlockb98f7472014-07-08 17:09:42 -0400289 mDetailAdapter.setMobileDataEnabled(enabled);
290 }
Rajeev Kumara8981f32017-07-19 18:12:47 -0700291 }
292
Rohan Shah53f3f152018-02-28 15:13:31 -0800293 static Intent getCellularSettingIntent() {
Fabian Kozynski2594b932019-04-11 12:22:39 -0400294 Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
295 int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
296 if (dataSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
297 intent.putExtra(Settings.EXTRA_SUB_ID,
298 SubscriptionManager.getDefaultDataSubscriptionId());
299 }
300 return intent;
Rajeev Kumara8981f32017-07-19 18:12:47 -0700301 }
John Spurlockb98f7472014-07-08 17:09:42 -0400302
303 private final class CellularDetailAdapter implements DetailAdapter {
John Spurlockb98f7472014-07-08 17:09:42 -0400304
305 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500306 public CharSequence getTitle() {
307 return mContext.getString(R.string.quick_settings_cellular_detail_title);
John Spurlockb98f7472014-07-08 17:09:42 -0400308 }
309
310 @Override
311 public Boolean getToggleState() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500312 return mDataController.isMobileDataSupported()
Jason Monkb5746272014-11-12 16:50:31 -0500313 ? mDataController.isMobileDataEnabled()
314 : null;
John Spurlockb98f7472014-07-08 17:09:42 -0400315 }
316
317 @Override
318 public Intent getSettingsIntent() {
Rohan Shah53f3f152018-02-28 15:13:31 -0800319 return getCellularSettingIntent();
John Spurlockb98f7472014-07-08 17:09:42 -0400320 }
321
322 @Override
323 public void setToggleState(boolean state) {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500324 MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
Jason Monkd2263cd2014-11-10 14:22:56 -0500325 mDataController.setMobileDataEnabled(state);
John Spurlockb98f7472014-07-08 17:09:42 -0400326 }
327
328 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400329 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500330 return MetricsEvent.QS_DATAUSAGEDETAIL;
Chris Wren457a21c2015-05-06 17:50:34 -0400331 }
332
333 @Override
John Spurlockb98f7472014-07-08 17:09:42 -0400334 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200335 final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
336 ? convertView
337 : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
Jason Monkf668d7c2016-01-14 10:38:41 -0500338 final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
John Spurlockb98f7472014-07-08 17:09:42 -0400339 if (info == null) return v;
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200340 v.bind(info);
Jason Monk0f0de132016-12-19 15:36:13 -0500341 v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
342 ? View.VISIBLE : View.INVISIBLE);
John Spurlockb98f7472014-07-08 17:09:42 -0400343 return v;
344 }
345
346 public void setMobileDataEnabled(boolean enabled) {
347 fireToggleStateChanged(enabled);
348 }
John Spurlockb98f7472014-07-08 17:09:42 -0400349 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400350}