StatusBarController.java revision f2efdd8c7ca65745245a0ac26c681a8d376adb87
1/* 2 * Copyright (C) 2015 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 17package com.android.server.policy; 18 19import android.app.StatusBarManager; 20import android.os.IBinder; 21import android.os.SystemClock; 22import android.view.View; 23import android.view.WindowManager; 24import android.view.animation.Animation; 25import android.view.animation.AnimationSet; 26import android.view.animation.Interpolator; 27import android.view.animation.TranslateAnimation; 28 29import com.android.server.LocalServices; 30import com.android.server.statusbar.StatusBarManagerInternal; 31 32import static android.view.WindowManagerInternal.*; 33 34/** 35 * Implements status bar specific behavior. 36 */ 37public class StatusBarController extends BarController { 38 39 private static final long TRANSITION_DURATION = 120L; 40 41 private final AppTransitionListener mAppTransitionListener 42 = new AppTransitionListener() { 43 44 @Override 45 public void onAppTransitionPendingLocked() { 46 mHandler.post(new Runnable() { 47 @Override 48 public void run() { 49 StatusBarManagerInternal statusbar = getStatusBarInternal(); 50 if (statusbar != null) { 51 statusbar.appTransitionPending(); 52 } 53 } 54 }); 55 } 56 57 @Override 58 public void onAppTransitionStartingLocked(IBinder openToken, IBinder closeToken, 59 final Animation openAnimation, final Animation closeAnimation) { 60 mHandler.post(new Runnable() { 61 @Override 62 public void run() { 63 StatusBarManagerInternal statusbar = getStatusBarInternal(); 64 if (statusbar != null) { 65 long startTime = calculateStatusBarTransitionStartTime(openAnimation, 66 closeAnimation); 67 long duration = closeAnimation != null || openAnimation != null 68 ? TRANSITION_DURATION : 0; 69 statusbar.appTransitionStarting(startTime, duration); 70 } 71 } 72 }); 73 } 74 75 @Override 76 public void onAppTransitionCancelledLocked() { 77 mHandler.post(new Runnable() { 78 @Override 79 public void run() { 80 StatusBarManagerInternal statusbar = getStatusBarInternal(); 81 if (statusbar != null) { 82 statusbar.appTransitionCancelled(); 83 } 84 } 85 }); 86 } 87 88 @Override 89 public void onAppTransitionFinishedLocked(IBinder token) { 90 mHandler.post(new Runnable() { 91 @Override 92 public void run() { 93 StatusBarManagerInternal statusbar = LocalServices.getService( 94 StatusBarManagerInternal.class); 95 if (statusbar != null) { 96 statusbar.appTransitionFinished(); 97 } 98 } 99 }); 100 } 101 }; 102 103 public StatusBarController() { 104 super("StatusBar", 105 View.STATUS_BAR_TRANSIENT, 106 View.STATUS_BAR_UNHIDE, 107 View.STATUS_BAR_TRANSLUCENT, 108 StatusBarManager.WINDOW_STATUS_BAR, 109 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, 110 View.STATUS_BAR_TRANSPARENT); 111 } 112 113 public AppTransitionListener getAppTransitionListener() { 114 return mAppTransitionListener; 115 } 116 117 /** 118 * For a given app transition with {@code openAnimation} and {@code closeAnimation}, this 119 * calculates the timings for the corresponding status bar transition. 120 * 121 * @return the desired start time of the status bar transition, in uptime millis 122 */ 123 private static long calculateStatusBarTransitionStartTime(Animation openAnimation, 124 Animation closeAnimation) { 125 if (openAnimation != null && closeAnimation != null) { 126 TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation); 127 TranslateAnimation closeTranslateAnimation = findTranslateAnimation(closeAnimation); 128 if (openTranslateAnimation != null) { 129 130 // Some interpolators are extremely quickly mostly finished, but not completely. For 131 // our purposes, we need to find the fraction for which ther interpolator is mostly 132 // there, and use that value for the calculation. 133 float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator()); 134 return SystemClock.uptimeMillis() 135 + openTranslateAnimation.getStartOffset() 136 + (long)(openTranslateAnimation.getDuration()*t) - TRANSITION_DURATION; 137 } else if (closeTranslateAnimation != null) { 138 return SystemClock.uptimeMillis(); 139 } else { 140 return SystemClock.uptimeMillis(); 141 } 142 } else { 143 return SystemClock.uptimeMillis(); 144 } 145 } 146 147 /** 148 * Tries to find a {@link TranslateAnimation} inside the {@code animation}. 149 * 150 * @return the found animation, {@code null} otherwise 151 */ 152 private static TranslateAnimation findTranslateAnimation(Animation animation) { 153 if (animation instanceof TranslateAnimation) { 154 return (TranslateAnimation) animation; 155 } else if (animation instanceof AnimationSet) { 156 AnimationSet set = (AnimationSet) animation; 157 for (int i = 0; i < set.getAnimations().size(); i++) { 158 Animation a = set.getAnimations().get(i); 159 if (a instanceof TranslateAnimation) { 160 return (TranslateAnimation) a; 161 } 162 } 163 } 164 return null; 165 } 166 167 /** 168 * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which 169 * {@code interpolator(t + eps) > 0.99}. 170 */ 171 private static float findAlmostThereFraction(Interpolator interpolator) { 172 float val = 0.5f; 173 float adj = 0.25f; 174 while (adj >= 0.01f) { 175 if (interpolator.getInterpolation(val) < 0.99f) { 176 val += adj; 177 } else { 178 val -= adj; 179 } 180 adj /= 2; 181 } 182 return val; 183 } 184} 185