1 /*
2  * Copyright (C) 2016 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.settings;
17 
18 import android.animation.Animator;
19 import android.animation.Animator.AnimatorListener;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.support.v4.view.PagerAdapter;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.ViewStub;
27 import android.view.ViewStub.OnInflateListener;
28 import android.view.animation.AccelerateInterpolator;
29 import android.view.animation.DecelerateInterpolator;
30 import android.view.animation.Interpolator;
31 import android.widget.FrameLayout;
32 import android.widget.LinearLayout;
33 import android.widget.ScrollView;
34 
35 /**
36  * A PagerAdapter used by PreviewSeekBarPreferenceFragment that for showing multiple preview screen
37  * regarding a single setting and allowing the user to swipe across them.
38  */
39 public class PreviewPagerAdapter extends PagerAdapter {
40 
41     /** Duration to use when cross-fading between previews. */
42     private static final long CROSS_FADE_DURATION_MS = 400;
43 
44     /** Interpolator to use when cross-fading between previews. */
45     private static final Interpolator FADE_IN_INTERPOLATOR = new DecelerateInterpolator();
46 
47     /** Interpolator to use when cross-fading between previews. */
48     private static final Interpolator FADE_OUT_INTERPOLATOR = new AccelerateInterpolator();
49 
50     private FrameLayout[] mPreviewFrames;
51 
52     private boolean mIsLayoutRtl;
53 
54     private Runnable mAnimationEndAction;
55 
56     private int mAnimationCounter;
57 
58     private boolean[][] mViewStubInflated;
59 
PreviewPagerAdapter(Context context, boolean isLayoutRtl, int[] previewSampleResIds, Configuration[] configurations)60     public PreviewPagerAdapter(Context context, boolean isLayoutRtl,
61             int[] previewSampleResIds, Configuration[] configurations) {
62         mIsLayoutRtl = isLayoutRtl;
63         mPreviewFrames = new FrameLayout[previewSampleResIds.length];
64         mViewStubInflated = new boolean[previewSampleResIds.length][configurations.length];
65 
66         for (int i = 0; i < previewSampleResIds.length; ++i) {
67             int p = mIsLayoutRtl ? previewSampleResIds.length - 1 - i : i;
68             mPreviewFrames[p] = new FrameLayout(context);
69             mPreviewFrames[p].setLayoutParams(new LinearLayout.LayoutParams(
70                     LinearLayout.LayoutParams.MATCH_PARENT,
71                     LinearLayout.LayoutParams.MATCH_PARENT));
72 
73             for (int j = 0; j < configurations.length; ++j) {
74                 // Create a new configuration for the specified value. It won't
75                 // have any theme set, so manually apply the current theme.
76                 final Context configContext = context.createConfigurationContext(configurations[j]);
77                 configContext.setTheme(context.getThemeResId());
78 
79                 final LayoutInflater configInflater = LayoutInflater.from(configContext);
80                 final ViewStub sampleViewStub = new ViewStub(configContext);
81                 sampleViewStub.setLayoutResource(previewSampleResIds[i]);
82                 final int fi = i, fj = j;
83                 sampleViewStub.setOnInflateListener(new OnInflateListener() {
84                     @Override
85                     public void onInflate(ViewStub stub, View inflated) {
86                         inflated.setVisibility(stub.getVisibility());
87                         mViewStubInflated[fi][fj] = true;
88                     }
89                 });
90 
91                 mPreviewFrames[p].addView(sampleViewStub);
92             }
93         }
94     }
95 
96     @Override
destroyItem(ViewGroup container, int position, Object object)97     public void destroyItem (ViewGroup container, int position, Object object) {
98         container.removeView((View) object);
99     }
100 
101     @Override
getCount()102     public int getCount() {
103         return mPreviewFrames.length;
104     }
105 
106     @Override
instantiateItem(ViewGroup container, int position)107     public Object instantiateItem(ViewGroup container, int position) {
108         container.addView(mPreviewFrames[position]);
109         return mPreviewFrames[position];
110     }
111 
112     @Override
isViewFromObject(View view, Object object)113     public boolean isViewFromObject(View view, Object object) {
114         return (view == object);
115     }
116 
isAnimating()117     boolean isAnimating() {
118         return mAnimationCounter > 0;
119     }
120 
setAnimationEndAction(Runnable action)121     void setAnimationEndAction(Runnable action) {
122         mAnimationEndAction = action;
123     }
124 
setPreviewLayer(int newLayerIndex, int currentLayerIndex, int currentFrameIndex, final boolean animate)125     void setPreviewLayer(int newLayerIndex, int currentLayerIndex, int currentFrameIndex,
126             final boolean animate) {
127         for (FrameLayout previewFrame : mPreviewFrames) {
128             if (currentLayerIndex >= 0) {
129                 final View lastLayer = previewFrame.getChildAt(currentLayerIndex);
130                 if (mViewStubInflated[currentFrameIndex][currentLayerIndex]) {
131                     // Explicitly set to INVISIBLE only when the stub has
132                     // already been inflated.
133                     if (previewFrame == mPreviewFrames[currentFrameIndex]) {
134                         setVisibility(lastLayer, View.INVISIBLE, animate);
135                     } else {
136                         setVisibility(lastLayer, View.INVISIBLE, false);
137                     }
138                 }
139             }
140 
141             // Set next layer visible, as well as inflate necessary views.
142             View nextLayer = previewFrame.getChildAt(newLayerIndex);
143             if (previewFrame == mPreviewFrames[currentFrameIndex]) {
144                 // Inflate immediately if the stub has not yet been inflated.
145                 if (!mViewStubInflated[currentFrameIndex][newLayerIndex]) {
146                     nextLayer = ((ViewStub) nextLayer).inflate();
147                     nextLayer.setAlpha(0.0f);
148                 }
149                 setVisibility(nextLayer, View.VISIBLE, animate);
150             } else {
151                 setVisibility(nextLayer, View.VISIBLE, false);
152             }
153         }
154     }
155 
setVisibility(final View view, final int visibility, boolean animate)156     private void setVisibility(final View view, final int visibility, boolean animate) {
157         final float alpha = (visibility == View.VISIBLE ? 1.0f : 0.0f);
158         if (!animate) {
159             view.setAlpha(alpha);
160             view.setVisibility(visibility);
161         } else {
162             final Interpolator interpolator = (visibility == View.VISIBLE ? FADE_IN_INTERPOLATOR
163                     : FADE_OUT_INTERPOLATOR);
164             if (visibility == View.VISIBLE) {
165                 // Fade in animation.
166                 view.animate()
167                 .alpha(alpha)
168                 .setInterpolator(FADE_IN_INTERPOLATOR)
169                 .setDuration(CROSS_FADE_DURATION_MS)
170                 .setListener(new PreviewFrameAnimatorListener())
171                 .withStartAction(new Runnable() {
172                     @Override
173                     public void run() {
174                         view.setVisibility(visibility);
175                     }
176                 });
177             } else {
178                 // Fade out animation.
179                 view.animate()
180                 .alpha(alpha)
181                 .setInterpolator(FADE_OUT_INTERPOLATOR)
182                 .setDuration(CROSS_FADE_DURATION_MS)
183                 .setListener(new PreviewFrameAnimatorListener())
184                 .withEndAction(new Runnable() {
185                     @Override
186                     public void run() {
187                         view.setVisibility(visibility);
188                     }
189                 });
190             }
191         }
192     }
193 
runAnimationEndAction()194     private void runAnimationEndAction() {
195         if (mAnimationEndAction != null && !isAnimating()) {
196             mAnimationEndAction.run();
197             mAnimationEndAction = null;
198         }
199     }
200 
201     private class PreviewFrameAnimatorListener implements AnimatorListener {
202         @Override
onAnimationStart(Animator animation)203         public void onAnimationStart(Animator animation) {
204             mAnimationCounter++;
205         }
206 
207         @Override
onAnimationEnd(Animator animation)208         public void onAnimationEnd(Animator animation) {
209             mAnimationCounter--;
210             runAnimationEndAction();
211         }
212 
213         @Override
onAnimationCancel(Animator animation)214         public void onAnimationCancel(Animator animation) {
215             // Empty method.
216         }
217 
218         @Override
onAnimationRepeat(Animator animation)219         public void onAnimationRepeat(Animator animation) {
220             // Empty method.
221         }
222     }
223 }
224