1906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu/* 2906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Copyright (C) 2014 The Android Open Source Project 3906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 4906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * in compliance with the License. You may obtain a copy of the License at 6906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 7906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * http://www.apache.org/licenses/LICENSE-2.0 8906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 9906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Unless required by applicable law or agreed to in writing, software distributed under the License 10906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * or implied. See the License for the specific language governing permissions and limitations under 12906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * the License. 13906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 14906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gupackage android.support.v17.leanback.widget; 15906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 16906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Guimport android.os.Bundle; 17906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Guimport android.os.Parcelable; 181102fc6fafe721522f2b67f86d89feda87096265Dake Guimport android.support.v4.util.LruCache; 19906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Guimport android.util.SparseArray; 20906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Guimport android.view.View; 21906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 221102fc6fafe721522f2b67f86d89feda87096265Dake Guimport java.util.Iterator; 231102fc6fafe721522f2b67f86d89feda87096265Dake Guimport java.util.Map; 241102fc6fafe721522f2b67f86d89feda87096265Dake Guimport java.util.Map.Entry; 251102fc6fafe721522f2b67f86d89feda87096265Dake Gu 261102fc6fafe721522f2b67f86d89feda87096265Dake Guimport static android.support.v17.leanback.widget.BaseGridView.SAVE_NO_CHILD; 271102fc6fafe721522f2b67f86d89feda87096265Dake Guimport static android.support.v17.leanback.widget.BaseGridView.SAVE_ON_SCREEN_CHILD; 281102fc6fafe721522f2b67f86d89feda87096265Dake Guimport static android.support.v17.leanback.widget.BaseGridView.SAVE_LIMITED_CHILD; 291102fc6fafe721522f2b67f86d89feda87096265Dake Guimport static android.support.v17.leanback.widget.BaseGridView.SAVE_ALL_CHILD; 301102fc6fafe721522f2b67f86d89feda87096265Dake Gu 31906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu/** 32906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Maintains a bundle of states for a group of views. Each view must have a unique id to identify 33906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * it. There are four different strategies {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD} 34906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}. 35906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * <p> 36906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * This class serves purpose of nested "listview" e.g. a vertical list of horizontal list. 37906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Vertical list maintains id->bundle mapping of all it's children (even the children is offscreen 38906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * and being pruned). 39906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * <p> 40906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * The class is currently used within {@link GridLayoutManager}, but it might be used by other 41906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * ViewGroup. 42906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 43906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Guclass ViewsStateBundle { 44906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 451102fc6fafe721522f2b67f86d89feda87096265Dake Gu public static final int LIMIT_DEFAULT = 100; 461102fc6fafe721522f2b67f86d89feda87096265Dake Gu public static final int UNLIMITED = Integer.MAX_VALUE; 47906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 48906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu private int mSavePolicy; 49906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu private int mLimitNumber; 50906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 511102fc6fafe721522f2b67f86d89feda87096265Dake Gu private LruCache<String, SparseArray<Parcelable>> mChildStates; 52906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 531102fc6fafe721522f2b67f86d89feda87096265Dake Gu public ViewsStateBundle() { 541102fc6fafe721522f2b67f86d89feda87096265Dake Gu mSavePolicy = SAVE_NO_CHILD; 551102fc6fafe721522f2b67f86d89feda87096265Dake Gu mLimitNumber = LIMIT_DEFAULT; 56906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 57906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 58906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public void clear() { 591102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates != null) { 601102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates.evictAll(); 611102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 62906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 63906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 6481a36a4dd93bf2f14c2eb88ae01464f85ddb0706Dake Gu public void remove(int id) { 651102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates != null && mChildStates.size() != 0) { 6681a36a4dd93bf2f14c2eb88ae01464f85ddb0706Dake Gu mChildStates.remove(getSaveStatesKey(id)); 6781a36a4dd93bf2f14c2eb88ae01464f85ddb0706Dake Gu } 6881a36a4dd93bf2f14c2eb88ae01464f85ddb0706Dake Gu } 6981a36a4dd93bf2f14c2eb88ae01464f85ddb0706Dake Gu 70906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 71906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @return the saved views states 72906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 731102fc6fafe721522f2b67f86d89feda87096265Dake Gu public final Bundle saveAsBundle() { 741102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates == null || mChildStates.size() == 0) { 751102fc6fafe721522f2b67f86d89feda87096265Dake Gu return null; 761102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 771102fc6fafe721522f2b67f86d89feda87096265Dake Gu Map<String, SparseArray<Parcelable>> snapshot = mChildStates.snapshot(); 781102fc6fafe721522f2b67f86d89feda87096265Dake Gu Bundle bundle = new Bundle(); 791102fc6fafe721522f2b67f86d89feda87096265Dake Gu for (Iterator<Entry<String, SparseArray<Parcelable>>> i = 801102fc6fafe721522f2b67f86d89feda87096265Dake Gu snapshot.entrySet().iterator(); i.hasNext(); ) { 811102fc6fafe721522f2b67f86d89feda87096265Dake Gu Entry<String, SparseArray<Parcelable>> e = i.next(); 821102fc6fafe721522f2b67f86d89feda87096265Dake Gu bundle.putSparseParcelableArray(e.getKey(), e.getValue()); 831102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 841102fc6fafe721522f2b67f86d89feda87096265Dake Gu return bundle; 851102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 861102fc6fafe721522f2b67f86d89feda87096265Dake Gu 871102fc6fafe721522f2b67f86d89feda87096265Dake Gu public final void loadFromBundle(Bundle savedBundle) { 881102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates != null && savedBundle != null) { 891102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates.evictAll(); 901102fc6fafe721522f2b67f86d89feda87096265Dake Gu for (Iterator<String> i = savedBundle.keySet().iterator(); i.hasNext(); ) { 911102fc6fafe721522f2b67f86d89feda87096265Dake Gu String key = i.next(); 921102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates.put(key, savedBundle.getSparseParcelableArray(key)); 931102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 941102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 95906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 96906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 97906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 98906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @return the savePolicy, see {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD} 99906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD} 100906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 101906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final int getSavePolicy() { 102906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu return mSavePolicy; 103906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 104906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 105906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 106906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @return the limitNumber, only works when {@link #getSavePolicy()} is 107906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * {@link #SAVE_LIMITED_CHILD} 108906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 109906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final int getLimitNumber() { 110906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu return mLimitNumber; 111906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 112906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 113906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 114906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @see ViewsStateBundle#getSavePolicy() 115906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 116906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final void setSavePolicy(int savePolicy) { 117906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu this.mSavePolicy = savePolicy; 1181102fc6fafe721522f2b67f86d89feda87096265Dake Gu applyPolicyChanges(); 119906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 120906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 121906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 122906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @see ViewsStateBundle#getLimitNumber() 123906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 124906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final void setLimitNumber(int limitNumber) { 125906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu this.mLimitNumber = limitNumber; 1261102fc6fafe721522f2b67f86d89feda87096265Dake Gu applyPolicyChanges(); 1271102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 1281102fc6fafe721522f2b67f86d89feda87096265Dake Gu 1291102fc6fafe721522f2b67f86d89feda87096265Dake Gu protected void applyPolicyChanges() { 1301102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mSavePolicy == SAVE_LIMITED_CHILD) { 1311102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mLimitNumber <= 0) { 1321102fc6fafe721522f2b67f86d89feda87096265Dake Gu throw new IllegalArgumentException(); 1331102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 1341102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates == null || mChildStates.maxSize() != mLimitNumber) { 1351102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates = new LruCache<String, SparseArray<Parcelable>>(mLimitNumber); 1361102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 1371102fc6fafe721522f2b67f86d89feda87096265Dake Gu } else if (mSavePolicy == SAVE_ALL_CHILD || mSavePolicy == SAVE_ON_SCREEN_CHILD) { 1381102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates == null || mChildStates.maxSize() != UNLIMITED) { 1391102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates = new LruCache<String, SparseArray<Parcelable>>(UNLIMITED); 1401102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 1411102fc6fafe721522f2b67f86d89feda87096265Dake Gu } else { 1421102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates = null; 1431102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 144906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 145906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 146906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 147906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Load view from states, it's none operation if the there is no state associated with the id. 148906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 149906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param view view where loads into 150906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param id unique id for the view within this ViewsStateBundle 151906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 152906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final void loadView(View view, int id) { 1531102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates != null) { 1541102fc6fafe721522f2b67f86d89feda87096265Dake Gu String key = getSaveStatesKey(id); 1551102fc6fafe721522f2b67f86d89feda87096265Dake Gu SparseArray<Parcelable> container = mChildStates.get(key); 1561102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (container != null) { 1571102fc6fafe721522f2b67f86d89feda87096265Dake Gu view.restoreHierarchyState(container); 1581102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 159906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 160906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 161906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 162906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 163906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Save views regardless what's the current policy is. 164906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 165906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param view view to save 166906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param id unique id for the view within this ViewsStateBundle 167906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 168906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu protected final void saveViewUnchecked(View view, int id) { 1691102fc6fafe721522f2b67f86d89feda87096265Dake Gu if (mChildStates != null) { 1701102fc6fafe721522f2b67f86d89feda87096265Dake Gu String key = getSaveStatesKey(id); 1711102fc6fafe721522f2b67f86d89feda87096265Dake Gu SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 1721102fc6fafe721522f2b67f86d89feda87096265Dake Gu view.saveHierarchyState(container); 1731102fc6fafe721522f2b67f86d89feda87096265Dake Gu mChildStates.put(key, container); 1741102fc6fafe721522f2b67f86d89feda87096265Dake Gu } 175906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 176906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 177906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 178906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * The on screen view is saved when policy is not {@link #SAVE_NO_CHILD}. 179906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 180906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param view 181906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param id 182906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 183906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final void saveOnScreenView(View view, int id) { 184906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu if (mSavePolicy != SAVE_NO_CHILD) { 185906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu saveViewUnchecked(view, id); 186906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 187906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 188906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 189906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu /** 190906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * Save off screen views according to policy. 191906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * 192906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param view view to save 193906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu * @param id unique id for the view within this ViewsStateBundle 194906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu */ 195906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu public final void saveOffscreenView(View view, int id) { 196906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu switch (mSavePolicy) { 197906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu case SAVE_LIMITED_CHILD: 198906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu case SAVE_ALL_CHILD: 199906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu saveViewUnchecked(view, id); 200906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu break; 2011102fc6fafe721522f2b67f86d89feda87096265Dake Gu case SAVE_ON_SCREEN_CHILD: 2021102fc6fafe721522f2b67f86d89feda87096265Dake Gu remove(id); 2031102fc6fafe721522f2b67f86d89feda87096265Dake Gu break; 204906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu default: 205906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu break; 206906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 207906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 208906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu 209906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu static String getSaveStatesKey(int id) { 210906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu return Integer.toString(id); 211906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu } 212906659fc65e7b8b1bc9f0c7cc3dabf7e64e8b9bfDake Gu} 213