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.widget; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Bundle; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcelable; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.SparseArray; 2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/** 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Maintains a bundle of states for a group of views. Each view must have a unique id to identify 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * it. There are four different strategies {@link #SAVE_NO_CHILD} {@link #SAVE_VISIBLE_CHILD} 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}. 2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p> 2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Why we "invent" another set of strategies beyond the default android view hierarchy saving 3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * mechanism? Because android strategy for saving view states has two limitations: all indirect 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * descendant views must have a unique view id or their content will be messed together; no way of 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * defining saving removed views. Those two limitations are critical to AdapterView: AdapterView 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * will inevitably have two descendant views with same view id, we also need save the views when 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * they are scrolled out of viewport and removed. 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p> 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * The class is currently used within {@link ScrollAdapterView}, but it might be used by other 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * ViewGroup. 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic abstract class ViewsStateBundle { 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** dont save states of any child views */ 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int SAVE_NO_CHILD = 0; 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** only save visible child views, the states are lost when they are gone */ 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int SAVE_VISIBLE_CHILD = 1; 4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** save visible views plus save removed child views states up to {@link #getLimitNumber()} */ 4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int SAVE_LIMITED_CHILD = 2; 4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * save visible views plus save removed child views without any limitation. This might cause out 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * of memory, only use it when you are dealing with limited data 5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int SAVE_ALL_CHILD = 3; 5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int SAVE_LIMITED_CHILD_DEFAULT_VALUE = 100; 5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int savePolicy; 5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int limitNumber; 5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Bundle childStates; 5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public ViewsStateBundle(int policy, int limit) { 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane savePolicy = policy; 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane limitNumber = limit; 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane childStates = new Bundle(); 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void clear() { 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane childStates.clear(); 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @return the saved views states 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final Bundle getChildStates() { 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return childStates; 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @return the savePolicy, see {@link #SAVE_NO_CHILD} {@link #SAVE_VISIBLE_CHILD} 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD} 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final int getSavePolicy() { 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return savePolicy; 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @return the limitNumber, only works when {@link #getSavePolicy()} is 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SAVE_LIMITED_CHILD} 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final int getLimitNumber() { 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return limitNumber; 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see ViewsStateBundle#getSavePolicy() 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void setSavePolicy(int savePolicy) { 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.savePolicy = savePolicy; 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see ViewsStateBundle#getLimitNumber() 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void setLimitNumber(int limitNumber) { 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.limitNumber = limitNumber; 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Load view from states, it's none operation if the there is no state associated with the id. 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param view view where loads into 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param id unique id for the view within this ViewsStateBundle 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void loadView(View view, int id) { 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane String key = getSaveStatesKey(id); 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane SparseArray<Parcelable> container = childStates.getSparseParcelableArray(key); 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (container != null) { 11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.restoreHierarchyState(container); 11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Save views regardless what's the current policy is. 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param view view to save 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param id unique id for the view within this ViewsStateBundle 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane protected final void saveViewUnchecked(View view, int id) { 12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane String key = getSaveStatesKey(id); 12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane view.saveHierarchyState(container); 13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane childStates.putSparseParcelableArray(key, container); 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * The visible view is saved when policy is not {@link #SAVE_NO_CHILD}. 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param view 13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param id 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void saveVisibleView(View view, int id) { 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (savePolicy != SAVE_NO_CHILD) { 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane saveViewUnchecked(view, id); 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Save all visible views 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void saveVisibleViews() { 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (savePolicy != SAVE_NO_CHILD) { 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane saveVisibleViewsUnchecked(); 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Save list of visible views without checking policy. The method is to be implemented by 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * subclass, client should use {@link #saveVisibleViews()}. 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane protected abstract void saveVisibleViewsUnchecked(); 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Save views according to policy. 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param view view to save 16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param id unique id for the view within this ViewsStateBundle 16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final void saveInvisibleView(View view, int id) { 16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (savePolicy) { 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SAVE_LIMITED_CHILD: 17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (childStates.size() > limitNumber) { 17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // TODO prune the Bundle to be under limit 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // slip through next case section to save view 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SAVE_ALL_CHILD: 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane saveViewUnchecked(view, id); 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane default: 17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane static String getSaveStatesKey(int id) { 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return Integer.toString(id); 18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 186