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