1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.widget; 18 19 import android.os.Bundle; 20 import android.os.Parcelable; 21 import android.util.SparseArray; 22 import android.view.View; 23 24 /** 25 * Maintains a bundle of states for a group of views. Each view must have a unique id to identify 26 * it. There are four different strategies {@link #SAVE_NO_CHILD} {@link #SAVE_VISIBLE_CHILD} 27 * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}. 28 * <p> 29 * Why we "invent" another set of strategies beyond the default android view hierarchy saving 30 * mechanism? Because android strategy for saving view states has two limitations: all indirect 31 * descendant views must have a unique view id or their content will be messed together; no way of 32 * defining saving removed views. Those two limitations are critical to AdapterView: AdapterView 33 * will inevitably have two descendant views with same view id, we also need save the views when 34 * they are scrolled out of viewport and removed. 35 * <p> 36 * The class is currently used within {@link ScrollAdapterView}, but it might be used by other 37 * ViewGroup. 38 */ 39 public abstract class ViewsStateBundle { 40 41 /** dont save states of any child views */ 42 public static final int SAVE_NO_CHILD = 0; 43 /** only save visible child views, the states are lost when they are gone */ 44 public static final int SAVE_VISIBLE_CHILD = 1; 45 /** save visible views plus save removed child views states up to {@link #getLimitNumber()} */ 46 public static final int SAVE_LIMITED_CHILD = 2; 47 /** 48 * save visible views plus save removed child views without any limitation. This might cause out 49 * of memory, only use it when you are dealing with limited data 50 */ 51 public static final int SAVE_ALL_CHILD = 3; 52 53 public static final int SAVE_LIMITED_CHILD_DEFAULT_VALUE = 100; 54 55 private int savePolicy; 56 private int limitNumber; 57 58 private final Bundle childStates; 59 60 public ViewsStateBundle(int policy, int limit) { 61 savePolicy = policy; 62 limitNumber = limit; 63 childStates = new Bundle(); 64 } 65 66 public void clear() { 67 childStates.clear(); 68 } 69 70 /** 71 * @return the saved views states 72 */ 73 public final Bundle getChildStates() { 74 return childStates; 75 } 76 77 /** 78 * @return the savePolicy, see {@link #SAVE_NO_CHILD} {@link #SAVE_VISIBLE_CHILD} 79 * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD} 80 */ 81 public final int getSavePolicy() { 82 return savePolicy; 83 } 84 85 /** 86 * @return the limitNumber, only works when {@link #getSavePolicy()} is 87 * {@link #SAVE_LIMITED_CHILD} 88 */ 89 public final int getLimitNumber() { 90 return limitNumber; 91 } 92 93 /** 94 * @see ViewsStateBundle#getSavePolicy() 95 */ 96 public final void setSavePolicy(int savePolicy) { 97 this.savePolicy = savePolicy; 98 } 99 100 /** 101 * @see ViewsStateBundle#getLimitNumber() 102 */ 103 public final void setLimitNumber(int limitNumber) { 104 this.limitNumber = limitNumber; 105 } 106 107 /** 108 * Load view from states, it's none operation if the there is no state associated with the id. 109 * 110 * @param view view where loads into 111 * @param id unique id for the view within this ViewsStateBundle 112 */ 113 public final void loadView(View view, int id) { 114 String key = getSaveStatesKey(id); 115 SparseArray<Parcelable> container = childStates.getSparseParcelableArray(key); 116 if (container != null) { 117 view.restoreHierarchyState(container); 118 } 119 } 120 121 /** 122 * Save views regardless what's the current policy is. 123 * 124 * @param view view to save 125 * @param id unique id for the view within this ViewsStateBundle 126 */ 127 protected final void saveViewUnchecked(View view, int id) { 128 String key = getSaveStatesKey(id); 129 SparseArray<Parcelable> container = new SparseArray<Parcelable>(); 130 view.saveHierarchyState(container); 131 childStates.putSparseParcelableArray(key, container); 132 } 133 134 /** 135 * The visible view is saved when policy is not {@link #SAVE_NO_CHILD}. 136 * 137 * @param view 138 * @param id 139 */ 140 public final void saveVisibleView(View view, int id) { 141 if (savePolicy != SAVE_NO_CHILD) { 142 saveViewUnchecked(view, id); 143 } 144 } 145 146 /** 147 * Save all visible views 148 */ 149 public final void saveVisibleViews() { 150 if (savePolicy != SAVE_NO_CHILD) { 151 saveVisibleViewsUnchecked(); 152 } 153 } 154 155 /** 156 * Save list of visible views without checking policy. The method is to be implemented by 157 * subclass, client should use {@link #saveVisibleViews()}. 158 */ 159 protected abstract void saveVisibleViewsUnchecked(); 160 161 /** 162 * Save views according to policy. 163 * 164 * @param view view to save 165 * @param id unique id for the view within this ViewsStateBundle 166 */ 167 public final void saveInvisibleView(View view, int id) { 168 switch (savePolicy) { 169 case SAVE_LIMITED_CHILD: 170 if (childStates.size() > limitNumber) { 171 // TODO prune the Bundle to be under limit 172 } 173 // slip through next case section to save view 174 case SAVE_ALL_CHILD: 175 saveViewUnchecked(view, id); 176 break; 177 default: 178 break; 179 } 180 } 181 182 static String getSaveStatesKey(int id) { 183 return Integer.toString(id); 184 } 185 } 186