CellularTile.java revision 06d3bca095aecbb7542ebf4bdaa56b368261dd9d
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.res.Resources;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.ViewGroup;
26import android.widget.TextView;
27
28import com.android.systemui.R;
29import com.android.systemui.qs.DataUsageGraph;
30import com.android.systemui.qs.QSTile;
31import com.android.systemui.qs.QSTileView;
32import com.android.systemui.qs.SignalTileView;
33import com.android.systemui.statusbar.policy.NetworkController;
34import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
35import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
36
37import java.text.DecimalFormat;
38
39/** Quick settings tile: Cellular **/
40public class CellularTile extends QSTile<QSTile.SignalState> {
41    private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
42            "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
43
44    private final NetworkController mController;
45    private final CellularDetailAdapter mDetailAdapter;
46
47    public CellularTile(Host host) {
48        super(host);
49        mController = host.getNetworkController();
50        mDetailAdapter = new CellularDetailAdapter();
51    }
52
53    @Override
54    protected SignalState newTileState() {
55        return new SignalState();
56    }
57
58    @Override
59    public DetailAdapter getDetailAdapter() {
60        return mDetailAdapter;
61    }
62
63    @Override
64    public void setListening(boolean listening) {
65        if (listening) {
66            mController.addNetworkSignalChangedCallback(mCallback);
67        } else {
68            mController.removeNetworkSignalChangedCallback(mCallback);
69        }
70    }
71
72    @Override
73    public QSTileView createTileView(Context context) {
74        return new SignalTileView(context);
75    }
76
77    @Override
78    protected void handleClick() {
79        if (mController.isMobileDataSupported()) {
80            showDetail(true);
81        } else {
82            mHost.startSettingsActivity(CELLULAR_SETTINGS);
83        }
84    }
85
86    @Override
87    protected void handleUpdateState(SignalState state, Object arg) {
88        state.visible = mController.hasMobileDataFeature();
89        if (!state.visible) return;
90        final CallbackInfo cb = (CallbackInfo) arg;
91        if (cb == null) return;
92
93        final Resources r = mContext.getResources();
94        state.iconId = cb.noSim ? R.drawable.stat_sys_no_sim
95                : !cb.enabled || cb.airplaneModeEnabled ? R.drawable.ic_qs_signal_disabled
96                : cb.mobileSignalIconId > 0 ? cb.mobileSignalIconId
97                : R.drawable.ic_qs_signal_no_signal;
98        state.autoMirrorDrawable = !cb.noSim;
99        state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiConnected
100                ? cb.dataTypeIconId
101                : 0;
102        state.filter = state.iconId != R.drawable.stat_sys_no_sim;
103        state.activityIn = cb.enabled && cb.activityIn;
104        state.activityOut = cb.enabled && cb.activityOut;
105
106        state.label = cb.enabled
107                ? removeTrailingPeriod(cb.enabledDesc)
108                : r.getString(R.string.quick_settings_rssi_emergency_only);
109
110        final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0)
111                ? cb.signalContentDescription
112                : r.getString(R.string.accessibility_no_signal);
113        final String dataContentDesc = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled
114                ? cb.dataContentDescription
115                : r.getString(R.string.accessibility_no_data);
116        state.contentDescription = r.getString(
117                R.string.accessibility_quick_settings_mobile,
118                signalContentDesc, dataContentDesc,
119                state.label);
120    }
121
122    // Remove the period from the network name
123    public static String removeTrailingPeriod(String string) {
124        if (string == null) return null;
125        final int length = string.length();
126        if (string.endsWith(".")) {
127            return string.substring(0, length - 1);
128        }
129        return string;
130    }
131
132    private static final class CallbackInfo {
133        boolean enabled;
134        boolean wifiEnabled;
135        boolean wifiConnected;
136        boolean airplaneModeEnabled;
137        int mobileSignalIconId;
138        String signalContentDescription;
139        int dataTypeIconId;
140        String dataContentDescription;
141        boolean activityIn;
142        boolean activityOut;
143        String enabledDesc;
144        boolean noSim;
145    }
146
147    private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
148        private boolean mWifiEnabled;
149        private boolean mWifiConnected;
150        private boolean mAirplaneModeEnabled;
151
152        @Override
153        public void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId,
154                boolean activityIn, boolean activityOut,
155                String wifiSignalContentDescriptionId, String description) {
156            mWifiEnabled = enabled;
157            mWifiConnected = connected;
158        }
159
160        @Override
161        public void onMobileDataSignalChanged(boolean enabled,
162                int mobileSignalIconId,
163                String mobileSignalContentDescriptionId, int dataTypeIconId,
164                boolean activityIn, boolean activityOut,
165                String dataTypeContentDescriptionId, String description, boolean noSim) {
166            final CallbackInfo info = new CallbackInfo();  // TODO pool?
167            info.enabled = enabled;
168            info.wifiEnabled = mWifiEnabled;
169            info.wifiConnected = mWifiConnected;
170            info.airplaneModeEnabled = mAirplaneModeEnabled;
171            info.mobileSignalIconId = mobileSignalIconId;
172            info.signalContentDescription = mobileSignalContentDescriptionId;
173            info.dataTypeIconId = dataTypeIconId;
174            info.dataContentDescription = dataTypeContentDescriptionId;
175            info.activityIn = activityIn;
176            info.activityOut = activityOut;
177            info.enabledDesc = description;
178            info.noSim = noSim;
179            refreshState(info);
180        }
181
182        @Override
183        public void onAirplaneModeChanged(boolean enabled) {
184            mAirplaneModeEnabled = enabled;
185        }
186
187        public void onMobileDataEnabled(boolean enabled) {
188            mDetailAdapter.setMobileDataEnabled(enabled);
189        }
190    };
191
192    private final class CellularDetailAdapter implements DetailAdapter {
193        private static final double KB = 1024;
194        private static final double MB = 1024 * KB;
195        private static final double GB = 1024 * MB;
196
197        private final DecimalFormat FORMAT = new DecimalFormat("#.##");
198
199        @Override
200        public int getTitle() {
201            return R.string.quick_settings_cellular_detail_title;
202        }
203
204        @Override
205        public Boolean getToggleState() {
206            return mController.isMobileDataSupported() ? mController.isMobileDataEnabled() : null;
207        }
208
209        @Override
210        public Intent getSettingsIntent() {
211            return CELLULAR_SETTINGS;
212        }
213
214        @Override
215        public void setToggleState(boolean state) {
216            mController.setMobileDataEnabled(state);
217        }
218
219        @Override
220        public View createDetailView(Context context, View convertView, ViewGroup parent) {
221            final View v = convertView != null ? convertView : LayoutInflater.from(mContext)
222                    .inflate(R.layout.data_usage, parent, false);
223            final DataUsageInfo info = mController.getDataUsageInfo();
224            if (info == null) return v;
225            final Resources res = mContext.getResources();
226            final int titleId;
227            final long bytes;
228            int usageColor = R.color.system_accent_color;
229            final String top;
230            String bottom = null;
231            if (info.usageLevel < info.warningLevel || info.limitLevel <= 0) {
232                // under warning, or no limit
233                titleId = R.string.quick_settings_cellular_detail_data_usage;
234                bytes = info.usageLevel;
235                top = res.getString(R.string.quick_settings_cellular_detail_data_warning,
236                        formatBytes(info.warningLevel));
237            } else if (info.usageLevel <= info.limitLevel) {
238                // over warning, under limit
239                titleId = R.string.quick_settings_cellular_detail_remaining_data;
240                bytes = info.limitLevel - info.usageLevel;
241                top = res.getString(R.string.quick_settings_cellular_detail_data_used,
242                        formatBytes(info.usageLevel));
243                bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit,
244                        formatBytes(info.limitLevel));
245            } else {
246                // over limit
247                titleId = R.string.quick_settings_cellular_detail_over_limit;
248                bytes = info.usageLevel - info.limitLevel;
249                top = res.getString(R.string.quick_settings_cellular_detail_data_used,
250                        formatBytes(info.usageLevel));
251                bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit,
252                        formatBytes(info.limitLevel));
253                usageColor = R.color.system_warning_color;
254            }
255
256            final TextView title = (TextView) v.findViewById(android.R.id.title);
257            title.setText(titleId);
258            final TextView usage = (TextView) v.findViewById(R.id.usage_text);
259            usage.setText(formatBytes(bytes));
260            usage.setTextColor(res.getColor(usageColor));
261            final DataUsageGraph graph = (DataUsageGraph) v.findViewById(R.id.usage_graph);
262            graph.setLevels(info.limitLevel, info.warningLevel, info.usageLevel);
263            final TextView carrier = (TextView) v.findViewById(R.id.usage_carrier_text);
264            carrier.setText(info.carrier);
265            final TextView period = (TextView) v.findViewById(R.id.usage_period_text);
266            period.setText(info.period);
267            final TextView infoTop = (TextView) v.findViewById(R.id.usage_info_top_text);
268            infoTop.setVisibility(top != null ? View.VISIBLE : View.GONE);
269            infoTop.setText(top);
270            final TextView infoBottom = (TextView) v.findViewById(R.id.usage_info_bottom_text);
271            infoBottom.setVisibility(bottom != null ? View.VISIBLE : View.GONE);
272            infoBottom.setText(bottom);
273            return v;
274        }
275
276        public void setMobileDataEnabled(boolean enabled) {
277            fireToggleStateChanged(enabled);
278        }
279
280        private String formatBytes(long bytes) {
281            final long b = Math.abs(bytes);
282            double val;
283            String suffix;
284            if (b > 100 * MB) {
285                val = b / GB;
286                suffix = "GB";
287            } else if (b > 100 * KB) {
288                val = b / MB;
289                suffix = "MB";
290            } else {
291                val = b / KB;
292                suffix = "KB";
293            }
294            return FORMAT.format(val * (bytes < 0 ? -1 : 1)) + " " + suffix;
295        }
296    }
297}
298