AccessPointPreference.java revision 7ed62eb7a214126d3dbfd6abd7d6a7db73cc9f3c
1/*
2 * Copyright (C) 2016 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.settingslib.wifi;
18
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.graphics.drawable.Drawable;
22import android.graphics.drawable.StateListDrawable;
23import android.net.wifi.WifiConfiguration;
24import android.os.Looper;
25import android.os.UserHandle;
26import android.support.v7.preference.Preference;
27import android.support.v7.preference.PreferenceViewHolder;
28import android.text.TextUtils;
29import android.util.AttributeSet;
30import android.util.SparseArray;
31import android.view.View;
32import android.widget.TextView;
33
34import com.android.settingslib.R;
35
36import java.util.Objects;
37
38public class AccessPointPreference extends Preference {
39
40    private static final int[] STATE_SECURED = {
41            R.attr.state_encrypted
42    };
43    private static final int[] STATE_NONE = {};
44
45    private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
46
47    private final StateListDrawable mWifiSld;
48    private final int mBadgePadding;
49    private final UserBadgeCache mBadgeCache;
50
51    private TextView mTitleView;
52    private boolean mForSavedNetworks = false;
53    private AccessPoint mAccessPoint;
54    private Drawable mBadge;
55    private int mLevel;
56    private CharSequence mContentDescription;
57
58    static final int[] WIFI_CONNECTION_STRENGTH = {
59            R.string.accessibility_wifi_one_bar,
60            R.string.accessibility_wifi_two_bars,
61            R.string.accessibility_wifi_three_bars,
62            R.string.accessibility_wifi_signal_full
63    };
64
65    // Used for dummy pref.
66    public AccessPointPreference(Context context, AttributeSet attrs) {
67        super(context, attrs);
68        mWifiSld = null;
69        mBadgePadding = 0;
70        mBadgeCache = null;
71    }
72
73    public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
74            boolean forSavedNetworks) {
75        super(context);
76        mBadgeCache = cache;
77        mAccessPoint = accessPoint;
78        mForSavedNetworks = forSavedNetworks;
79        mAccessPoint.setTag(this);
80        mLevel = -1;
81
82        mWifiSld = (StateListDrawable) context.getTheme()
83                .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
84
85        // Distance from the end of the title at which this AP's user badge should sit.
86        mBadgePadding = context.getResources()
87                .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
88        refresh();
89    }
90
91    public AccessPoint getAccessPoint() {
92        return mAccessPoint;
93    }
94
95    @Override
96    public void onBindViewHolder(final PreferenceViewHolder view) {
97        super.onBindViewHolder(view);
98        if (mAccessPoint == null) {
99            // Used for dummy pref.
100            return;
101        }
102        Drawable drawable = getIcon();
103        if (drawable != null) {
104            drawable.setLevel(mLevel);
105        }
106
107        mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
108        if (mTitleView != null) {
109            // Attach to the end of the title view
110            mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
111            mTitleView.setCompoundDrawablePadding(mBadgePadding);
112        }
113        view.itemView.setContentDescription(mContentDescription);
114        view.itemView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
115    }
116
117    protected void updateIcon(int level, Context context) {
118        if (level == -1) {
119            setIcon(null);
120        } else {
121            if (getIcon() == null) {
122                // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
123                // set the icon (drawable) to that state's drawable.
124                // If sld is null then we are indexing and therefore do not have access to
125                // (nor need to display) the drawable.
126                if (mWifiSld != null) {
127                    mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
128                            ? STATE_SECURED
129                            : STATE_NONE);
130                    Drawable drawable = mWifiSld.getCurrent();
131                    if (!mForSavedNetworks) {
132                        setIcon(drawable);
133                    } else {
134                        setIcon(null);
135                    }
136                }
137            }
138        }
139    }
140
141    protected void updateBadge(Context context) {
142        WifiConfiguration config = mAccessPoint.getConfig();
143        if (config != null) {
144            // Fetch badge (may be null)
145            // Get the badge using a cache since the PM will ask the UserManager for the list
146            // of profiles every time otherwise.
147            mBadge = mBadgeCache.getUserBadge(config.creatorUid);
148        }
149    }
150
151    /**
152     * Updates the title and summary; may indirectly call notifyChanged().
153     */
154    public void refresh() {
155        boolean updated = false;
156        if (mForSavedNetworks) {
157            setTitle(mAccessPoint.getConfigName());
158        } else {
159            setTitle(mAccessPoint.getSsid());
160        }
161
162        final Context context = getContext();
163        int level = mAccessPoint.getLevel();
164        if (level != mLevel) {
165            mLevel = level;
166            updateIcon(mLevel, context);
167            updated = true;
168        }
169        updateBadge(context);
170
171        setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
172                : mAccessPoint.getSettingsSummary());
173
174        CharSequence contentDescription = getTitle();
175        if (getSummary() != null) {
176            contentDescription = TextUtils.concat(contentDescription, ",", getSummary());
177        }
178        if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
179            contentDescription = TextUtils.concat(contentDescription, ",",
180                    getContext().getString(WIFI_CONNECTION_STRENGTH[level]));
181        }
182        if (!Objects.equals(contentDescription, mContentDescription)) {
183            mContentDescription = contentDescription;
184            updated = true;
185        }
186        if (updated) {
187            notifyChanged();
188        }
189    }
190
191    @Override
192    protected void notifyChanged() {
193        if (Looper.getMainLooper() != Looper.myLooper()) {
194            // Let our BG thread callbacks call setTitle/setSummary.
195            postNotifyChanged();
196        } else {
197            super.notifyChanged();
198        }
199    }
200
201    public void onLevelChanged() {
202        postNotifyChanged();
203    }
204
205    private void postNotifyChanged() {
206        if (mTitleView != null) {
207            mTitleView.post(mNotifyChanged);
208        } // Otherwise we haven't been bound yet, and don't need to update.
209    }
210
211    private final Runnable mNotifyChanged = new Runnable() {
212        @Override
213        public void run() {
214            notifyChanged();
215        }
216    };
217
218    public static class UserBadgeCache {
219        private final SparseArray<Drawable> mBadges = new SparseArray<>();
220        private final PackageManager mPm;
221
222        public UserBadgeCache(PackageManager pm) {
223            mPm = pm;
224        }
225
226        private Drawable getUserBadge(int userId) {
227            int index = mBadges.indexOfKey(userId);
228            if (index < 0) {
229                Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
230                mBadges.put(userId, badge);
231                return badge;
232            }
233            return mBadges.valueAt(index);
234        }
235    }
236}
237