OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / qs / tiles / CellularTile.java
1 /*
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
17 package com.android.systemui.qs.tiles;
18
19 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
20
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;
35
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;
50
51 import javax.inject.Inject;
52
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";
56
57     private final NetworkController mController;
58     private final DataUsageController mDataController;
59     private final CellularDetailAdapter mDetailAdapter;
60
61     private final CellSignalCallback mSignalCallback = new CellSignalCallback();
62     private final ActivityStarter mActivityStarter;
63
64     @Inject
65     public CellularTile(QSHost host, NetworkController networkController,
66             ActivityStarter activityStarter) {
67         super(host);
68         mController = networkController;
69         mActivityStarter = activityStarter;
70         mDataController = mController.getMobileDataController();
71         mDetailAdapter = new CellularDetailAdapter();
72         mController.observe(getLifecycle(), mSignalCallback);
73     }
74
75     @Override
76     public SignalState newTileState() {
77         return new SignalState();
78     }
79
80     @Override
81     public DetailAdapter getDetailAdapter() {
82         return mDetailAdapter;
83     }
84
85     @Override
86     public void handleSetListening(boolean listening) {
87     }
88
89     @Override
90     public Intent getLongClickIntent() {
91         return getCellularSettingIntent();
92     }
93
94     @Override
95     protected void handleClick() {
96         if (getState().state == Tile.STATE_UNAVAILABLE) {
97             return;
98         }
99         if (mDataController.isMobileDataEnabled()) {
100             maybeShowDisableDialog();
101         } else {
102             mDataController.setMobileDataEnabled(true);
103         }
104     }
105
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);
110             return;
111         }
112         String carrierName = mController.getMobileDataNetworkName();
113         if (TextUtils.isEmpty(carrierName)) {
114             carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
115         }
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)
120                 .setPositiveButton(
121                         com.android.internal.R.string.alert_windows_notification_turn_off_action,
122                         (d, w) -> {
123                             mDataController.setMobileDataEnabled(false);
124                             Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
125                         })
126                 .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();
132     }
133
134     @Override
135     protected void handleSecondaryClick() {
136         if (mDataController.isMobileDataSupported()) {
137             showDetail(true);
138         } else {
139             mActivityStarter
140                     .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
141         }
142     }
143
144     @Override
145     public CharSequence getTileLabel() {
146         return mContext.getString(R.string.quick_settings_cellular_detail_title);
147     }
148
149     @Override
150     protected void handleUpdateState(SignalState state, Object arg) {
151         CallbackInfo cb = (CallbackInfo) arg;
152         if (cb == null) {
153             cb = mSignalCallback.mInfo;
154         }
155
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();
164         if (cb.noSim) {
165             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
166         } else {
167             state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
168         }
169
170         if (cb.noSim) {
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));
182         } else {
183             state.state = Tile.STATE_INACTIVE;
184             state.secondaryLabel = r.getString(R.string.cell_data_off);
185         }
186
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;
198     }
199
200     private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
201         if (TextUtils.isEmpty(dataType)) {
202             return current;
203         }
204         if (TextUtils.isEmpty(current)) {
205             return dataType;
206         }
207         return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
208     }
209
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);
215         }
216         if (cb.roaming) {
217             return mContext.getString(R.string.data_connection_roaming);
218         }
219         return cb.dataContentDescription;
220     }
221
222     @Override
223     public int getMetricsCategory() {
224         return MetricsEvent.QS_CELLULAR;
225     }
226
227     @Override
228     public boolean isAvailable() {
229         return mController.hasMobileDataFeature();
230     }
231
232     private static final class CallbackInfo {
233         boolean airplaneModeEnabled;
234         CharSequence dataSubscriptionName;
235         CharSequence dataContentDescription;
236         boolean activityIn;
237         boolean activityOut;
238         boolean noSim;
239         boolean roaming;
240         boolean multipleSubs;
241     }
242
243     private final class CellSignalCallback implements SignalCallback {
244         private final CallbackInfo mInfo = new CallbackInfo();
245
246         @Override
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.
252                 return;
253             }
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;
260             refreshState(mInfo);
261         }
262
263         @Override
264         public void setNoSims(boolean show, boolean simDetected) {
265             mInfo.noSim = show;
266             refreshState(mInfo);
267         }
268
269         @Override
270         public void setIsAirplaneMode(IconState icon) {
271             mInfo.airplaneModeEnabled = icon.visible;
272             refreshState(mInfo);
273         }
274
275         @Override
276         public void setMobileDataEnabled(boolean enabled) {
277             mDetailAdapter.setMobileDataEnabled(enabled);
278         }
279     }
280
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());
287         }
288         return intent;
289     }
290
291     private final class CellularDetailAdapter implements DetailAdapter {
292
293         @Override
294         public CharSequence getTitle() {
295             return mContext.getString(R.string.quick_settings_cellular_detail_title);
296         }
297
298         @Override
299         public Boolean getToggleState() {
300             return mDataController.isMobileDataSupported()
301                     ? mDataController.isMobileDataEnabled()
302                     : null;
303         }
304
305         @Override
306         public Intent getSettingsIntent() {
307             return getCellularSettingIntent();
308         }
309
310         @Override
311         public void setToggleState(boolean state) {
312             MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
313             mDataController.setMobileDataEnabled(state);
314         }
315
316         @Override
317         public int getMetricsCategory() {
318             return MetricsEvent.QS_DATAUSAGEDETAIL;
319         }
320
321         @Override
322         public View createDetailView(Context context, View convertView, ViewGroup parent) {
323             final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
324                     ? convertView
325                     : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
326             final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
327             if (info == null) return v;
328             v.bind(info);
329             v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
330                     ? View.VISIBLE : View.INVISIBLE);
331             return v;
332         }
333
334         public void setMobileDataEnabled(boolean enabled) {
335             fireToggleStateChanged(enabled);
336         }
337     }
338 }