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