blob: 20069ead5e8db3ff8505decac2baf37cd0b7310e [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;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -050029import android.text.TextUtils;
John Spurlockb98f7472014-07-08 17:09:42 -040030import android.view.LayoutInflater;
31import android.view.View;
32import android.view.ViewGroup;
Jason Monk9ca18482017-08-01 10:38:02 -040033import android.view.WindowManager.LayoutParams;
Jason Monke5b770e2017-03-03 21:49:29 -050034import android.widget.Switch;
Amin Shaikh0e003312018-03-08 11:39:01 -050035
Chris Wren457a21c2015-05-06 17:50:34 -040036import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010037import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkf668d7c2016-01-14 10:38:41 -050038import com.android.settingslib.net.DataUsageController;
Amin Shaikh329c8282018-03-09 14:50:59 -050039import com.android.systemui.Prefs;
John Spurlockaf8d6c42014-05-07 17:49:08 -040040import com.android.systemui.R;
Jason Monkec34da82017-02-24 15:57:05 -050041import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050042import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monk702e2eb2017-03-03 16:53:44 -050043import com.android.systemui.plugins.qs.QSTile.SignalState;
Jason Monke5b770e2017-03-03 21:49:29 -050044import com.android.systemui.qs.QSHost;
Jason Monk702e2eb2017-03-03 16:53:44 -050045import com.android.systemui.qs.tileimpl.QSTileImpl;
Jason Monk54ff0ef2017-07-25 14:44:28 -040046import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlockaf8d6c42014-05-07 17:49:08 -040047import com.android.systemui.statusbar.policy.NetworkController;
Jason Monk07b75fe2015-05-14 16:47:03 -040048import com.android.systemui.statusbar.policy.NetworkController.IconState;
Jason Monk0f0de132016-12-19 15:36:13 -050049import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
John Spurlockaf8d6c42014-05-07 17:49:08 -040050
Jason Monk5d577202018-12-26 15:43:06 -050051import javax.inject.Inject;
52
John Spurlockaf8d6c42014-05-07 17:49:08 -040053/** Quick settings tile: Cellular **/
Jason Monk702e2eb2017-03-03 16:53:44 -050054public class CellularTile extends QSTileImpl<SignalState> {
Rajeev Kumara8981f32017-07-19 18:12:47 -070055 private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
John Spurlockaf8d6c42014-05-07 17:49:08 -040056
57 private final NetworkController mController;
Jason Monkf668d7c2016-01-14 10:38:41 -050058 private final DataUsageController mDataController;
John Spurlockb98f7472014-07-08 17:09:42 -040059 private final CellularDetailAdapter mDetailAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040060
Jason Monk878c0932015-07-21 10:18:01 -040061 private final CellSignalCallback mSignalCallback = new CellSignalCallback();
Jason Monk9c7844c2017-01-18 15:21:53 -050062 private final ActivityStarter mActivityStarter;
Jason Monk878c0932015-07-21 10:18:01 -040063
Jason Monk5d577202018-12-26 15:43:06 -050064 @Inject
Jason Monkc0e0e2b2018-12-26 16:47:41 -050065 public CellularTile(QSHost host, NetworkController networkController,
Fabian Kozynski88a1fe52019-03-25 14:58:55 -040066 ActivityStarter activityStarter) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040067 super(host);
Jason Monkc0e0e2b2018-12-26 16:47:41 -050068 mController = networkController;
69 mActivityStarter = activityStarter;
Jason Monkd2263cd2014-11-10 14:22:56 -050070 mDataController = mController.getMobileDataController();
John Spurlockb98f7472014-07-08 17:09:42 -040071 mDetailAdapter = new CellularDetailAdapter();
Jason Monkfa452ef2018-12-26 17:26:10 -050072 mController.observe(getLifecycle(), mSignalCallback);
John Spurlockaf8d6c42014-05-07 17:49:08 -040073 }
74
75 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050076 public SignalState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040077 return new SignalState();
78 }
79
80 @Override
John Spurlockb98f7472014-07-08 17:09:42 -040081 public DetailAdapter getDetailAdapter() {
82 return mDetailAdapter;
83 }
84
85 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -040086 public void handleSetListening(boolean listening) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040087 }
88
89 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -050090 public Intent getLongClickIntent() {
Rohan Shah53f3f152018-02-28 15:13:31 -080091 return getCellularSettingIntent();
Jason Monk76c67aa2016-02-19 14:49:42 -050092 }
93
94 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -040095 protected void handleClick() {
Evan Lairdb3f974f2017-09-07 19:02:23 -040096 if (getState().state == Tile.STATE_UNAVAILABLE) {
97 return;
98 }
Jason Monk54ff0ef2017-07-25 14:44:28 -040099 if (mDataController.isMobileDataEnabled()) {
Fabian Kozynski88a1fe52019-03-25 14:58:55 -0400100 maybeShowDisableDialog();
Jason Monk54ff0ef2017-07-25 14:44:28 -0400101 } else {
102 mDataController.setMobileDataEnabled(true);
103 }
104 }
105
Amin Shaikh26142a62018-04-11 15:20:32 -0400106 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);
110 return;
111 }
Amin Shaikh329c8282018-03-09 14:50:59 -0500112 String carrierName = mController.getMobileDataNetworkName();
113 if (TextUtils.isEmpty(carrierName)) {
114 carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
115 }
Jason Monk9ca18482017-08-01 10:38:02 -0400116 AlertDialog dialog = new Builder(mContext)
Amin Shaikh329c8282018-03-09 14:50:59 -0500117 .setTitle(R.string.mobile_data_disable_title)
118 .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
Jason Monk54ff0ef2017-07-25 14:44:28 -0400119 .setNegativeButton(android.R.string.cancel, null)
120 .setPositiveButton(
121 com.android.internal.R.string.alert_windows_notification_turn_off_action,
Amin Shaikh329c8282018-03-09 14:50:59 -0500122 (d, w) -> {
123 mDataController.setMobileDataEnabled(false);
124 Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
125 })
Jason Monk9ca18482017-08-01 10:38:02 -0400126 .create();
127 dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
128 SystemUIDialog.setShowForAllUsers(dialog, true);
129 SystemUIDialog.registerDismissListener(dialog);
130 SystemUIDialog.setWindowOnTop(dialog);
131 dialog.show();
Jason Monk32508852017-01-18 09:17:13 -0500132 }
133
134 @Override
135 protected void handleSecondaryClick() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500136 if (mDataController.isMobileDataSupported()) {
John Spurlockb98f7472014-07-08 17:09:42 -0400137 showDetail(true);
138 } else {
Rajeev Kumara8981f32017-07-19 18:12:47 -0700139 mActivityStarter
Rohan Shah53f3f152018-02-28 15:13:31 -0800140 .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
John Spurlockb98f7472014-07-08 17:09:42 -0400141 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142 }
143
144 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400145 public CharSequence getTileLabel() {
146 return mContext.getString(R.string.quick_settings_cellular_detail_title);
147 }
148
149 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400150 protected void handleUpdateState(SignalState state, Object arg) {
Jason Monk878c0932015-07-21 10:18:01 -0400151 CallbackInfo cb = (CallbackInfo) arg;
152 if (cb == null) {
153 cb = mSignalCallback.mInfo;
154 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400155
156 final Resources r = mContext.getResources();
Jason Monke5b770e2017-03-03 21:49:29 -0500157 state.label = r.getString(R.string.mobile_data);
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500158 boolean mobileDataEnabled = mDataController.isMobileDataSupported()
159 && mDataController.isMobileDataEnabled();
160 state.value = mobileDataEnabled;
Amin Shaikh0a8155e02018-09-06 17:23:32 -0400161 state.activityIn = mobileDataEnabled && cb.activityIn;
162 state.activityOut = mobileDataEnabled && cb.activityOut;
Jason Monke5b770e2017-03-03 21:49:29 -0500163 state.expandedAccessibilityClassName = Switch.class.getName();
Evan Laird4a7db952017-09-06 16:43:24 -0400164 if (cb.noSim) {
165 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
166 } else {
Amin Shaikh0e003312018-03-08 11:39:01 -0500167 state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
Evan Laird4a7db952017-09-06 16:43:24 -0400168 }
169
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500170 if (cb.noSim) {
Evan Lairdb3f974f2017-09-07 19:02:23 -0400171 state.state = Tile.STATE_UNAVAILABLE;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500172 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) {
Evan Laird3d1aad62017-06-20 16:10:44 -0400177 state.state = Tile.STATE_ACTIVE;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400178 state.secondaryLabel = appendMobileDataType(
179 // Only show carrier name if there are more than 1 subscription
180 cb.multipleSubs ? cb.dataSubscriptionName : "",
181 getMobileDataContentName(cb));
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500182 } else {
183 state.state = Tile.STATE_INACTIVE;
184 state.secondaryLabel = r.getString(R.string.cell_data_off);
Evan Laird3d1aad62017-06-20 16:10:44 -0400185 }
Rohan Shah7bc37442018-04-10 00:34:50 -0400186
187
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);
193 } else {
194 contentDescriptionSuffix = state.secondaryLabel;
195 }
196
197 state.contentDescription = state.label + ", " + contentDescriptionSuffix;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400198 }
199
Fabian Kozynskic651e642019-02-21 13:20:34 -0500200 private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
201 if (TextUtils.isEmpty(dataType)) {
202 return current;
203 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400204 if (TextUtils.isEmpty(current)) {
205 return dataType;
206 }
207 return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
Fabian Kozynskic651e642019-02-21 13:20:34 -0500208 }
209
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400210 private CharSequence getMobileDataContentName(CallbackInfo cb) {
211 if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500212 String roaming = mContext.getString(R.string.data_connection_roaming);
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400213 String dataDescription = cb.dataContentDescription.toString();
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500214 return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
215 }
216 if (cb.roaming) {
217 return mContext.getString(R.string.data_connection_roaming);
218 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400219 return cb.dataContentDescription;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500220 }
221
Chris Wren457a21c2015-05-06 17:50:34 -0400222 @Override
223 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500224 return MetricsEvent.QS_CELLULAR;
Chris Wren457a21c2015-05-06 17:50:34 -0400225 }
226
Jason Monkc3f42c12016-02-05 12:33:13 -0500227 @Override
228 public boolean isAvailable() {
229 return mController.hasMobileDataFeature();
230 }
231
John Spurlockaf8d6c42014-05-07 17:49:08 -0400232 private static final class CallbackInfo {
John Spurlockd5cd2fd2014-07-27 11:36:33 -0400233 boolean airplaneModeEnabled;
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500234 CharSequence dataSubscriptionName;
Fabian Kozynskic651e642019-02-21 13:20:34 -0500235 CharSequence dataContentDescription;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400236 boolean activityIn;
237 boolean activityOut;
John Spurlock899f4392014-06-11 10:59:11 -0400238 boolean noSim;
Jason Monk0f0de132016-12-19 15:36:13 -0500239 boolean roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400240 boolean multipleSubs;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400241 }
242
Jason Monk0f0de132016-12-19 15:36:13 -0500243 private final class CellSignalCallback implements SignalCallback {
Jason Monkb5746272014-11-12 16:50:31 -0500244 private final CallbackInfo mInfo = new CallbackInfo();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400245
246 @Override
Jason Monk7e6c83c2017-04-26 14:35:24 -0400247 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) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400251 // Not data sim, don't display.
252 return;
253 }
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500254 mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
Fabian Kozynskic651e642019-02-21 13:20:34 -0500255 mInfo.dataContentDescription = (description != null) ? typeContentDescription : null;
Jason Monkb5746272014-11-12 16:50:31 -0500256 mInfo.activityIn = activityIn;
257 mInfo.activityOut = activityOut;
Jason Monk0f0de132016-12-19 15:36:13 -0500258 mInfo.roaming = roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400259 mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
Jason Monkb5746272014-11-12 16:50:31 -0500260 refreshState(mInfo);
261 }
262
263 @Override
Jason Monk1ff77662017-09-29 11:17:05 -0400264 public void setNoSims(boolean show, boolean simDetected) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400265 mInfo.noSim = show;
Jason Monkb5746272014-11-12 16:50:31 -0500266 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400267 }
268
269 @Override
Jason Monk07b75fe2015-05-14 16:47:03 -0400270 public void setIsAirplaneMode(IconState icon) {
271 mInfo.airplaneModeEnabled = icon.visible;
Jason Monkb5746272014-11-12 16:50:31 -0500272 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400273 }
John Spurlockb98f7472014-07-08 17:09:42 -0400274
Jason Monk07b75fe2015-05-14 16:47:03 -0400275 @Override
276 public void setMobileDataEnabled(boolean enabled) {
John Spurlockb98f7472014-07-08 17:09:42 -0400277 mDetailAdapter.setMobileDataEnabled(enabled);
278 }
Rajeev Kumara8981f32017-07-19 18:12:47 -0700279 }
280
Rohan Shah53f3f152018-02-28 15:13:31 -0800281 static Intent getCellularSettingIntent() {
Fabian Kozynski2594b932019-04-11 12:22:39 -0400282 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());
287 }
288 return intent;
Rajeev Kumara8981f32017-07-19 18:12:47 -0700289 }
John Spurlockb98f7472014-07-08 17:09:42 -0400290
291 private final class CellularDetailAdapter implements DetailAdapter {
John Spurlockb98f7472014-07-08 17:09:42 -0400292
293 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500294 public CharSequence getTitle() {
295 return mContext.getString(R.string.quick_settings_cellular_detail_title);
John Spurlockb98f7472014-07-08 17:09:42 -0400296 }
297
298 @Override
299 public Boolean getToggleState() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500300 return mDataController.isMobileDataSupported()
Jason Monkb5746272014-11-12 16:50:31 -0500301 ? mDataController.isMobileDataEnabled()
302 : null;
John Spurlockb98f7472014-07-08 17:09:42 -0400303 }
304
305 @Override
306 public Intent getSettingsIntent() {
Rohan Shah53f3f152018-02-28 15:13:31 -0800307 return getCellularSettingIntent();
John Spurlockb98f7472014-07-08 17:09:42 -0400308 }
309
310 @Override
311 public void setToggleState(boolean state) {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500312 MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
Jason Monkd2263cd2014-11-10 14:22:56 -0500313 mDataController.setMobileDataEnabled(state);
John Spurlockb98f7472014-07-08 17:09:42 -0400314 }
315
316 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400317 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500318 return MetricsEvent.QS_DATAUSAGEDETAIL;
Chris Wren457a21c2015-05-06 17:50:34 -0400319 }
320
321 @Override
John Spurlockb98f7472014-07-08 17:09:42 -0400322 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200323 final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
324 ? convertView
325 : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
Jason Monkf668d7c2016-01-14 10:38:41 -0500326 final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
John Spurlockb98f7472014-07-08 17:09:42 -0400327 if (info == null) return v;
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200328 v.bind(info);
Jason Monk0f0de132016-12-19 15:36:13 -0500329 v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
330 ? View.VISIBLE : View.INVISIBLE);
John Spurlockb98f7472014-07-08 17:09:42 -0400331 return v;
332 }
333
334 public void setMobileDataEnabled(boolean enabled) {
335 fireToggleStateChanged(enabled);
336 }
John Spurlockb98f7472014-07-08 17:09:42 -0400337 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400338}