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