• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.managedprovisioning.preprovisioning.anim;
17 
18 import static com.android.internal.util.Preconditions.checkNotNull;
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorInflater;
22 import android.animation.AnimatorSet;
23 import android.animation.ObjectAnimator;
24 import android.app.Activity;
25 import android.graphics.drawable.Animatable2;
26 import android.graphics.drawable.AnimatedVectorDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.support.annotation.NonNull;
29 import android.view.ContextThemeWrapper;
30 import android.view.View;
31 import android.view.ViewGroup.LayoutParams;
32 import android.widget.ImageView;
33 import android.widget.TextView;
34 
35 import com.android.managedprovisioning.R;
36 import com.android.managedprovisioning.model.CustomizationParams;
37 
38 import java.util.List;
39 
40 /**
41  * <p>Drives the animation showing benefits of having a Managed Profile.
42  * <p>Tightly coupled with the {@link R.layout#intro_animation} layout.
43  */
44 public class BenefitsAnimation {
45     /** Array of Id pairs: {{@link ObjectAnimator}, {@link TextView}} */
46     private static final int[][] ID_ANIMATION_TARGET = {
47             {R.anim.text_scene_0_animation, R.id.text_0},
48             {R.anim.text_scene_1_animation, R.id.text_1},
49             {R.anim.text_scene_2_animation, R.id.text_2},
50             {R.anim.text_scene_3_animation, R.id.text_3},
51             {R.anim.text_scene_master_animation, R.id.text_master}};
52 
53     private static final int[] SLIDE_CAPTION_TEXT_VIEWS = {
54             R.id.text_0, R.id.text_1, R.id.text_2, R.id.text_3};
55 
56     /** Id of an {@link ImageView} containing the animated graphic */
57     private static final int ID_ANIMATED_GRAPHIC = R.id.animated_info;
58 
59     private static final int SLIDE_COUNT = 3;
60     private static final int ANIMATION_ORIGINAL_WIDTH_PX = 1080;
61 
62     private final AnimatedVectorDrawable mTopAnimation;
63     private final Animator mTextAnimation;
64     private final Activity mActivity;
65 
66     private boolean mStopped;
67 
68     /**
69      * @param captions slide captions for the animation
70      * @param contentDescription for accessibility
71      */
BenefitsAnimation(@onNull Activity activity, @NonNull List<Integer> captions, int contentDescription, CustomizationParams customizationParams)72     public BenefitsAnimation(@NonNull Activity activity, @NonNull List<Integer> captions,
73             int contentDescription, CustomizationParams customizationParams) {
74         if (captions.size() != SLIDE_COUNT) {
75             throw new IllegalArgumentException(
76                     "Wrong number of slide captions. Expected: " + SLIDE_COUNT);
77         }
78         mActivity = checkNotNull(activity);
79         mTextAnimation = checkNotNull(assembleTextAnimation());
80         applySlideCaptions(captions);
81         applyContentDescription(contentDescription);
82 
83         setTopInfoDrawable(customizationParams);
84 
85         mTopAnimation = checkNotNull(extractAnimationFromImageView(ID_ANIMATED_GRAPHIC));
86 
87         // chain all animations together
88         chainAnimations();
89 
90         // once the screen is ready, adjust size
91         mActivity.findViewById(android.R.id.content).post(this::adjustToScreenSize);
92     }
93 
setTopInfoDrawable(CustomizationParams customizationParams)94     private void setTopInfoDrawable(CustomizationParams customizationParams) {
95         int swiperTheme = new SwiperThemeMatcher(mActivity, new ColorMatcher())
96                 .findTheme(customizationParams.mainColor);
97 
98         ContextThemeWrapper wrapper = new ContextThemeWrapper(mActivity, swiperTheme);
99         Drawable drawable =
100                 mActivity.getResources().getDrawable(
101                         R.drawable.topinfo_animation,
102                         wrapper.getTheme());
103         ImageView imageView = mActivity.findViewById(ID_ANIMATED_GRAPHIC);
104         imageView.setImageDrawable(drawable);
105     }
106 
107     /** Starts playing the animation in a loop. */
start()108     public void start() {
109         mStopped = false;
110         mTopAnimation.start();
111     }
112 
113     /** Stops the animation. */
stop()114     public void stop() {
115         mStopped = true;
116         mTopAnimation.stop();
117     }
118 
119     /**
120      * Adjust animation and text to match actual screen size
121      */
adjustToScreenSize()122     private void adjustToScreenSize() {
123         if (mActivity.isDestroyed()) {
124             return;
125         }
126 
127         ImageView animatedInfo = mActivity.findViewById(R.id.animated_info);
128         int widthPx = animatedInfo.getWidth();
129         float scaleRatio = (float) widthPx / ANIMATION_ORIGINAL_WIDTH_PX;
130 
131         // adjust animation height; width happens automatically
132         LayoutParams layoutParams = animatedInfo.getLayoutParams();
133         int originalHeight = animatedInfo.getHeight();
134         int adjustedHeight = (int) (originalHeight * scaleRatio);
135         layoutParams.height = adjustedHeight;
136         animatedInfo.setLayoutParams(layoutParams);
137 
138         // adjust captions size only if downscaling
139         if (scaleRatio < 1) {
140             for (int textViewId : SLIDE_CAPTION_TEXT_VIEWS) {
141                 View view = mActivity.findViewById(textViewId);
142                 view.setScaleX(scaleRatio);
143                 view.setScaleY(scaleRatio);
144             }
145         }
146 
147         // if the content is bigger than the screen, try to shrink just the animation
148         int offset = adjustedHeight - originalHeight;
149         int contentHeight = mActivity.findViewById(R.id.intro_po_content).getHeight() + offset;
150         int viewportHeight = mActivity.findViewById(R.id.suw_layout_content).getHeight();
151         if (contentHeight > viewportHeight) {
152             int targetHeight = layoutParams.height - (contentHeight - viewportHeight);
153             int minHeight = mActivity.getResources().getDimensionPixelSize(
154                     R.dimen.intro_animation_min_height);
155 
156             // if the animation becomes too small, leave it as is and the scrollbar will show
157             if (targetHeight >= minHeight) {
158                 layoutParams.height = targetHeight;
159                 animatedInfo.setLayoutParams(layoutParams);
160             }
161         }
162     }
163 
164     /**
165      * <p>Chains all three sub-animations, and configures them to play in sync in a loop.
166      * <p>Looping {@link AnimatedVectorDrawable} and {@link AnimatorSet} currently not possible in
167      * XML.
168      */
chainAnimations()169     private void chainAnimations() {
170         mTopAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
171             @Override
172             public void onAnimationStart(Drawable drawable) {
173                 super.onAnimationStart(drawable);
174 
175                 // starting the other animations at the same time
176                 mTextAnimation.start();
177             }
178 
179             @Override
180             public void onAnimationEnd(Drawable drawable) {
181                 super.onAnimationEnd(drawable);
182 
183                 // without explicitly stopping them, sometimes they won't restart
184                 mTextAnimation.cancel();
185 
186                 // repeating the animation in loop
187                 if (!mStopped) {
188                     mTopAnimation.start();
189                 }
190             }
191         });
192     }
193 
194     /**
195      * <p>Inflates animators required to animate text headers' part of the whole animation.
196      * <p>This has to be done through code, as setting a target on {@link
197      * android.animation.ObjectAnimator} is not currently possible in XML.
198      *
199      * @return {@link AnimatorSet} responsible for the animated text
200      */
assembleTextAnimation()201     private AnimatorSet assembleTextAnimation() {
202         Animator[] animators = new Animator[ID_ANIMATION_TARGET.length];
203         for (int i = 0; i < ID_ANIMATION_TARGET.length; i++) {
204             int[] instance = ID_ANIMATION_TARGET[i];
205             animators[i] = AnimatorInflater.loadAnimator(mActivity, instance[0]);
206             animators[i].setTarget(mActivity.findViewById(instance[1]));
207         }
208 
209         AnimatorSet animatorSet = new AnimatorSet();
210         animatorSet.playTogether(animators);
211         return animatorSet;
212     }
213 
214     /**
215      * @param captions slide titles
216      */
applySlideCaptions(List<Integer> captions)217     private void applySlideCaptions(List<Integer> captions) {
218         int slideIx = 0;
219         for (int viewId : SLIDE_CAPTION_TEXT_VIEWS) {
220             ((TextView) mActivity.findViewById(viewId)).setText(
221                     captions.get(slideIx++ % captions.size()));
222         }
223     }
224 
applyContentDescription(int contentDescription)225     private void applyContentDescription(int contentDescription) {
226         mActivity.findViewById(R.id.animation_top_level_frame).setContentDescription(
227                 mActivity.getString(contentDescription));
228     }
229 
230     /** Extracts an {@link AnimatedVectorDrawable} from a containing {@link ImageView}. */
extractAnimationFromImageView(int id)231     private AnimatedVectorDrawable extractAnimationFromImageView(int id) {
232         ImageView imageView = mActivity.findViewById(id);
233         return (AnimatedVectorDrawable) imageView.getDrawable();
234     }
235 }