1 /*
2  * Copyright (C) 2015 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 package com.android.messaging.ui;
17 
18 import android.os.Bundle;
19 import android.os.Parcelable;
20 import androidx.viewpager.widget.PagerAdapter;
21 import android.view.View;
22 import android.view.ViewGroup;
23 
24 import com.android.messaging.Factory;
25 import com.android.messaging.util.Assert;
26 import com.android.messaging.util.UiUtils;
27 import com.google.common.annotations.VisibleForTesting;
28 
29 /**
30  * A PagerAdapter that provides a fixed number of paged Views provided by a fixed set of
31  * {@link PagerViewHolder}'s. This allows us to put a fixed number of Views, instead of fragments,
32  * into a given ViewPager.
33  */
34 public class FixedViewPagerAdapter<T extends PagerViewHolder> extends PagerAdapter {
35     private final T[] mViewHolders;
36 
FixedViewPagerAdapter(final T[] viewHolders)37     public FixedViewPagerAdapter(final T[] viewHolders) {
38         Assert.notNull(viewHolders);
39         mViewHolders = viewHolders;
40     }
41 
42     @Override
instantiateItem(final ViewGroup container, final int position)43     public Object instantiateItem(final ViewGroup container, final int position) {
44         final PagerViewHolder viewHolder = getViewHolder(position);
45         final View view = viewHolder.getView(container);
46         if (view == null) {
47             return null;
48         }
49         view.setTag(viewHolder);
50         container.addView(view);
51         return viewHolder;
52     }
53 
54     @Override
destroyItem(final ViewGroup container, final int position, final Object object)55     public void destroyItem(final ViewGroup container, final int position, final Object object) {
56         final PagerViewHolder viewHolder = getViewHolder(position);
57         final View destroyedView = viewHolder.destroyView();
58         if (destroyedView != null) {
59             container.removeView(destroyedView);
60         }
61     }
62 
63     @Override
getCount()64     public int getCount() {
65         return mViewHolders.length;
66     }
67 
68     @Override
isViewFromObject(final View view, final Object object)69     public boolean isViewFromObject(final View view, final Object object) {
70         return view.getTag() == object;
71     }
72 
getViewHolder(final int i)73     public T getViewHolder(final int i) {
74         return getViewHolder(i, true /* rtlAware */);
75     }
76 
77     @VisibleForTesting
getViewHolder(final int i, final boolean rtlAware)78     public T getViewHolder(final int i, final boolean rtlAware) {
79         return mViewHolders[rtlAware ? getRtlPosition(i) : i];
80     }
81 
82     @Override
saveState()83     public Parcelable saveState() {
84         // The paged views in the view pager gets created and destroyed as the user scrolls through
85         // them. By default, only the pages to the immediate left and right of the current visible
86         // page are realized. Moreover, if the activity gets destroyed and recreated, the pages are
87         // automatically destroyed. Therefore, in order to preserve transient page UI states that
88         // are not persisted in the DB we'd like to store them in a Bundle when views get
89         // destroyed. When the views get recreated, we rehydrate them by passing them the saved
90         // data. When the activity gets destroyed, it invokes saveState() on this adapter to
91         // add this saved Bundle to the overall saved instance state.
92         final Bundle savedViewHolderState = new Bundle(Factory.get().getApplicationContext()
93                 .getClassLoader());
94         for (int i = 0; i < mViewHolders.length; i++) {
95             final Parcelable pageState = getViewHolder(i).saveState();
96             savedViewHolderState.putParcelable(getInstanceStateKeyForPage(i), pageState);
97         }
98         return savedViewHolderState;
99     }
100 
101     @Override
restoreState(final Parcelable state, final ClassLoader loader)102     public void restoreState(final Parcelable state, final ClassLoader loader) {
103         if (state instanceof Bundle) {
104             final Bundle restoredViewHolderState = (Bundle) state;
105             ((Bundle) state).setClassLoader(Factory.get().getApplicationContext().getClassLoader());
106             for (int i = 0; i < mViewHolders.length; i++) {
107                 final Parcelable pageState = restoredViewHolderState
108                         .getParcelable(getInstanceStateKeyForPage(i));
109                 getViewHolder(i).restoreState(pageState);
110             }
111         } else {
112             super.restoreState(state, loader);
113         }
114     }
115 
resetState()116     public void resetState() {
117         for (int i = 0; i < mViewHolders.length; i++) {
118             getViewHolder(i).resetState();
119         }
120     }
121 
getInstanceStateKeyForPage(final int i)122     private String getInstanceStateKeyForPage(final int i) {
123         return getViewHolder(i).getClass().getCanonicalName() + "_savedstate_" + i;
124     }
125 
getRtlPosition(final int position)126     protected int getRtlPosition(final int position) {
127         if (UiUtils.isRtlMode()) {
128             return mViewHolders.length - 1 - position;
129         }
130         return position;
131     }
132 }
133