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