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.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.provider.Settings;
23import android.util.Log;
24import android.view.View;
25import android.view.ViewGroup;
26
27import com.android.internal.logging.MetricsLogger;
28import com.android.settingslib.wifi.AccessPoint;
29import com.android.systemui.R;
30import com.android.systemui.qs.QSDetailItems;
31import com.android.systemui.qs.QSDetailItems.Item;
32import com.android.systemui.qs.QSTile;
33import com.android.systemui.qs.QSTileView;
34import com.android.systemui.qs.SignalTileView;
35import com.android.systemui.statusbar.policy.NetworkController;
36import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
37import com.android.systemui.statusbar.policy.NetworkController.IconState;
38import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
39
40import java.util.List;
41
42/** Quick settings tile: Wifi **/
43public class WifiTile extends QSTile<QSTile.SignalState> {
44    private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
45
46    private final NetworkController mController;
47    private final AccessPointController mWifiController;
48    private final WifiDetailAdapter mDetailAdapter;
49    private final QSTile.SignalState mStateBeforeClick = newTileState();
50
51    private final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
52
53    public WifiTile(Host host) {
54        super(host);
55        mController = host.getNetworkController();
56        mWifiController = mController.getAccessPointController();
57        mDetailAdapter = new WifiDetailAdapter();
58    }
59
60    @Override
61    public boolean supportsDualTargets() {
62        return true;
63    }
64
65    @Override
66    protected SignalState newTileState() {
67        return new SignalState();
68    }
69
70    @Override
71    public void setListening(boolean listening) {
72        if (listening) {
73            mController.addSignalCallback(mSignalCallback);
74        } else {
75            mController.removeSignalCallback(mSignalCallback);
76        }
77    }
78
79    @Override
80    public void setDetailListening(boolean listening) {
81        if (listening) {
82            mWifiController.addAccessPointCallback(mDetailAdapter);
83        } else {
84            mWifiController.removeAccessPointCallback(mDetailAdapter);
85        }
86    }
87
88    @Override
89    public DetailAdapter getDetailAdapter() {
90        return mDetailAdapter;
91    }
92
93    @Override
94    public QSTileView createTileView(Context context) {
95        return new SignalTileView(context);
96    }
97
98    @Override
99    protected void handleClick() {
100        mState.copyTo(mStateBeforeClick);
101        MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled);
102        mController.setWifiEnabled(!mState.enabled);
103    }
104
105    @Override
106    protected void handleSecondaryClick() {
107        if (!mWifiController.canConfigWifi()) {
108            mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS));
109            return;
110        }
111        if (!mState.enabled) {
112            mController.setWifiEnabled(true);
113            mState.enabled = true;
114        }
115        showDetail(true);
116    }
117
118    @Override
119    protected void handleUpdateState(SignalState state, Object arg) {
120        state.visible = true;
121        if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
122        CallbackInfo cb = (CallbackInfo) arg;
123        if (cb == null) {
124            cb = mSignalCallback.mInfo;
125        }
126
127        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
128        boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
129        boolean enabledChanging = state.enabled != cb.enabled;
130        if (enabledChanging) {
131            mDetailAdapter.setItemsVisible(cb.enabled);
132            fireToggleStateChanged(cb.enabled);
133        }
134        state.enabled = cb.enabled;
135        state.connected = wifiConnected;
136        state.activityIn = cb.enabled && cb.activityIn;
137        state.activityOut = cb.enabled && cb.activityOut;
138        state.filter = true;
139        final String signalContentDescription;
140        final Resources r = mContext.getResources();
141        if (!state.enabled) {
142            state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled);
143            state.label = r.getString(R.string.quick_settings_wifi_label);
144            signalContentDescription = r.getString(R.string.accessibility_wifi_off);
145        } else if (wifiConnected) {
146            state.icon = ResourceIcon.get(cb.wifiSignalIconId);
147            state.label = removeDoubleQuotes(cb.enabledDesc);
148            signalContentDescription = cb.wifiSignalContentDescription;
149        } else if (wifiNotConnected) {
150            state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_full_0);
151            state.label = r.getString(R.string.quick_settings_wifi_label);
152            signalContentDescription = r.getString(R.string.accessibility_no_wifi);
153        } else {
154            state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_no_network);
155            state.label = r.getString(R.string.quick_settings_wifi_label);
156            signalContentDescription = r.getString(R.string.accessibility_wifi_off);
157        }
158        state.contentDescription = mContext.getString(
159                R.string.accessibility_quick_settings_wifi,
160                signalContentDescription);
161        String wifiName = state.label;
162        if (state.connected) {
163            wifiName = r.getString(R.string.accessibility_wifi_name, state.label);
164        }
165        state.dualLabelContentDescription = wifiName;
166    }
167
168    @Override
169    public int getMetricsCategory() {
170        return MetricsLogger.QS_WIFI;
171    }
172
173    @Override
174    protected boolean shouldAnnouncementBeDelayed() {
175        return mStateBeforeClick.enabled == mState.enabled;
176    }
177
178    @Override
179    protected String composeChangeAnnouncement() {
180        if (mState.enabled) {
181            return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_on);
182        } else {
183            return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_off);
184        }
185    }
186
187    private static String removeDoubleQuotes(String string) {
188        if (string == null) return null;
189        final int length = string.length();
190        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
191            return string.substring(1, length - 1);
192        }
193        return string;
194    }
195
196    private static final class CallbackInfo {
197        boolean enabled;
198        boolean connected;
199        int wifiSignalIconId;
200        String enabledDesc;
201        boolean activityIn;
202        boolean activityOut;
203        String wifiSignalContentDescription;
204
205        @Override
206        public String toString() {
207            return new StringBuilder("CallbackInfo[")
208                .append("enabled=").append(enabled)
209                .append(",connected=").append(connected)
210                .append(",wifiSignalIconId=").append(wifiSignalIconId)
211                .append(",enabledDesc=").append(enabledDesc)
212                .append(",activityIn=").append(activityIn)
213                .append(",activityOut=").append(activityOut)
214                .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
215                .append(']').toString();
216        }
217    }
218
219    private final class WifiSignalCallback extends SignalCallbackAdapter {
220        final CallbackInfo mInfo = new CallbackInfo();
221
222        @Override
223        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
224                boolean activityIn, boolean activityOut, String description) {
225            if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
226            mInfo.enabled = enabled;
227            mInfo.connected = qsIcon.visible;
228            mInfo.wifiSignalIconId = qsIcon.icon;
229            mInfo.enabledDesc = description;
230            mInfo.activityIn = activityIn;
231            mInfo.activityOut = activityOut;
232            mInfo.wifiSignalContentDescription = qsIcon.contentDescription;
233            refreshState(mInfo);
234        }
235    };
236
237    private final class WifiDetailAdapter implements DetailAdapter,
238            NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback {
239
240        private QSDetailItems mItems;
241        private AccessPoint[] mAccessPoints;
242
243        @Override
244        public int getTitle() {
245            return R.string.quick_settings_wifi_label;
246        }
247
248        public Intent getSettingsIntent() {
249            return WIFI_SETTINGS;
250        }
251
252        @Override
253        public Boolean getToggleState() {
254            return mState.enabled;
255        }
256
257        @Override
258        public void setToggleState(boolean state) {
259            if (DEBUG) Log.d(TAG, "setToggleState " + state);
260            MetricsLogger.action(mContext, MetricsLogger.QS_WIFI_TOGGLE, state);
261            mController.setWifiEnabled(state);
262            showDetail(false);
263        }
264
265        @Override
266        public int getMetricsCategory() {
267            return MetricsLogger.QS_WIFI_DETAILS;
268        }
269
270        @Override
271        public View createDetailView(Context context, View convertView, ViewGroup parent) {
272            if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
273            mAccessPoints = null;
274            mWifiController.scanForAccessPoints();
275            fireScanStateChanged(true);
276            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
277            mItems.setTagSuffix("Wifi");
278            mItems.setCallback(this);
279            mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty,
280                    R.string.quick_settings_wifi_detail_empty_text);
281            updateItems();
282            setItemsVisible(mState.enabled);
283            return mItems;
284        }
285
286        @Override
287        public void onAccessPointsChanged(final List<AccessPoint> accessPoints) {
288            mAccessPoints = accessPoints.toArray(new AccessPoint[accessPoints.size()]);
289            updateItems();
290            if (accessPoints != null && accessPoints.size() > 0) {
291                fireScanStateChanged(false);
292            }
293        }
294
295        @Override
296        public void onSettingsActivityTriggered(Intent settingsIntent) {
297            mHost.startActivityDismissingKeyguard(settingsIntent);
298        }
299
300        @Override
301        public void onDetailItemClick(Item item) {
302            if (item == null || item.tag == null) return;
303            final AccessPoint ap = (AccessPoint) item.tag;
304            if (!ap.isActive()) {
305                if (mWifiController.connect(ap)) {
306                    mHost.collapsePanels();
307                }
308            }
309            showDetail(false);
310        }
311
312        @Override
313        public void onDetailItemDisconnect(Item item) {
314            // noop
315        }
316
317        public void setItemsVisible(boolean visible) {
318            if (mItems == null) return;
319            mItems.setItemsVisible(visible);
320        }
321
322        private void updateItems() {
323            if (mItems == null) return;
324            Item[] items = null;
325            if (mAccessPoints != null) {
326                items = new Item[mAccessPoints.length];
327                for (int i = 0; i < mAccessPoints.length; i++) {
328                    final AccessPoint ap = mAccessPoints[i];
329                    final Item item = new Item();
330                    item.tag = ap;
331                    item.icon = mWifiController.getIcon(ap);
332                    item.line1 = ap.getSsid();
333                    item.line2 = ap.isActive() ? ap.getSummary() : null;
334                    item.overlay = ap.getSecurity() != AccessPoint.SECURITY_NONE
335                            ? mContext.getDrawable(R.drawable.qs_ic_wifi_lock)
336                            : null;
337                    items[i] = item;
338                }
339            }
340            mItems.setItems(items);
341        }
342    };
343}
344