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