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 120 /** 121 * Switches the sample layouts for the preview pager. 122 * 123 * @param newLayerIndex the new layer index 124 * @param currentLayerIndex the current layer index 125 * @param currentFrameIndex the current frame index 126 * @param animate whether to enable the animation 127 */ setPreviewLayer(int newLayerIndex, int currentLayerIndex, int currentFrameIndex, final boolean animate)128 public void setPreviewLayer(int newLayerIndex, int currentLayerIndex, int currentFrameIndex, 129 final boolean animate) { 130 for (FrameLayout previewFrame : mPreviewFrames) { 131 if (currentLayerIndex >= 0) { 132 final View lastLayer = previewFrame.getChildAt(currentLayerIndex); 133 if (mViewStubInflated[currentFrameIndex][currentLayerIndex]) { 134 // Explicitly set to INVISIBLE only when the stub has 135 // already been inflated. 136 if (previewFrame == mPreviewFrames[currentFrameIndex]) { 137 setVisibility(lastLayer, View.INVISIBLE, animate); 138 } else { 139 setVisibility(lastLayer, View.INVISIBLE, false); 140 } 141 } 142 } 143 144 // Set next layer visible, as well as inflate necessary views. 145 View nextLayer = previewFrame.getChildAt(newLayerIndex); 146 if (previewFrame == mPreviewFrames[currentFrameIndex]) { 147 // Inflate immediately if the stub has not yet been inflated. 148 if (!mViewStubInflated[currentFrameIndex][newLayerIndex]) { 149 nextLayer = ((ViewStub) nextLayer).inflate(); 150 nextLayer.setAlpha(0.0f); 151 } 152 setVisibility(nextLayer, View.VISIBLE, animate); 153 } else { 154 setVisibility(nextLayer, View.VISIBLE, false); 155 } 156 } 157 } 158 setVisibility(final View view, final int visibility, boolean animate)159 private void setVisibility(final View view, final int visibility, boolean animate) { 160 final float alpha = (visibility == View.VISIBLE ? 1.0f : 0.0f); 161 if (!animate) { 162 view.setAlpha(alpha); 163 view.setVisibility(visibility); 164 } else { 165 final Interpolator interpolator = (visibility == View.VISIBLE ? FADE_IN_INTERPOLATOR 166 : FADE_OUT_INTERPOLATOR); 167 if (visibility == View.VISIBLE) { 168 // Fade in animation. 169 view.animate() 170 .alpha(alpha) 171 .setInterpolator(FADE_IN_INTERPOLATOR) 172 .setDuration(CROSS_FADE_DURATION_MS) 173 .setListener(new PreviewFrameAnimatorListener()) 174 .withStartAction(new Runnable() { 175 @Override 176 public void run() { 177 view.setVisibility(visibility); 178 } 179 }); 180 } else { 181 // Fade out animation. 182 view.animate() 183 .alpha(alpha) 184 .setInterpolator(FADE_OUT_INTERPOLATOR) 185 .setDuration(CROSS_FADE_DURATION_MS) 186 .setListener(new PreviewFrameAnimatorListener()) 187 .withEndAction(new Runnable() { 188 @Override 189 public void run() { 190 view.setVisibility(visibility); 191 } 192 }); 193 } 194 } 195 } 196 runAnimationEndAction()197 private void runAnimationEndAction() { 198 if (mAnimationEndAction != null && !isAnimating()) { 199 mAnimationEndAction.run(); 200 mAnimationEndAction = null; 201 } 202 } 203 204 private class PreviewFrameAnimatorListener implements AnimatorListener { 205 @Override onAnimationStart(Animator animation)206 public void onAnimationStart(Animator animation) { 207 mAnimationCounter++; 208 } 209 210 @Override onAnimationEnd(Animator animation)211 public void onAnimationEnd(Animator animation) { 212 mAnimationCounter--; 213 runAnimationEndAction(); 214 } 215 216 @Override onAnimationCancel(Animator animation)217 public void onAnimationCancel(Animator animation) { 218 // Empty method. 219 } 220 221 @Override onAnimationRepeat(Animator animation)222 public void onAnimationRepeat(Animator animation) { 223 // Empty method. 224 } 225 } 226 } 227