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