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.getTheme().setTo(context.getTheme()); 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