blob: 38962eb1470588fff8fadc432b1dce07b3b14e8a [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;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -050028import android.text.TextUtils;
John Spurlockb98f7472014-07-08 17:09:42 -040029import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
Jason Monk9ca18482017-08-01 10:38:02 -040032import android.view.WindowManager.LayoutParams;
Jason Monke5b770e2017-03-03 21:49:29 -050033import android.widget.Switch;
Amin Shaikh0e003312018-03-08 11:39:01 -050034
Chris Wren457a21c2015-05-06 17:50:34 -040035import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010036import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkf668d7c2016-01-14 10:38:41 -050037import com.android.settingslib.net.DataUsageController;
Amin Shaikh329c8282018-03-09 14:50:59 -050038import com.android.systemui.Prefs;
John Spurlockaf8d6c42014-05-07 17:49:08 -040039import com.android.systemui.R;
Jason Monkec34da82017-02-24 15:57:05 -050040import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050041import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monk702e2eb2017-03-03 16:53:44 -050042import com.android.systemui.plugins.qs.QSIconView;
43import com.android.systemui.plugins.qs.QSTile.SignalState;
Jason Monk01df36f2017-06-07 13:02:47 -040044import com.android.systemui.qs.CellTileView;
Jason Monke5b770e2017-03-03 21:49:29 -050045import com.android.systemui.qs.QSHost;
Jason Monk702e2eb2017-03-03 16:53:44 -050046import com.android.systemui.qs.tileimpl.QSTileImpl;
Jason Monk54ff0ef2017-07-25 14:44:28 -040047import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlockaf8d6c42014-05-07 17:49:08 -040048import com.android.systemui.statusbar.policy.NetworkController;
Jason Monk07b75fe2015-05-14 16:47:03 -040049import com.android.systemui.statusbar.policy.NetworkController.IconState;
Jason Monk0f0de132016-12-19 15:36:13 -050050import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
John Spurlockaf8d6c42014-05-07 17:49:08 -040051
Jason Monk5d577202018-12-26 15:43:06 -050052import javax.inject.Inject;
53
John Spurlockaf8d6c42014-05-07 17:49:08 -040054/** Quick settings tile: Cellular **/
Jason Monk702e2eb2017-03-03 16:53:44 -050055public class CellularTile extends QSTileImpl<SignalState> {
Rajeev Kumara8981f32017-07-19 18:12:47 -070056 private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
John Spurlockaf8d6c42014-05-07 17:49:08 -040057
58 private final NetworkController mController;
Jason Monkf668d7c2016-01-14 10:38:41 -050059 private final DataUsageController mDataController;
John Spurlockb98f7472014-07-08 17:09:42 -040060 private final CellularDetailAdapter mDetailAdapter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040061
Jason Monk878c0932015-07-21 10:18:01 -040062 private final CellSignalCallback mSignalCallback = new CellSignalCallback();
Jason Monk9c7844c2017-01-18 15:21:53 -050063 private final ActivityStarter mActivityStarter;
Jason Monk878c0932015-07-21 10:18:01 -040064
Jason Monk5d577202018-12-26 15:43:06 -050065 @Inject
Jason Monkc0e0e2b2018-12-26 16:47:41 -050066 public CellularTile(QSHost host, NetworkController networkController,
Fabian Kozynski88a1fe52019-03-25 14:58:55 -040067 ActivityStarter activityStarter) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040068 super(host);
Jason Monkc0e0e2b2018-12-26 16:47:41 -050069 mController = networkController;
70 mActivityStarter = activityStarter;
Jason Monkd2263cd2014-11-10 14:22:56 -050071 mDataController = mController.getMobileDataController();
John Spurlockb98f7472014-07-08 17:09:42 -040072 mDetailAdapter = new CellularDetailAdapter();
Jason Monkfa452ef2018-12-26 17:26:10 -050073 mController.observe(getLifecycle(), mSignalCallback);
John Spurlockaf8d6c42014-05-07 17:49:08 -040074 }
75
76 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050077 public SignalState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040078 return new SignalState();
79 }
80
81 @Override
John Spurlockb98f7472014-07-08 17:09:42 -040082 public DetailAdapter getDetailAdapter() {
83 return mDetailAdapter;
84 }
85
86 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -040087 public void handleSetListening(boolean listening) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040088 }
89
90 @Override
Jason Monkdc35dcb2015-12-04 16:36:15 -050091 public QSIconView createTileView(Context context) {
Jason Monk01df36f2017-06-07 13:02:47 -040092 return new CellTileView(context);
John Spurlockaf8d6c42014-05-07 17:49:08 -040093 }
94
95 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -050096 public Intent getLongClickIntent() {
Rohan Shah53f3f152018-02-28 15:13:31 -080097 return getCellularSettingIntent();
Jason Monk76c67aa2016-02-19 14:49:42 -050098 }
99
100 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400101 protected void handleClick() {
Evan Lairdb3f974f2017-09-07 19:02:23 -0400102 if (getState().state == Tile.STATE_UNAVAILABLE) {
103 return;
104 }
Jason Monk54ff0ef2017-07-25 14:44:28 -0400105 if (mDataController.isMobileDataEnabled()) {
Fabian Kozynski88a1fe52019-03-25 14:58:55 -0400106 maybeShowDisableDialog();
Jason Monk54ff0ef2017-07-25 14:44:28 -0400107 } else {
108 mDataController.setMobileDataEnabled(true);
109 }
110 }
111
Amin Shaikh26142a62018-04-11 15:20:32 -0400112 private void maybeShowDisableDialog() {
113 if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
114 // Directly turn off mobile data if the user has seen the dialog before.
115 mDataController.setMobileDataEnabled(false);
116 return;
117 }
Amin Shaikh329c8282018-03-09 14:50:59 -0500118 String carrierName = mController.getMobileDataNetworkName();
119 if (TextUtils.isEmpty(carrierName)) {
120 carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
121 }
Jason Monk9ca18482017-08-01 10:38:02 -0400122 AlertDialog dialog = new Builder(mContext)
Amin Shaikh329c8282018-03-09 14:50:59 -0500123 .setTitle(R.string.mobile_data_disable_title)
124 .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
Jason Monk54ff0ef2017-07-25 14:44:28 -0400125 .setNegativeButton(android.R.string.cancel, null)
126 .setPositiveButton(
127 com.android.internal.R.string.alert_windows_notification_turn_off_action,
Amin Shaikh329c8282018-03-09 14:50:59 -0500128 (d, w) -> {
129 mDataController.setMobileDataEnabled(false);
130 Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
131 })
Jason Monk9ca18482017-08-01 10:38:02 -0400132 .create();
133 dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
134 SystemUIDialog.setShowForAllUsers(dialog, true);
135 SystemUIDialog.registerDismissListener(dialog);
136 SystemUIDialog.setWindowOnTop(dialog);
137 dialog.show();
Jason Monk32508852017-01-18 09:17:13 -0500138 }
139
140 @Override
141 protected void handleSecondaryClick() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500142 if (mDataController.isMobileDataSupported()) {
John Spurlockb98f7472014-07-08 17:09:42 -0400143 showDetail(true);
144 } else {
Rajeev Kumara8981f32017-07-19 18:12:47 -0700145 mActivityStarter
Rohan Shah53f3f152018-02-28 15:13:31 -0800146 .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
John Spurlockb98f7472014-07-08 17:09:42 -0400147 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400148 }
149
150 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400151 public CharSequence getTileLabel() {
152 return mContext.getString(R.string.quick_settings_cellular_detail_title);
153 }
154
155 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400156 protected void handleUpdateState(SignalState state, Object arg) {
Jason Monk878c0932015-07-21 10:18:01 -0400157 CallbackInfo cb = (CallbackInfo) arg;
158 if (cb == null) {
159 cb = mSignalCallback.mInfo;
160 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400161
162 final Resources r = mContext.getResources();
Jason Monke5b770e2017-03-03 21:49:29 -0500163 state.label = r.getString(R.string.mobile_data);
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500164 boolean mobileDataEnabled = mDataController.isMobileDataSupported()
165 && mDataController.isMobileDataEnabled();
166 state.value = mobileDataEnabled;
Amin Shaikh0a8155e02018-09-06 17:23:32 -0400167 state.activityIn = mobileDataEnabled && cb.activityIn;
168 state.activityOut = mobileDataEnabled && cb.activityOut;
Jason Monke5b770e2017-03-03 21:49:29 -0500169 state.expandedAccessibilityClassName = Switch.class.getName();
Evan Laird4a7db952017-09-06 16:43:24 -0400170 if (cb.noSim) {
171 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
172 } else {
Amin Shaikh0e003312018-03-08 11:39:01 -0500173 state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
Evan Laird4a7db952017-09-06 16:43:24 -0400174 }
175
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500176 if (cb.noSim) {
Evan Lairdb3f974f2017-09-07 19:02:23 -0400177 state.state = Tile.STATE_UNAVAILABLE;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500178 state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
179 } else if (cb.airplaneModeEnabled) {
180 state.state = Tile.STATE_UNAVAILABLE;
181 state.secondaryLabel = r.getString(R.string.status_bar_airplane);
182 } else if (mobileDataEnabled) {
Evan Laird3d1aad62017-06-20 16:10:44 -0400183 state.state = Tile.STATE_ACTIVE;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400184 state.secondaryLabel = appendMobileDataType(
185 // Only show carrier name if there are more than 1 subscription
186 cb.multipleSubs ? cb.dataSubscriptionName : "",
187 getMobileDataContentName(cb));
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500188 } else {
189 state.state = Tile.STATE_INACTIVE;
190 state.secondaryLabel = r.getString(R.string.cell_data_off);
Evan Laird3d1aad62017-06-20 16:10:44 -0400191 }
Rohan Shah7bc37442018-04-10 00:34:50 -0400192
193
194 // TODO(b/77881974): Instead of switching out the description via a string check for
195 // we need to have two strings provided by the MobileIconGroup.
196 final CharSequence contentDescriptionSuffix;
197 if (state.state == Tile.STATE_INACTIVE) {
198 contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
199 } else {
200 contentDescriptionSuffix = state.secondaryLabel;
201 }
202
203 state.contentDescription = state.label + ", " + contentDescriptionSuffix;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400204 }
205
Fabian Kozynskic651e642019-02-21 13:20:34 -0500206 private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
207 if (TextUtils.isEmpty(dataType)) {
208 return current;
209 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400210 if (TextUtils.isEmpty(current)) {
211 return dataType;
212 }
213 return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
Fabian Kozynskic651e642019-02-21 13:20:34 -0500214 }
215
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400216 private CharSequence getMobileDataContentName(CallbackInfo cb) {
217 if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500218 String roaming = mContext.getString(R.string.data_connection_roaming);
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400219 String dataDescription = cb.dataContentDescription.toString();
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500220 return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
221 }
222 if (cb.roaming) {
223 return mContext.getString(R.string.data_connection_roaming);
224 }
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400225 return cb.dataContentDescription;
Amin Shaikh43ad7fe2018-03-05 14:34:44 -0500226 }
227
Chris Wren457a21c2015-05-06 17:50:34 -0400228 @Override
229 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500230 return MetricsEvent.QS_CELLULAR;
Chris Wren457a21c2015-05-06 17:50:34 -0400231 }
232
Jason Monkc3f42c12016-02-05 12:33:13 -0500233 @Override
234 public boolean isAvailable() {
235 return mController.hasMobileDataFeature();
236 }
237
John Spurlockaf8d6c42014-05-07 17:49:08 -0400238 private static final class CallbackInfo {
John Spurlockd5cd2fd2014-07-27 11:36:33 -0400239 boolean airplaneModeEnabled;
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500240 CharSequence dataSubscriptionName;
Fabian Kozynskic651e642019-02-21 13:20:34 -0500241 CharSequence dataContentDescription;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400242 boolean activityIn;
243 boolean activityOut;
John Spurlock899f4392014-06-11 10:59:11 -0400244 boolean noSim;
Jason Monk0f0de132016-12-19 15:36:13 -0500245 boolean roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400246 boolean multipleSubs;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400247 }
248
Jason Monk0f0de132016-12-19 15:36:13 -0500249 private final class CellSignalCallback implements SignalCallback {
Jason Monkb5746272014-11-12 16:50:31 -0500250 private final CallbackInfo mInfo = new CallbackInfo();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400251
252 @Override
Jason Monk7e6c83c2017-04-26 14:35:24 -0400253 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
254 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
255 String description, boolean isWide, int subId, boolean roaming) {
256 if (qsIcon == null) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400257 // Not data sim, don't display.
258 return;
259 }
Fabian Kozynski9d3fd6c2019-01-17 10:56:45 -0500260 mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
Fabian Kozynskic651e642019-02-21 13:20:34 -0500261 mInfo.dataContentDescription = (description != null) ? typeContentDescription : null;
Jason Monkb5746272014-11-12 16:50:31 -0500262 mInfo.activityIn = activityIn;
263 mInfo.activityOut = activityOut;
Jason Monk0f0de132016-12-19 15:36:13 -0500264 mInfo.roaming = roaming;
Fabian Kozynskif1c32a02019-03-22 15:09:30 -0400265 mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
Jason Monkb5746272014-11-12 16:50:31 -0500266 refreshState(mInfo);
267 }
268
269 @Override
Jason Monk1ff77662017-09-29 11:17:05 -0400270 public void setNoSims(boolean show, boolean simDetected) {
Jason Monk07b75fe2015-05-14 16:47:03 -0400271 mInfo.noSim = show;
Jason Monkb5746272014-11-12 16:50:31 -0500272 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400273 }
274
275 @Override
Jason Monk07b75fe2015-05-14 16:47:03 -0400276 public void setIsAirplaneMode(IconState icon) {
277 mInfo.airplaneModeEnabled = icon.visible;
Jason Monkb5746272014-11-12 16:50:31 -0500278 refreshState(mInfo);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400279 }
John Spurlockb98f7472014-07-08 17:09:42 -0400280
Jason Monk07b75fe2015-05-14 16:47:03 -0400281 @Override
282 public void setMobileDataEnabled(boolean enabled) {
John Spurlockb98f7472014-07-08 17:09:42 -0400283 mDetailAdapter.setMobileDataEnabled(enabled);
284 }
Rajeev Kumara8981f32017-07-19 18:12:47 -0700285 }
286
Rohan Shah53f3f152018-02-28 15:13:31 -0800287 static Intent getCellularSettingIntent() {
288 return new Intent(Settings.ACTION_DATA_USAGE_SETTINGS);
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}