CellularTile.java revision 54de77e1c6c99725f50f58cd832fb1cacd867383
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
17package com.android.systemui.qs.tiles;
18
19import android.app.AlertDialog;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.content.res.Resources;
25import android.os.SystemProperties;
26import android.service.quicksettings.Tile;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.Switch;
31import com.android.internal.logging.MetricsLogger;
32import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
33import com.android.settingslib.net.DataUsageController;
34import com.android.systemui.Dependency;
35import com.android.systemui.R;
36import com.android.systemui.plugins.ActivityStarter;
37import com.android.systemui.plugins.qs.DetailAdapter;
38import com.android.systemui.plugins.qs.QSIconView;
39import com.android.systemui.plugins.qs.QSTile.SignalState;
40import com.android.systemui.qs.CellTileView;
41import com.android.systemui.qs.CellTileView.SignalIcon;
42import com.android.systemui.qs.QSHost;
43import com.android.systemui.qs.tileimpl.QSTileImpl;
44import com.android.systemui.statusbar.phone.SystemUIDialog;
45import com.android.systemui.statusbar.policy.KeyguardMonitor;
46import com.android.systemui.statusbar.policy.NetworkController;
47import com.android.systemui.statusbar.policy.NetworkController.IconState;
48import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
49
50/** Quick settings tile: Cellular **/
51public class CellularTile extends QSTileImpl<SignalState> {
52    private static final ComponentName CELLULAR_SETTING_COMPONENT = new ComponentName(
53            "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity");
54    private static final ComponentName DATA_PLAN_CELLULAR_COMPONENT = new ComponentName(
55            "com.android.settings", "com.android.settings.Settings$DataPlanUsageSummaryActivity");
56
57    private static final Intent CELLULAR_SETTINGS =
58            new Intent().setComponent(CELLULAR_SETTING_COMPONENT);
59    private static final Intent DATA_PLAN_CELLULAR_SETTINGS =
60            new Intent().setComponent(DATA_PLAN_CELLULAR_COMPONENT);
61
62    private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
63
64    private final NetworkController mController;
65    private final DataUsageController mDataController;
66    private final CellularDetailAdapter mDetailAdapter;
67
68    private final CellSignalCallback mSignalCallback = new CellSignalCallback();
69    private final ActivityStarter mActivityStarter;
70    private final KeyguardMonitor mKeyguardMonitor;
71
72    public CellularTile(QSHost host) {
73        super(host);
74        mController = Dependency.get(NetworkController.class);
75        mActivityStarter = Dependency.get(ActivityStarter.class);
76        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
77        mDataController = mController.getMobileDataController();
78        mDetailAdapter = new CellularDetailAdapter();
79    }
80
81    @Override
82    public SignalState newTileState() {
83        return new SignalState();
84    }
85
86    @Override
87    public DetailAdapter getDetailAdapter() {
88        return mDetailAdapter;
89    }
90
91    @Override
92    public void setListening(boolean listening) {
93        if (listening) {
94            mController.addCallback(mSignalCallback);
95        } else {
96            mController.removeCallback(mSignalCallback);
97        }
98    }
99
100    @Override
101    public QSIconView createTileView(Context context) {
102        return new CellTileView(context);
103    }
104
105    @Override
106    public Intent getLongClickIntent() {
107        return getCellularSettingIntent(mContext);
108    }
109
110    @Override
111    protected void handleClick() {
112        if (mDataController.isMobileDataEnabled()) {
113            if (mKeyguardMonitor.isSecure() && !mKeyguardMonitor.canSkipBouncer()) {
114                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
115                    showDisableDialog();
116                });
117            } else {
118                showDisableDialog();
119            }
120        } else {
121            mDataController.setMobileDataEnabled(true);
122        }
123    }
124
125    private void showDisableDialog() {
126        mHost.collapsePanels();
127        SystemUIDialog.applyFlags(new AlertDialog.Builder(mContext)
128                .setMessage(R.string.data_usage_disable_mobile)
129                .setNegativeButton(android.R.string.cancel, null)
130                .setPositiveButton(
131                        com.android.internal.R.string.alert_windows_notification_turn_off_action,
132                        (d, w) -> mDataController.setMobileDataEnabled(false))
133                .create()).show();
134    }
135
136    @Override
137    protected void handleSecondaryClick() {
138        if (mDataController.isMobileDataSupported()) {
139            showDetail(true);
140        } else {
141            mActivityStarter
142                    .postStartActivityDismissingKeyguard(getCellularSettingIntent(mContext),
143                            0 /* delay */);
144        }
145    }
146
147    @Override
148    public CharSequence getTileLabel() {
149        return mContext.getString(R.string.quick_settings_cellular_detail_title);
150    }
151
152    @Override
153    protected void handleUpdateState(SignalState state, Object arg) {
154        CallbackInfo cb = (CallbackInfo) arg;
155        if (cb == null) {
156            cb = mSignalCallback.mInfo;
157        }
158
159        final Resources r = mContext.getResources();
160        state.activityIn = cb.enabled && cb.activityIn;
161        state.activityOut = cb.enabled && cb.activityOut;
162        state.isOverlayIconWide = cb.isDataTypeIconWide;
163        state.overlayIconId = cb.dataTypeIconId;
164
165        state.label = r.getString(R.string.mobile_data);
166
167        final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0)
168                ? cb.signalContentDescription
169                : r.getString(R.string.accessibility_no_signal);
170        if (cb.noSim) {
171            state.contentDescription = state.label;
172        } else {
173            state.contentDescription = signalContentDesc + ", " + state.label;
174        }
175
176        state.expandedAccessibilityClassName = Switch.class.getName();
177        state.value = mDataController.isMobileDataSupported()
178                && mDataController.isMobileDataEnabled();
179
180        state.icon = new SignalIcon(cb.mobileSignalIconId);
181        if (cb.airplaneModeEnabled) {
182            state.state = Tile.STATE_INACTIVE;
183        } else {
184            state.state = Tile.STATE_ACTIVE;
185        }
186    }
187
188    @Override
189    public int getMetricsCategory() {
190        return MetricsEvent.QS_CELLULAR;
191    }
192
193    @Override
194    public boolean isAvailable() {
195        return mController.hasMobileDataFeature();
196    }
197
198    // Remove the period from the network name
199    public static String removeTrailingPeriod(String string) {
200        if (string == null) return null;
201        final int length = string.length();
202        if (string.endsWith(".")) {
203            return string.substring(0, length - 1);
204        }
205        return string;
206    }
207
208    private static final class CallbackInfo {
209        boolean enabled;
210        boolean wifiEnabled;
211        boolean airplaneModeEnabled;
212        int mobileSignalIconId;
213        String signalContentDescription;
214        int dataTypeIconId;
215        String dataContentDescription;
216        boolean activityIn;
217        boolean activityOut;
218        String enabledDesc;
219        boolean noSim;
220        boolean isDataTypeIconWide;
221        boolean roaming;
222    }
223
224    private final class CellSignalCallback implements SignalCallback {
225        private final CallbackInfo mInfo = new CallbackInfo();
226        @Override
227        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
228                boolean activityIn, boolean activityOut, String description, boolean isTransient) {
229            mInfo.wifiEnabled = enabled;
230            refreshState(mInfo);
231        }
232
233        @Override
234        public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
235                int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
236                String description, boolean isWide, int subId, boolean roaming) {
237            if (qsIcon == null) {
238                // Not data sim, don't display.
239                return;
240            }
241            mInfo.enabled = qsIcon.visible;
242            mInfo.mobileSignalIconId = qsIcon.icon;
243            mInfo.signalContentDescription = qsIcon.contentDescription;
244            mInfo.dataTypeIconId = qsType;
245            mInfo.dataContentDescription = typeContentDescription;
246            mInfo.activityIn = activityIn;
247            mInfo.activityOut = activityOut;
248            mInfo.enabledDesc = description;
249            mInfo.isDataTypeIconWide = qsType != 0 && isWide;
250            mInfo.roaming = roaming;
251            refreshState(mInfo);
252        }
253
254        @Override
255        public void setNoSims(boolean show) {
256            mInfo.noSim = show;
257            if (mInfo.noSim) {
258                // Make sure signal gets cleared out when no sims.
259                mInfo.mobileSignalIconId = 0;
260                mInfo.dataTypeIconId = 0;
261                // Show a No SIMs description to avoid emergency calls message.
262                mInfo.enabled = true;
263                mInfo.enabledDesc = mContext.getString(
264                        R.string.keyguard_missing_sim_message_short);
265                mInfo.signalContentDescription = mInfo.enabledDesc;
266            }
267            refreshState(mInfo);
268        }
269
270        @Override
271        public void setIsAirplaneMode(IconState icon) {
272            mInfo.airplaneModeEnabled = icon.visible;
273            refreshState(mInfo);
274        }
275
276        @Override
277        public void setMobileDataEnabled(boolean enabled) {
278            mDetailAdapter.setMobileDataEnabled(enabled);
279        }
280    }
281
282    static Intent getCellularSettingIntent(Context context) {
283        // TODO(b/62349208): We should replace feature flag check below with data plans
284        // availability check. If the data plans are available we display the data plans usage
285        // summary otherwise we display data usage summary without data plans.
286        boolean isDataPlanFeatureEnabled =
287                SystemProperties.getBoolean(ENABLE_SETTINGS_DATA_PLAN, false /* default */);
288        context.getPackageManager()
289                .setComponentEnabledSetting(
290                        DATA_PLAN_CELLULAR_COMPONENT,
291                        isDataPlanFeatureEnabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
292                                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
293                        PackageManager.DONT_KILL_APP);
294        context.getPackageManager()
295                .setComponentEnabledSetting(
296                        CELLULAR_SETTING_COMPONENT,
297                        isDataPlanFeatureEnabled ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
298                                : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
299                        PackageManager.DONT_KILL_APP);
300        return isDataPlanFeatureEnabled ? DATA_PLAN_CELLULAR_SETTINGS : CELLULAR_SETTINGS;
301    }
302
303    private final class CellularDetailAdapter implements DetailAdapter {
304
305        @Override
306        public CharSequence getTitle() {
307            return mContext.getString(R.string.quick_settings_cellular_detail_title);
308        }
309
310        @Override
311        public Boolean getToggleState() {
312            return mDataController.isMobileDataSupported()
313                    ? mDataController.isMobileDataEnabled()
314                    : null;
315        }
316
317        @Override
318        public Intent getSettingsIntent() {
319            return getCellularSettingIntent(mContext);
320        }
321
322        @Override
323        public void setToggleState(boolean state) {
324            MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
325            mDataController.setMobileDataEnabled(state);
326        }
327
328        @Override
329        public int getMetricsCategory() {
330            return MetricsEvent.QS_DATAUSAGEDETAIL;
331        }
332
333        @Override
334        public View createDetailView(Context context, View convertView, ViewGroup parent) {
335            final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
336                    ? convertView
337                    : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
338            final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
339            if (info == null) return v;
340            v.bind(info);
341            v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
342                    ? View.VISIBLE : View.INVISIBLE);
343            return v;
344        }
345
346        public void setMobileDataEnabled(boolean enabled) {
347            fireToggleStateChanged(enabled);
348        }
349    }
350}
351