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