Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2017 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.server.wm;
     18 
     19 import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
     20 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
     21 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
     22 import static com.android.server.wm.AnimationSpecProto.WINDOW;
     23 import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
     24 
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.os.SystemClock;
     28 import android.util.proto.ProtoOutputStream;
     29 import android.view.SurfaceControl;
     30 import android.view.SurfaceControl.Transaction;
     31 import android.view.animation.Animation;
     32 import android.view.animation.AnimationSet;
     33 import android.view.animation.Interpolator;
     34 import android.view.animation.Transformation;
     35 import android.view.animation.TranslateAnimation;
     36 
     37 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
     38 
     39 import java.io.PrintWriter;
     40 
     41 /**
     42  * Animation spec for regular window animations.
     43  */
     44 public class WindowAnimationSpec implements AnimationSpec {
     45 
     46     private Animation mAnimation;
     47     private final Point mPosition = new Point();
     48     private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
     49     private final boolean mCanSkipFirstFrame;
     50     private final boolean mIsAppAnimation;
     51     private final Rect mStackBounds = new Rect();
     52     private int mStackClipMode;
     53     private final Rect mTmpRect = new Rect();
     54 
     55     public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame)  {
     56         this(animation, position, null /* stackBounds */, canSkipFirstFrame, STACK_CLIP_NONE,
     57                 false /* isAppAnimation */);
     58     }
     59 
     60     public WindowAnimationSpec(Animation animation, Point position, Rect stackBounds,
     61             boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation) {
     62         mAnimation = animation;
     63         if (position != null) {
     64             mPosition.set(position.x, position.y);
     65         }
     66         mCanSkipFirstFrame = canSkipFirstFrame;
     67         mIsAppAnimation = isAppAnimation;
     68         mStackClipMode = stackClipMode;
     69         if (stackBounds != null) {
     70             mStackBounds.set(stackBounds);
     71         }
     72     }
     73 
     74     @Override
     75     public boolean getDetachWallpaper() {
     76         return mAnimation.getDetachWallpaper();
     77     }
     78 
     79     @Override
     80     public boolean getShowWallpaper() {
     81         return mAnimation.getShowWallpaper();
     82     }
     83 
     84     @Override
     85     public int getBackgroundColor() {
     86         return mAnimation.getBackgroundColor();
     87     }
     88 
     89     @Override
     90     public long getDuration() {
     91         return mAnimation.computeDurationHint();
     92     }
     93 
     94     @Override
     95     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
     96         final TmpValues tmp = mThreadLocalTmps.get();
     97         tmp.transformation.clear();
     98         mAnimation.getTransformation(currentPlayTime, tmp.transformation);
     99         tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
    100         t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
    101         t.setAlpha(leash, tmp.transformation.getAlpha());
    102         if (mStackClipMode == STACK_CLIP_NONE) {
    103             t.setWindowCrop(leash, tmp.transformation.getClipRect());
    104         } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
    105             mTmpRect.set(mStackBounds);
    106             // Offset stack bounds to stack position so the final crop is in screen space.
    107             mTmpRect.offsetTo(mPosition.x, mPosition.y);
    108             t.setFinalCrop(leash, mTmpRect);
    109             t.setWindowCrop(leash, tmp.transformation.getClipRect());
    110         } else {
    111             mTmpRect.set(mStackBounds);
    112             mTmpRect.intersect(tmp.transformation.getClipRect());
    113             t.setWindowCrop(leash, mTmpRect);
    114         }
    115     }
    116 
    117     @Override
    118     public long calculateStatusBarTransitionStartTime() {
    119         TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
    120         if (openTranslateAnimation != null) {
    121 
    122             // Some interpolators are extremely quickly mostly finished, but not completely. For
    123             // our purposes, we need to find the fraction for which ther interpolator is mostly
    124             // there, and use that value for the calculation.
    125             float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
    126             return SystemClock.uptimeMillis()
    127                     + openTranslateAnimation.getStartOffset()
    128                     + (long)(openTranslateAnimation.getDuration() * t)
    129                     - STATUS_BAR_TRANSITION_DURATION;
    130         } else {
    131             return SystemClock.uptimeMillis();
    132         }
    133     }
    134 
    135     @Override
    136     public boolean canSkipFirstFrame() {
    137         return mCanSkipFirstFrame;
    138     }
    139 
    140     @Override
    141     public boolean needsEarlyWakeup() {
    142         return mIsAppAnimation;
    143     }
    144 
    145     @Override
    146     public void dump(PrintWriter pw, String prefix) {
    147         pw.print(prefix); pw.println(mAnimation);
    148     }
    149 
    150     @Override
    151     public void writeToProtoInner(ProtoOutputStream proto) {
    152         final long token = proto.start(WINDOW);
    153         proto.write(ANIMATION, mAnimation.toString());
    154         proto.end(token);
    155     }
    156 
    157     /**
    158      * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
    159      *
    160      * @return the found animation, {@code null} otherwise
    161      */
    162     private static TranslateAnimation findTranslateAnimation(Animation animation) {
    163         if (animation instanceof TranslateAnimation) {
    164             return (TranslateAnimation) animation;
    165         } else if (animation instanceof AnimationSet) {
    166             AnimationSet set = (AnimationSet) animation;
    167             for (int i = 0; i < set.getAnimations().size(); i++) {
    168                 Animation a = set.getAnimations().get(i);
    169                 if (a instanceof TranslateAnimation) {
    170                     return (TranslateAnimation) a;
    171                 }
    172             }
    173         }
    174         return null;
    175     }
    176 
    177     /**
    178      * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
    179      * {@code interpolator(t + eps) > 0.99}.
    180      */
    181     private static float findAlmostThereFraction(Interpolator interpolator) {
    182         float val = 0.5f;
    183         float adj = 0.25f;
    184         while (adj >= 0.01f) {
    185             if (interpolator.getInterpolation(val) < 0.99f) {
    186                 val += adj;
    187             } else {
    188                 val -= adj;
    189             }
    190             adj /= 2;
    191         }
    192         return val;
    193     }
    194 
    195     private static class TmpValues {
    196         final Transformation transformation = new Transformation();
    197         final float[] floats = new float[9];
    198     }
    199 }
    200