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 package com.android.systemui.shared.recents.view;
17 
18 import android.app.ActivityOptions;
19 import android.app.ActivityOptions.OnAnimationStartedListener;
20 import android.content.Context;
21 import android.graphics.Bitmap;
22 import android.graphics.Canvas;
23 import android.graphics.GraphicBuffer;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.IRemoteCallback;
27 import android.os.RemoteException;
28 import android.view.DisplayListCanvas;
29 import android.view.RenderNode;
30 import android.view.ThreadedRenderer;
31 import android.view.View;
32 
33 import java.util.function.Consumer;
34 
35 /**
36  * A helper class to create transitions to/from an App to Recents.
37  */
38 public class RecentsTransition {
39 
40     /**
41      * Creates a new transition aspect scaled transition activity options.
42      */
createAspectScaleAnimation(Context context, Handler handler, boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture, final Runnable animationStartCallback)43     public static ActivityOptions createAspectScaleAnimation(Context context, Handler handler,
44             boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture,
45             final Runnable animationStartCallback) {
46         final OnAnimationStartedListener animStartedListener = new OnAnimationStartedListener() {
47             private boolean mHandled;
48 
49             @Override
50             public void onAnimationStarted() {
51                 // OnAnimationStartedListener can be called numerous times, so debounce here to
52                 // prevent multiple callbacks
53                 if (mHandled) {
54                     return;
55                 }
56                 mHandled = true;
57 
58                 if (animationStartCallback != null) {
59                     animationStartCallback.run();
60                 }
61             }
62         };
63         final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(
64                 context, handler,
65                 animationSpecsFuture != null ? animationSpecsFuture.getFuture() : null,
66                 animStartedListener, scaleUp);
67         return opts;
68     }
69 
70     /**
71      * Wraps a animation-start callback in a binder that can be called from window manager.
72      */
wrapStartedListener(final Handler handler, final Runnable animationStartCallback)73     public static IRemoteCallback wrapStartedListener(final Handler handler,
74             final Runnable animationStartCallback) {
75         if (animationStartCallback == null) {
76             return null;
77         }
78         return new IRemoteCallback.Stub() {
79             @Override
80             public void sendResult(Bundle data) throws RemoteException {
81                 handler.post(animationStartCallback);
82             }
83         };
84     }
85 
86     /**
87      * @return a {@link GraphicBuffer} with the {@param view} drawn into it. Result can be null if
88      *         we were unable to allocate a hardware bitmap.
89      */
90     public static Bitmap drawViewIntoHardwareBitmap(int width, int height, final View view,
91             final float scale, final int eraseColor) {
92         return createHardwareBitmap(width, height, new Consumer<Canvas>() {
93             @Override
94             public void accept(Canvas c) {
95                 c.scale(scale, scale);
96                 if (eraseColor != 0) {
97                     c.drawColor(eraseColor);
98                 }
99                 if (view != null) {
100                     view.draw(c);
101                 }
102             }
103         });
104     }
105 
106     /**
107      * @return a hardware {@link Bitmap} after being drawn with the {@param consumer}. Result can be
108      *         null if we were unable to allocate a hardware bitmap.
109      */
110     public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) {
111         RenderNode node = RenderNode.create("RecentsTransition", null);
112         node.setLeftTopRightBottom(0, 0, width, height);
113         node.setClipToBounds(false);
114         DisplayListCanvas c = node.start(width, height);
115         consumer.accept(c);
116         node.end(c);
117         return ThreadedRenderer.createHardwareBitmap(node, width, height);
118     }
119 }
120