CellularTile.java revision 53f3f15654a12a820e08cd5d42b5f94d061598ab
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.app.AlertDialog.Builder;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.res.Resources;
26import android.os.SystemProperties;
27import android.provider.Settings;
28import android.service.quicksettings.Tile;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.WindowManager.LayoutParams;
33import android.widget.Switch;
34import com.android.internal.logging.MetricsLogger;
35import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
36import com.android.settingslib.net.DataUsageController;
37import com.android.systemui.Dependency;
38import com.android.systemui.R;
39import com.android.systemui.R.string;
40import com.android.systemui.plugins.ActivityStarter;
41import com.android.systemui.plugins.qs.DetailAdapter;
42import com.android.systemui.plugins.qs.QSIconView;
43import com.android.systemui.plugins.qs.QSTile.SignalState;
44import com.android.systemui.qs.CellTileView;
45import com.android.systemui.qs.CellTileView.SignalIcon;
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                mUiHandler.post(this::showDisableDialog);
114            }
115        } else {
116            mDataController.setMobileDataEnabled(true);
117        }
118    }
119
120    private void showDisableDialog() {
121        mHost.collapsePanels();
122        AlertDialog dialog = new Builder(mContext)
123                .setMessage(string.data_usage_disable_mobile)
124                .setNegativeButton(android.R.string.cancel, null)
125                .setPositiveButton(
126                        com.android.internal.R.string.alert_windows_notification_turn_off_action,
127                        (d, w) -> mDataController.setMobileDataEnabled(false))
128                .create();
129        dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
130        SystemUIDialog.setShowForAllUsers(dialog, true);
131        SystemUIDialog.registerDismissListener(dialog);
132        SystemUIDialog.setWindowOnTop(dialog);
133        dialog.show();
134    }
135
136    @Override
137    protected void handleSecondaryClick() {
138        if (mDataController.isMobileDataSupported()) {
139            showDetail(true);
140        } else {
141            mActivityStarter
142                    .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
143        }
144    }
145
146    @Override
147    public CharSequence getTileLabel() {
148        return mContext.getString(R.string.quick_settings_cellular_detail_title);
149    }
150
151    @Override
152    protected void handleUpdateState(SignalState state, Object arg) {
153        CallbackInfo cb = (CallbackInfo) arg;
154        if (cb == null) {
155            cb = mSignalCallback.mInfo;
156        }
157
158        final Resources r = mContext.getResources();
159        state.activityIn = cb.enabled && cb.activityIn;
160        state.activityOut = cb.enabled && cb.activityOut;
161        state.isOverlayIconWide = cb.isDataTypeIconWide;
162        state.overlayIconId = cb.dataTypeIconId;
163
164        state.label = r.getString(R.string.mobile_data);
165
166        final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0)
167                ? cb.signalContentDescription
168                : r.getString(R.string.accessibility_no_signal);
169        if (cb.noSim) {
170            state.contentDescription = state.label;
171        } else {
172            state.contentDescription = signalContentDesc + ", " + state.label;
173        }
174
175        state.expandedAccessibilityClassName = Switch.class.getName();
176        state.value = mDataController.isMobileDataSupported()
177                && mDataController.isMobileDataEnabled();
178
179        if (cb.noSim) {
180            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
181        } else {
182            state.icon = new SignalIcon(cb.mobileSignalIconId);
183        }
184
185        if (cb.airplaneModeEnabled | cb.noSim) {
186            state.state = Tile.STATE_UNAVAILABLE;
187        } else {
188            state.state = Tile.STATE_ACTIVE;
189        }
190    }
191
192    @Override
193    public int getMetricsCategory() {
194        return MetricsEvent.QS_CELLULAR;
195    }
196
197    @Override
198    public boolean isAvailable() {
199        return mController.hasMobileDataFeature();
200    }
201
202    // Remove the period from the network name
203    public static String removeTrailingPeriod(String string) {
204        if (string == null) return null;
205        final int length = string.length();
206        if (string.endsWith(".")) {
207            return string.substring(0, length - 1);
208        }
209        return string;
210    }
211
212    private static final class CallbackInfo {
213        boolean enabled;
214        boolean wifiEnabled;
215        boolean airplaneModeEnabled;
216        int mobileSignalIconId;
217        String signalContentDescription;
218        int dataTypeIconId;
219        String dataContentDescription;
220        boolean activityIn;
221        boolean activityOut;
222        String enabledDesc;
223        boolean noSim;
224        boolean isDataTypeIconWide;
225        boolean roaming;
226    }
227
228    private final class CellSignalCallback implements SignalCallback {
229        private final CallbackInfo mInfo = new CallbackInfo();
230        @Override
231        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
232                boolean activityIn, boolean activityOut, String description, boolean isTransient) {
233            mInfo.wifiEnabled = enabled;
234            refreshState(mInfo);
235        }
236
237        @Override
238        public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
239                int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
240                String description, boolean isWide, int subId, boolean roaming) {
241            if (qsIcon == null) {
242                // Not data sim, don't display.
243                return;
244            }
245            mInfo.enabled = qsIcon.visible;
246            mInfo.mobileSignalIconId = qsIcon.icon;
247            mInfo.signalContentDescription = qsIcon.contentDescription;
248            mInfo.dataTypeIconId = qsType;
249            mInfo.dataContentDescription = typeContentDescription;
250            mInfo.activityIn = activityIn;
251            mInfo.activityOut = activityOut;
252            mInfo.enabledDesc = description;
253            mInfo.isDataTypeIconWide = qsType != 0 && isWide;
254            mInfo.roaming = roaming;
255            refreshState(mInfo);
256        }
257
258        @Override
259        public void setNoSims(boolean show, boolean simDetected) {
260            mInfo.noSim = show;
261            if (mInfo.noSim) {
262                // Make sure signal gets cleared out when no sims.
263                mInfo.mobileSignalIconId = 0;
264                mInfo.dataTypeIconId = 0;
265                // Show a No SIMs description to avoid emergency calls message.
266                mInfo.enabled = true;
267                mInfo.enabledDesc = mContext.getString(
268                        R.string.keyguard_missing_sim_message_short);
269                mInfo.signalContentDescription = mInfo.enabledDesc;
270            }
271            refreshState(mInfo);
272        }
273
274        @Override
275        public void setIsAirplaneMode(IconState icon) {
276            mInfo.airplaneModeEnabled = icon.visible;
277            refreshState(mInfo);
278        }
279
280        @Override
281        public void setMobileDataEnabled(boolean enabled) {
282            mDetailAdapter.setMobileDataEnabled(enabled);
283        }
284    }
285
286    static Intent getCellularSettingIntent() {
287        return new Intent(Settings.ACTION_DATA_USAGE_SETTINGS);
288    }
289
290    private final class CellularDetailAdapter implements DetailAdapter {
291
292        @Override
293        public CharSequence getTitle() {
294            return mContext.getString(R.string.quick_settings_cellular_detail_title);
295        }
296
297        @Override
298        public Boolean getToggleState() {
299            return mDataController.isMobileDataSupported()
300                    ? mDataController.isMobileDataEnabled()
301                    : null;
302        }
303
304        @Override
305        public Intent getSettingsIntent() {
306            return getCellularSettingIntent();
307        }
308
309        @Override
310        public void setToggleState(boolean state) {
311            MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
312            mDataController.setMobileDataEnabled(state);
313        }
314
315        @Override
316        public int getMetricsCategory() {
317            return MetricsEvent.QS_DATAUSAGEDETAIL;
318        }
319
320        @Override
321        public View createDetailView(Context context, View convertView, ViewGroup parent) {
322            final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
323                    ? convertView
324                    : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
325            final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
326            if (info == null) return v;
327            v.bind(info);
328            v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
329                    ? View.VISIBLE : View.INVISIBLE);
330            return v;
331        }
332
333        public void setMobileDataEnabled(boolean enabled) {
334            fireToggleStateChanged(enabled);
335        }
336    }
337}
338