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