1 /* 2 * Copyright (C) 2011 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 17 package com.android.systemui.recent; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorSet; 21 import android.animation.AnimatorSet.Builder; 22 import android.animation.ObjectAnimator; 23 import android.content.res.Resources; 24 import android.graphics.drawable.ColorDrawable; 25 import android.graphics.drawable.Drawable; 26 import android.util.Slog; 27 import android.view.View; 28 import android.view.ViewRootImpl; 29 30 import com.android.systemui.R; 31 32 /* package */ class Choreographer implements Animator.AnimatorListener { 33 // should group this into a multi-property animation 34 private static final int OPEN_DURATION = 136; 35 private static final int CLOSE_DURATION = 230; 36 private static final int SCRIM_DURATION = 400; 37 private static final String TAG = RecentsPanelView.TAG; 38 private static final boolean DEBUG = RecentsPanelView.DEBUG; 39 40 boolean mVisible; 41 int mPanelHeight; 42 RecentsPanelView mRootView; 43 View mScrimView; 44 View mContentView; 45 View mNoRecentAppsView; 46 AnimatorSet mContentAnim; 47 Animator.AnimatorListener mListener; 48 49 // the panel will start to appear this many px from the end 50 final int HYPERSPACE_OFFRAMP = 200; 51 52 public Choreographer(RecentsPanelView root, View scrim, View content, 53 View noRecentApps, Animator.AnimatorListener listener) { 54 mRootView = root; 55 mScrimView = scrim; 56 mContentView = content; 57 mListener = listener; 58 mNoRecentAppsView = noRecentApps; 59 } 60 61 void createAnimation(boolean appearing) { 62 float start, end; 63 64 // 0: on-screen 65 // height: off-screen 66 float y = mContentView.getTranslationY(); 67 if (appearing) { 68 // we want to go from near-the-top to the top, unless we're half-open in the right 69 // general vicinity 70 start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP; 71 end = 0; 72 } else { 73 start = y; 74 end = y; 75 } 76 77 Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY", 78 start, end); 79 posAnim.setInterpolator(appearing 80 ? new android.view.animation.DecelerateInterpolator(2.5f) 81 : new android.view.animation.AccelerateInterpolator(2.5f)); 82 posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); 83 84 Animator fadeAnim = ObjectAnimator.ofFloat(mContentView, "alpha", 85 mContentView.getAlpha(), appearing ? 1.0f : 0.0f); 86 fadeAnim.setInterpolator(appearing 87 ? new android.view.animation.AccelerateInterpolator(1.0f) 88 : new android.view.animation.AccelerateInterpolator(2.5f)); 89 fadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); 90 91 Animator noRecentAppsFadeAnim = null; 92 if (mNoRecentAppsView != null && // doesn't exist on large devices 93 mNoRecentAppsView.getVisibility() == View.VISIBLE) { 94 noRecentAppsFadeAnim = ObjectAnimator.ofFloat(mNoRecentAppsView, "alpha", 95 mContentView.getAlpha(), appearing ? 1.0f : 0.0f); 96 noRecentAppsFadeAnim.setInterpolator(appearing 97 ? new android.view.animation.AccelerateInterpolator(1.0f) 98 : new android.view.animation.DecelerateInterpolator(1.0f)); 99 noRecentAppsFadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); 100 } 101 102 mContentAnim = new AnimatorSet(); 103 final Builder builder = mContentAnim.play(fadeAnim).with(posAnim); 104 105 if (noRecentAppsFadeAnim != null) { 106 builder.with(noRecentAppsFadeAnim); 107 } 108 109 if (appearing) { 110 Drawable background = mScrimView.getBackground(); 111 if (background != null) { 112 Animator bgAnim = ObjectAnimator.ofInt(background, 113 "alpha", appearing ? 0 : 255, appearing ? 255 : 0); 114 bgAnim.setDuration(appearing ? SCRIM_DURATION : CLOSE_DURATION); 115 builder.with(bgAnim); 116 } 117 } else { 118 final Resources res = mRootView.getResources(); 119 boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets); 120 if (!isTablet) { 121 View recentsTransitionBackground = 122 mRootView.findViewById(R.id.recents_transition_background); 123 recentsTransitionBackground.setVisibility(View.VISIBLE); 124 Drawable bgDrawable = new ColorDrawable(0xFF000000); 125 recentsTransitionBackground.setBackground(bgDrawable); 126 Animator bgAnim = ObjectAnimator.ofInt(bgDrawable, "alpha", 0, 255); 127 bgAnim.setDuration(CLOSE_DURATION); 128 bgAnim.setInterpolator(new android.view.animation.AccelerateInterpolator(1f)); 129 builder.with(bgAnim); 130 } 131 } 132 mContentAnim.addListener(this); 133 if (mListener != null) { 134 mContentAnim.addListener(mListener); 135 } 136 } 137 138 void startAnimation(boolean appearing) { 139 if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")"); 140 141 createAnimation(appearing); 142 143 // isHardwareAccelerated() checks if we're attached to a window and if that 144 // window is HW accelerated-- we were sometimes not attached to a window 145 // and buildLayer was throwing an IllegalStateException 146 if (mContentView.isHardwareAccelerated()) { 147 mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 148 mContentView.buildLayer(); 149 } 150 mContentAnim.start(); 151 152 mVisible = appearing; 153 } 154 155 void jumpTo(boolean appearing) { 156 mContentView.setTranslationY(appearing ? 0 : mPanelHeight); 157 if (mScrimView.getBackground() != null) { 158 mScrimView.getBackground().setAlpha(appearing ? 255 : 0); 159 } 160 View recentsTransitionBackground = 161 mRootView.findViewById(R.id.recents_transition_background); 162 recentsTransitionBackground.setVisibility(View.INVISIBLE); 163 mRootView.requestLayout(); 164 } 165 166 public void setPanelHeight(int h) { 167 if (DEBUG) Slog.d(TAG, "panelHeight=" + h); 168 mPanelHeight = h; 169 } 170 171 public void onAnimationCancel(Animator animation) { 172 if (DEBUG) Slog.d(TAG, "onAnimationCancel"); 173 // force this to zero so we close the window 174 mVisible = false; 175 } 176 177 public void onAnimationEnd(Animator animation) { 178 if (DEBUG) Slog.d(TAG, "onAnimationEnd"); 179 if (!mVisible) { 180 mRootView.hideWindow(); 181 } 182 mContentView.setLayerType(View.LAYER_TYPE_NONE, null); 183 mContentView.setAlpha(1f); 184 mContentAnim = null; 185 } 186 187 public void onAnimationRepeat(Animator animation) { 188 } 189 190 public void onAnimationStart(Animator animation) { 191 } 192 } 193