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