165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.connectivity.setup;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.app.Activity;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.app.Fragment;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context;
2221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport android.content.res.Resources;
230ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liuimport android.content.res.TypedArray;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.ScanResult;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.wifi.WifiManager;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Bundle;
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Handler;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcel;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcelable;
300ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liuimport android.support.v17.leanback.widget.FacetProvider;
310ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liuimport android.support.v17.leanback.widget.ItemAlignmentFacet;
320ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liuimport android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef;
3321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport android.support.v17.leanback.widget.VerticalGridView;
3421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport android.support.v7.util.SortedList;
3521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport android.support.v7.widget.RecyclerView;
3621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport android.support.v7.widget.util.SortedListAdapterCallback;
3744ce0a96ae3043039a231fac44607df20d3186afPowei Fengimport android.text.TextUtils;
380ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liuimport android.util.DisplayMetrics;
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.LayoutInflater;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View;
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.ViewGroup;
42f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Fengimport android.view.ViewTreeObserver.OnPreDrawListener;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.inputmethod.InputMethodManager;
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.ImageView;
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.TextView;
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
476316b89e65828d537da309851529d1295efd5730Tony Mantlerimport com.android.tv.settings.R;
486316b89e65828d537da309851529d1295efd5730Tony Mantlerimport com.android.tv.settings.connectivity.WifiSecurity;
496316b89e65828d537da309851529d1295efd5730Tony Mantlerimport com.android.tv.settings.util.AccessibilityHelper;
506316b89e65828d537da309851529d1295efd5730Tony Mantler
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.ArrayList;
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.Comparator;
5321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport java.util.List;
5421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Fengimport java.util.TreeSet;
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/**
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Displays a UI for selecting a wifi network from a list in the "wizard" style.
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class SelectFromListWizardFragment extends Fragment {
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static class ListItemComparator implements Comparator<ListItem> {
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public int compare(ListItem o1, ListItem o2) {
6421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            int pinnedPos1 = o1.getPinnedPosition();
6521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            int pinnedPos2 = o2.getPinnedPosition();
6621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
6721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (pinnedPos1 != PinnedListItem.UNPINNED && pinnedPos2 == PinnedListItem.UNPINNED) {
6821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos1 == PinnedListItem.FIRST) return -1;
6921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos1 == PinnedListItem.LAST) return 1;
7021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
7121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
7221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (pinnedPos1 == PinnedListItem.UNPINNED && pinnedPos2 != PinnedListItem.UNPINNED) {
7321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos2 == PinnedListItem.FIRST) return 1;
7421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos2 == PinnedListItem.LAST) return -1;
7521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
7621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
7721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (pinnedPos1 != PinnedListItem.UNPINNED && pinnedPos2 != PinnedListItem.UNPINNED) {
7821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos1 == pinnedPos2) {
7921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    PinnedListItem po1 = (PinnedListItem) o1;
8021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    PinnedListItem po2 = (PinnedListItem) o2;
8121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    return po1.getPinnedPriority() - po2.getPinnedPriority();
8221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                }
8321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (pinnedPos1 == PinnedListItem.LAST) return 1;
8421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
8521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                return -1;
8621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
8721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ScanResult o1ScanResult = o1.getScanResult();
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ScanResult o2ScanResult = o2.getScanResult();
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (o1ScanResult == null) {
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (o2ScanResult == null) {
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return 0;
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return 1;
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (o2ScanResult == null) {
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return -1;
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    int levelDiff = o2ScanResult.level - o1ScanResult.level;
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (levelDiff != 0) {
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return levelDiff;
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return o1ScanResult.SSID.compareTo(o2ScanResult.SSID);
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static class ListItem implements Parcelable {
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final String mName;
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final int mIconResource;
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final int mIconLevel;
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final boolean mHasIconLevel;
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final ScanResult mScanResult;
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public ListItem(String name, int iconResource) {
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mName = name;
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mIconResource = iconResource;
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mIconLevel = 0;
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHasIconLevel = false;
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScanResult = null;
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public ListItem(ScanResult scanResult) {
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mName = scanResult.SSID;
1287e552863cea67e57b375f3db15e0baf70e22c39eTony Mantler            mIconResource = WifiSecurity.NONE == WifiSecurity.getSecurity(scanResult)
1297e552863cea67e57b375f3db15e0baf70e22c39eTony Mantler                    ? R.drawable.setup_wifi_signal_open
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    : R.drawable.setup_wifi_signal_lock;
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mIconLevel = WifiManager.calculateSignalLevel(scanResult.level, 4);
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHasIconLevel = true;
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScanResult = scanResult;
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public String getName() {
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mName;
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int getIconResource() {
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mIconResource;
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int getIconLevel() {
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mIconLevel;
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        boolean hasIconLevel() {
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mHasIconLevel;
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        ScanResult getScanResult() {
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScanResult;
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        /**
15721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * Returns whether this item is pinned to the front/back of a sorted list.  Returns
15821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * PinnedListItem.UNPINNED if the item is not pinned.
15921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * @return  the pinned/unpinned setting for this item.
16021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         */
16121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public int getPinnedPosition() {
16221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return PinnedListItem.UNPINNED;
16321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
16421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public String toString() {
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mName;
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public static Parcelable.Creator<ListItem> CREATOR = new Parcelable.Creator<ListItem>() {
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public ListItem createFromParcel(Parcel source) {
1747e552863cea67e57b375f3db15e0baf70e22c39eTony Mantler                ScanResult scanResult = source.readParcelable(ScanResult.class.getClassLoader());
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (scanResult == null) {
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return new ListItem(source.readString(), source.readInt());
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return new ListItem(scanResult);
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public ListItem[] newArray(int size) {
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return new ListItem[size];
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        };
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public int describeContents() {
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return 0;
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void writeToParcel(Parcel dest, int flags) {
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dest.writeParcelable(mScanResult, flags);
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScanResult == null) {
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                dest.writeString(mName);
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                dest.writeInt(mIconResource);
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public boolean equals(Object o) {
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (o instanceof ListItem) {
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                ListItem li = (ListItem) o;
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mScanResult == null && li.mScanResult == null) {
20744ce0a96ae3043039a231fac44607df20d3186afPowei Feng                    return TextUtils.equals(mName, li.mName);
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
20944ce0a96ae3043039a231fac44607df20d3186afPowei Feng                return (mScanResult != null && li.mScanResult != null
21044ce0a96ae3043039a231fac44607df20d3186afPowei Feng                        && TextUtils.equals(mName, li.mName)
21144ce0a96ae3043039a231fac44607df20d3186afPowei Feng                        && WifiSecurity.getSecurity(mScanResult)
21244ce0a96ae3043039a231fac44607df20d3186afPowei Feng                                == WifiSecurity.getSecurity(li.mScanResult));
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    public static class PinnedListItem extends ListItem {
21921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public static final int UNPINNED = 0;
22021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public static final int FIRST = 1;
22121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public static final int LAST = 2;
22221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
22321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private int mPinnedPosition;
22421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private int mPinnedPriority;
22521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
22621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public PinnedListItem(
22721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                String name, int iconResource, int pinnedPosition, int pinnedPriority) {
22821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            super(name, iconResource);
22921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mPinnedPosition = pinnedPosition;
23021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mPinnedPriority = pinnedPriority;
23121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
23221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
23321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        @Override
23421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public int getPinnedPosition() {
23521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return mPinnedPosition;
23621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
23721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
23821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        /**
23921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * Returns the priority for this item, which is used for ordering the item between pinned
24021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * items in a sorted list.  For example, if two items are pinned to the front of the list
24121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * (FIRST), the priority value is used to determine their ordering.
24221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         * @return  the sorting priority for this item
24321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng         */
24421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public int getPinnedPriority() {
24521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return mPinnedPriority;
24621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
24721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    }
24821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public interface Listener {
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void onListSelectionComplete(ListItem listItem);
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void onListFocusChanged(ListItem listItem);
25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    private static interface ActionListener {
25521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public void onClick(ListItem item);
25621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public void onFocus(ListItem item);
25721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    }
25821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
2590ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu    private static class ListItemViewHolder extends RecyclerView.ViewHolder implements
2600ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu            FacetProvider {
26121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public ListItemViewHolder(View v) {
26221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            super(v);
26321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
26421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
26521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public void init(ListItem item, View.OnClickListener onClick,
26621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                View.OnFocusChangeListener onFocusChange) {
26721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            TextView title = (TextView) itemView.findViewById(R.id.list_item_text);
26821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            title.setText(item.getName());
26921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            itemView.setOnClickListener(onClick);
27021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            itemView.setOnFocusChangeListener(onFocusChange);
27121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
27221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            int iconResource = item.getIconResource();
27321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ImageView icon = (ImageView) itemView.findViewById(R.id.list_item_icon);
27421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            // Set the icon if there is one.
27521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (iconResource == 0) {
27621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                icon.setVisibility(View.GONE);
27721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                return;
27821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
27921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            icon.setVisibility(View.VISIBLE);
28021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            icon.setImageResource(iconResource);
28121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (item.hasIconLevel()) {
28221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                icon.setImageLevel(item.getIconLevel());
28321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
28421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
2850ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu
2860ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        // Provide a customized ItemAlignmentFacet so that the mean line of textView is matched.
2870ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        // Here We use mean line of the textview to work as the baseline to be matched with
2880ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        // guidance title baseline.
2890ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        @Override
2900ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        public Object getFacet(Class facet) {
2910ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu            if (facet.equals(ItemAlignmentFacet.class)) {
2920ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                ItemAlignmentFacet.ItemAlignmentDef alignedDef =
2930ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                        new ItemAlignmentFacet.ItemAlignmentDef();
2940ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                alignedDef.setItemAlignmentViewId(R.id.list_item_text);
2950ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                alignedDef.setAlignedToTextViewBaseline(false);
2960ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                alignedDef.setItemAlignmentOffset(0);
2970ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                alignedDef.setItemAlignmentOffsetWithPadding(true);
2980ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                // 50 refers to 50 percent, which refers to mid position of textView.
2990ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                alignedDef.setItemAlignmentOffsetPercent(50);
3000ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                ItemAlignmentFacet f = new ItemAlignmentFacet();
3010ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                f.setAlignmentDefs(new ItemAlignmentDef[] {alignedDef});
3020ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                return f;
3030ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu            }
3040ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu            return null;
3050ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        }
30621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    }
30721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
30821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    private class VerticalListAdapter extends RecyclerView.Adapter {
30921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private SortedList mItems;
31021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private final ActionListener mActionListener;
31121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
31221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public VerticalListAdapter(ActionListener actionListener, List<ListItem> choices) {
31321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            super();
31421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mActionListener = actionListener;
31521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ListItemComparator comparator = new ListItemComparator();
31621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mItems = new SortedList<ListItem>(
31721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    ListItem.class, new SortedListAdapterCallback<ListItem>(this) {
31821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        @Override
31921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        public int compare(ListItem t0, ListItem t1) {
32021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                            return comparator.compare(t0, t1);
32121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        }
32221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
32321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        @Override
32421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        public boolean areContentsTheSame(ListItem oldItem, ListItem newItem) {
325f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng                            return comparator.compare(oldItem, newItem) == 0;
32621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        }
32721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
32821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        @Override
32921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        public boolean areItemsTheSame(ListItem item1, ListItem item2) {
33021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                            return item1.equals(item2);
33121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        }
33221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    });
33321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mItems.addAll(choices.toArray(new ListItem[0]), false);
33421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
33521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
33621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private View.OnClickListener createClickListener(final ListItem item) {
33721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return new View.OnClickListener() {
33821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                @Override
33921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                public void onClick(View v) {
34021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    if (v == null || v.getWindowToken() == null || mActionListener == null) {
34121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        return;
34221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    }
34321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    mActionListener.onClick(item);
34421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                }
34521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            };
34621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
34721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
34821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        private View.OnFocusChangeListener createFocusListener(final ListItem item) {
34921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return new View.OnFocusChangeListener() {
35021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                @Override
35121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                public void onFocusChange(View v, boolean hasFocus) {
35221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    if (v == null || v.getWindowToken() == null || mActionListener == null
35321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                            || !hasFocus) {
35421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                        return;
35521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    }
35621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    mActionListener.onFocus(item);
35721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                }
35821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            };
35921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
36021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
36121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        @Override
36221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
36321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(
36421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    Context.LAYOUT_INFLATER_SERVICE);
36521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            View v = inflater.inflate(R.layout.setup_list_item, parent, false);
36621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return new ListItemViewHolder(v);
36721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
36821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
36921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        @Override
37021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) {
37121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            if (position >= mItems.size()) {
37221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                return;
37321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
37421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
37521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ListItemViewHolder viewHolder = (ListItemViewHolder) baseHolder;
37621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ListItem item = (ListItem) mItems.get(position);
37721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            viewHolder.init((ListItem) item, createClickListener(item), createFocusListener(item));
37821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
37921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
38021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public SortedList<ListItem> getItems() {
38121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return mItems;
38221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
38321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
38421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        @Override
38521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public int getItemCount() {
38621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            return mItems.size();
38721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
38821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
38921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        public void updateItems(List<ListItem> inputItems) {
39021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            TreeSet<ListItem> newItemSet = new TreeSet<ListItem>(new ListItemComparator());
39121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            for (ListItem item: inputItems) {
39221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                newItemSet.add(item);
39321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
39421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ArrayList<ListItem> toRemove = new ArrayList<ListItem>();
39521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            for (int j = 0 ; j < mItems.size(); j++) {
39621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                ListItem oldItem = (ListItem) mItems.get(j);
39721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                if (!newItemSet.contains(oldItem)) {
39821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    toRemove.add(oldItem);
39921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                }
40021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
40121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            for (ListItem item: toRemove) {
40221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                mItems.remove(item);
40321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            }
40421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            mItems.addAll(inputItems.toArray(new ListItem[0]), true);
40521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
40621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    }
40721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String EXTRA_TITLE = "title";
40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String EXTRA_DESCRIPTION = "description";
41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String EXTRA_LIST_ELEMENTS = "list_elements";
41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String EXTRA_LAST_SELECTION = "last_selection";
412f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    private static final int SELECT_ITEM_DELAY = 100;
41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static SelectFromListWizardFragment newInstance(String title, String description,
41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ArrayList<ListItem> listElements, ListItem lastSelection) {
41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        SelectFromListWizardFragment fragment = new SelectFromListWizardFragment();
41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Bundle args = new Bundle();
41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        args.putString(EXTRA_TITLE, title);
41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        args.putString(EXTRA_DESCRIPTION, description);
42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        args.putParcelableArrayList(EXTRA_LIST_ELEMENTS, listElements);
42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        args.putParcelable(EXTRA_LAST_SELECTION, lastSelection);
42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        fragment.setArguments(args);
42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return fragment;
42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Handler mHandler;
42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private View mMainView;
42821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    private VerticalGridView mListView;
42921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    private String mLastSelectedName;
430f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    private OnPreDrawListener mOnListPreDrawListener;
431f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    private Runnable mSelectItemRunnable;
43221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
43321e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    private void updateSelected(String lastSelectionName) {
43421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        SortedList<ListItem> items = ((VerticalListAdapter) mListView.getAdapter()).getItems();
43521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        for (int i = 0; i < items.size(); i++) {
43621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            ListItem item = (ListItem) items.get(i);
43744ce0a96ae3043039a231fac44607df20d3186afPowei Feng            if (TextUtils.equals(lastSelectionName, item.getName())) {
43821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                mListView.setSelectedPosition(i);
43921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                break;
44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
442f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        mLastSelectedName = lastSelectionName;
44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
44521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    public void update(List<ListItem> listElements) {
446f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        // We want keep the highlight on the same selected item from before the update.  This is
447f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        // currently not possible (b/28120126).  So we post a runnable to run after the update
448f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        // completes.
449f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        if (mSelectItemRunnable != null) {
450f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mHandler.removeCallbacks(mSelectItemRunnable);
451f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        }
452f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng
453f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        final String lastSelected = mLastSelectedName;
454f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        mSelectItemRunnable = () -> {
455f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            updateSelected(lastSelected);
456f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            if (mOnListPreDrawListener != null) {
457f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng                mListView.getViewTreeObserver().removeOnPreDrawListener(mOnListPreDrawListener);
458f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng                mOnListPreDrawListener = null;
459f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            }
460f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mSelectItemRunnable = null;
461f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        };
462f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng
463f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        if (mOnListPreDrawListener != null) {
464f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mListView.getViewTreeObserver().removeOnPreDrawListener(mOnListPreDrawListener);
465f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        }
466f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng
467f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        mOnListPreDrawListener = () -> {
468f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mHandler.removeCallbacks(mSelectItemRunnable);
469f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            // Pre-draw can be called multiple times per update.  We delay the runnable to select
470f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            // the item so that it will only run after the last pre-draw of this batch of update.
471f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mHandler.postDelayed(mSelectItemRunnable, SELECT_ITEM_DELAY);
472f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            return true;
473f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        };
474f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng
475f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        mListView.getViewTreeObserver().addOnPreDrawListener(mOnListPreDrawListener);
476f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        ((VerticalListAdapter) mListView.getAdapter()).updateItems(listElements);
47721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng    }
47821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
4790ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu    private static float getKeyLinePercent(Context context) {
4800ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        TypedArray ta = context.getTheme().obtainStyledAttributes(
4810ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                R.styleable.LeanbackGuidedStepTheme);
4820ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        float percent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline, 40);
4830ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        ta.recycle();
4840ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        return percent;
4850ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu    }
4860ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu
48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
48921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        Resources resources = getContext().getResources();
49021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mHandler = new Handler();
49265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mMainView = inflater.inflate(R.layout.account_content_area, container, false);
4936316b89e65828d537da309851529d1295efd5730Tony Mantler
4946316b89e65828d537da309851529d1295efd5730Tony Mantler        final ViewGroup descriptionArea = (ViewGroup) mMainView.findViewById(R.id.description);
4956316b89e65828d537da309851529d1295efd5730Tony Mantler        final View content = inflater.inflate(R.layout.wifi_content, descriptionArea, false);
4966316b89e65828d537da309851529d1295efd5730Tony Mantler        descriptionArea.addView(content);
4976316b89e65828d537da309851529d1295efd5730Tony Mantler
4986316b89e65828d537da309851529d1295efd5730Tony Mantler        final ViewGroup actionArea = (ViewGroup) mMainView.findViewById(R.id.action);
49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
500b4785a2a647d8be3c090a950fb909552ec3578caZhizhi Liu        TextView titleText = (TextView) content.findViewById(R.id.guidance_title);
501b4785a2a647d8be3c090a950fb909552ec3578caZhizhi Liu        TextView descriptionText = (TextView) content.findViewById(R.id.guidance_description);
50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Bundle args = getArguments();
50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String title = args.getString(EXTRA_TITLE);
50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String description = args.getString(EXTRA_DESCRIPTION);
50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        boolean forceFocusable = AccessibilityHelper.forceFocusableViews(getActivity());
50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (title != null) {
50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            titleText.setText(title);
50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            titleText.setVisibility(View.VISIBLE);
51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (forceFocusable) {
51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                titleText.setFocusable(true);
51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                titleText.setFocusableInTouchMode(true);
51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            titleText.setVisibility(View.GONE);
51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (description != null) {
51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            descriptionText.setText(description);
52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            descriptionText.setVisibility(View.VISIBLE);
52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (forceFocusable) {
52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                descriptionText.setFocusable(true);
52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                descriptionText.setFocusableInTouchMode(true);
52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            descriptionText.setVisibility(View.GONE);
52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
52921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        ArrayList<ListItem> listItems = args.getParcelableArrayList(EXTRA_LIST_ELEMENTS);
53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
53121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        mListView =
53221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                (VerticalGridView) inflater.inflate(R.layout.setup_list_view, actionArea, false);
5330ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu
5340ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        SelectFromListWizardFragment.align(mListView, getActivity());
53521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng
53621e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        actionArea.addView(mListView);
53721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        ActionListener actionListener = new ActionListener() {
53865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
53921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            public void onClick(ListItem item) {
54065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Activity a = getActivity();
54148fce31d5176b2f4100da514dd46a45f24e41c84Tony Mantler                if (a instanceof Listener && isResumed()) {
54221e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    ((Listener) a).onListSelectionComplete(item);
54365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
54465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
54565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
54665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
54721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            public void onFocus(ListItem item) {
54865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Activity a = getActivity();
54921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                mLastSelectedName = item.getName();
55065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (a instanceof Listener) {
55121e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng                    ((Listener) a).onListFocusChanged(item);
55265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
55365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
55421e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        };
55521e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        mListView.setAdapter(new VerticalListAdapter(actionListener, listItems));
55665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
55721e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        ListItem lastSelection = args.getParcelable(EXTRA_LAST_SELECTION);
55821e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        if (lastSelection != null) {
55921e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng            updateSelected(lastSelection.getName());
56021e8c04b87d6cdf30547fed13a1f7ddf606438f9Powei Feng        }
56165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mMainView;
56265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
56365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5640ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu    private static void align(VerticalGridView listView, Activity activity) {
5650ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        Context context = listView.getContext();
5660ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        DisplayMetrics displayMetrics = new DisplayMetrics();
5670ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        float keyLinePercent = getKeyLinePercent(context);
5680ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
5690ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu
5700ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        listView.setItemSpacing(activity.getResources()
5710ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu                .getDimensionPixelSize(R.dimen.setup_list_item_margin));
5720ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        // Make the keyline of the page match with the mean line(roughly) of the first list item.
5730ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        listView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE);
5740ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        listView.setWindowAlignmentOffset(0);
5750ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu        listView.setWindowAlignmentOffsetPercent(keyLinePercent);
5760ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu    }
5770ef274ff1eab910d220b02bc5ef8c8e2937fd714Zhizhi Liu
57865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
579f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    public void onPause() {
580f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        super.onPause();
581f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        if (mSelectItemRunnable != null) {
582f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mHandler.removeCallbacks(mSelectItemRunnable);
583f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mSelectItemRunnable = null;
584f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        }
585f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        if (mOnListPreDrawListener != null) {
586f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mListView.getViewTreeObserver().removeOnPreDrawListener(mOnListPreDrawListener);
587f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng            mOnListPreDrawListener = null;
588f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng        }
589f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    }
590f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng
591f95e0a8d4a27d101dc4c42a2989c553d306f2033Powei Feng    @Override
59265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void onResume() {
59365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        super.onResume();
59465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mHandler.post(new Runnable() {
59565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
59665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public void run() {
59765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                InputMethodManager inputMethodManager = (InputMethodManager) getActivity()
59865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        .getSystemService(Context.INPUT_METHOD_SERVICE);
59965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                inputMethodManager.hideSoftInputFromWindow(
60065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        mMainView.getApplicationWindowToken(), 0);
60165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
60265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        });
60365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
60465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
605