113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati/* 213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * Copyright (C) 2014 The Android Open Source Project 313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * 413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * Licensed under the Apache License, Version 2.0 (the "License"); 513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * you may not use this file except in compliance with the License. 613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * You may obtain a copy of the License at 713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * 813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * http://www.apache.org/licenses/LICENSE-2.0 913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * 1013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * Unless required by applicable law or agreed to in writing, software 1113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * distributed under the License is distributed on an "AS IS" BASIS, 1213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * See the License for the specific language governing permissions and 1413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati * limitations under the License 1513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati */ 1613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 1713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatipackage com.android.phone.common.animation; 1813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 1913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.animation.Animator; 2013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.animation.AnimatorListenerAdapter; 215ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Leeimport android.animation.ValueAnimator; 2213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.view.View; 2313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.view.ViewPropertyAnimator; 2413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.view.animation.Interpolator; 2513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatiimport android.view.animation.PathInterpolator; 2613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 275ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Leeimport java.lang.Float; 285ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee 2913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapatipublic class AnimUtils { 3013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static final int DEFAULT_DURATION = -1; 31451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static final int NO_DELAY = 0; 32451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee 3313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static final Interpolator EASE_IN = new PathInterpolator(0.0f, 0.0f, 0.2f, 1.0f); 3413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static final Interpolator EASE_OUT = new PathInterpolator(0.4f, 0.0f, 1.0f, 1.0f); 3513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static final Interpolator EASE_OUT_EASE_IN = new PathInterpolator(0.4f, 0, 0.2f, 1); 3613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 3713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static class AnimationCallback { 3813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationEnd() {} 3913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationCancel() {} 4013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 4113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 4213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static void crossFadeViews(View fadeIn, View fadeOut, int duration) { 4313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeIn(fadeIn, duration); 4413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut(fadeOut, duration); 4513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 4613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 4713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public static void fadeOut(View fadeOut, int duration) { 4813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut(fadeOut, duration, null); 4913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 5013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 51451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static void fadeOut(final View fadeOut, int durationMs, 52451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee final AnimationCallback callback) { 5313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut.setAlpha(1); 5413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati final ViewPropertyAnimator animator = fadeOut.animate(); 5513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.cancel(); 5613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.alpha(0).withLayer().setListener(new AnimatorListenerAdapter() { 5713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati @Override 5813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationEnd(Animator animation) { 5913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut.setVisibility(View.GONE); 6013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati if (callback != null) { 6113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati callback.onAnimationEnd(); 6213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 6313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 6413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 6513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati @Override 6613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationCancel(Animator animation) { 6713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut.setVisibility(View.GONE); 6813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeOut.setAlpha(0); 6913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati if (callback != null) { 7013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati callback.onAnimationCancel(); 7113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 7213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 7313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati }); 74451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee if (durationMs != DEFAULT_DURATION) { 75451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee animator.setDuration(durationMs); 7613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 7713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.start(); 7813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 7913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 80451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static void fadeIn(View fadeIn, int durationMs) { 81451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee fadeIn(fadeIn, durationMs, NO_DELAY, null); 82b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 83b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 84451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static void fadeIn(final View fadeIn, int durationMs, int delay, 85451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee final AnimationCallback callback) { 8613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeIn.setAlpha(0); 8713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati final ViewPropertyAnimator animator = fadeIn.animate(); 8813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.cancel(); 89b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 90b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee animator.setStartDelay(delay); 9113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.alpha(1).withLayer().setListener(new AnimatorListenerAdapter() { 9213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati @Override 9313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationStart(Animator animation) { 9413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeIn.setVisibility(View.VISIBLE); 9513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 9613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 9713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati @Override 9813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationCancel(Animator animation) { 9913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati fadeIn.setAlpha(1); 10013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati if (callback != null) { 10113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati callback.onAnimationCancel(); 10213b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 10313b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 10413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati 10513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati @Override 10613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati public void onAnimationEnd(Animator animation) { 10713b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati if (callback != null) { 10813b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati callback.onAnimationEnd(); 10913b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 11013b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 11113b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati }); 112451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee if (durationMs != DEFAULT_DURATION) { 113451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee animator.setDuration(durationMs); 11413b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 11513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati animator.start(); 11613b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati } 1175ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee 1185ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee /** 119b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee * Scales in the view from scale of 0 to actual dimensions. 120b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee * @param view The view to scale. 121451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee * @param durationMs The duration of the scaling in milliseconds. 122451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee * @param startDelayMs The delay to applying the scaling in milliseconds. 123b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee */ 124451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static void scaleIn(final View view, int durationMs, int startDelayMs) { 125b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee AnimatorListenerAdapter listener = (new AnimatorListenerAdapter() { 126b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee @Override 127b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee public void onAnimationStart(Animator animation) { 128b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setVisibility(View.VISIBLE); 129b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 130b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 131b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee @Override 132b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee public void onAnimationCancel(Animator animation) { 133b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleX(1); 134b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleY(1); 135b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 136b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee }); 137451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee scaleInternal(view, 0 /* startScaleValue */, 1 /* endScaleValue */, durationMs, 138451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee startDelayMs, listener, EASE_IN); 139b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 140b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 141b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 142b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee /** 143b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee * Scales out the view from actual dimensions to 0. 144b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee * @param view The view to scale. 145451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee * @param durationMs The duration of the scaling in milliseconds. 146b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee */ 147451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee public static void scaleOut(final View view, int durationMs) { 148b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { 149b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee @Override 150b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee public void onAnimationEnd(Animator animation) { 151b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setVisibility(View.GONE); 152b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 153b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 154b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee @Override 155b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee public void onAnimationCancel(Animator animation) { 156b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setVisibility(View.GONE); 157b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleX(0); 158b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleY(0); 159b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 160b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee }; 161b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 162451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee scaleInternal(view, 1 /* startScaleValue */, 0 /* endScaleValue */, durationMs, 163451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee NO_DELAY, listener, EASE_OUT); 164b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 165b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 166451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee private static void scaleInternal(final View view, int startScaleValue, int endScaleValue, 167451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee int durationMs, int startDelay, AnimatorListenerAdapter listener, 168451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee Interpolator interpolator) { 169b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleX(startScaleValue); 170b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee view.setScaleY(startScaleValue); 171b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 172b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee final ViewPropertyAnimator animator = view.animate(); 173b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee animator.cancel(); 174b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 175451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee animator.setInterpolator(interpolator) 176b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee .scaleX(endScaleValue) 177b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee .scaleY(endScaleValue) 178451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee .setListener(listener) 179451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee .withLayer(); 180b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 181451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee if (durationMs != DEFAULT_DURATION) { 182451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee animator.setDuration(durationMs); 183b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 184451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee animator.setStartDelay(startDelay); 185451ac31bdc5f89257429d5c5328de14af583bd6eAndrew Lee 186b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee animator.start(); 187b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee } 188b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee 189b652c8c99a729edac5c090d866cea578db02e77bAndrew Lee /** 1905ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee * Animates a view to the new specified dimensions. 1915ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee * @param view The view to change the dimensions of. 1925ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee * @param newWidth The new width of the view. 1935ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee * @param newHeight The new height of the view. 1945ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee */ 1955ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee public static void changeDimensions(final View view, final int newWidth, final int newHeight) { 1965ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); 1975ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee 1985ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee final int oldWidth = view.getWidth(); 1995ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee final int oldHeight = view.getHeight(); 2005ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee final int deltaWidth = newWidth - oldWidth; 2015ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee final int deltaHeight = newHeight - oldHeight; 2025ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee 2035ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 2045ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee @Override 2055ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee public void onAnimationUpdate(ValueAnimator animator) { 2065ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee Float value = (Float) animator.getAnimatedValue(); 2075ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee 2085ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee view.getLayoutParams().width = (int) (value * deltaWidth + oldWidth); 2095ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee view.getLayoutParams().height = (int) (value * deltaHeight + oldHeight); 2105ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee view.requestLayout(); 2115ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee } 2125ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee }); 2135ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee animator.start(); 2145ead1b9b7742203fc0f08bee39fee79939cbfed3Andrew Lee } 21513b8948c0cc543f62a68fa32c4692ba16a2e93c6Sai Cheemalapati} 216