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