Home | History | Annotate | Download | only in views
      1 /*
      2  * Copyright (C) 2014 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.recents.views;
     18 
     19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
     20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
     21 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
     22 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     23 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
     24 
     25 import android.content.Context;
     26 import android.graphics.Bitmap;
     27 import android.graphics.GraphicBuffer;
     28 import android.graphics.Rect;
     29 import android.util.Log;
     30 
     31 import com.android.systemui.recents.Recents;
     32 import com.android.systemui.recents.RecentsDebugFlags;
     33 import com.android.systemui.shared.recents.model.Task;
     34 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
     35 import com.android.systemui.shared.recents.view.RecentsTransition;
     36 
     37 import java.util.ArrayList;
     38 import java.util.Collections;
     39 import java.util.List;
     40 
     41 /**
     42  * A helper class to create the transition app animation specs to/from Recents
     43  */
     44 public class RecentsTransitionComposer {
     45 
     46     private static final String TAG = "RecentsTransitionComposer";
     47 
     48     private Context mContext;
     49     private TaskViewTransform mTmpTransform = new TaskViewTransform();
     50 
     51     public RecentsTransitionComposer(Context context) {
     52         mContext = context;
     53     }
     54 
     55     /**
     56      * Composes a single animation spec for the given {@link TaskView}
     57      */
     58     private static AppTransitionAnimationSpecCompat composeAnimationSpec(TaskStackView stackView,
     59             TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
     60         Bitmap b = null;
     61         if (addHeaderBitmap) {
     62             b = composeHeaderBitmap(taskView, transform);
     63             if (b == null) {
     64                 return null;
     65             }
     66         }
     67 
     68         Rect taskRect = new Rect();
     69         transform.rect.round(taskRect);
     70         // Disable in for low ram devices because each task does in Recents does not have fullscreen
     71         // height (stackView height) and when transitioning to fullscreen app, the code below would
     72         // force the task thumbnail to full stackView height immediately causing the transition
     73         // jarring.
     74         if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
     75                 stackView.getStack().getFrontMostTask()) {
     76             taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
     77         }
     78         return new AppTransitionAnimationSpecCompat(taskView.getTask().key.id, b, taskRect);
     79     }
     80 
     81     /**
     82      * Composes the transition spec when docking a task, which includes a full task bitmap.
     83      */
     84     public List<AppTransitionAnimationSpecCompat> composeDockAnimationSpec(TaskView taskView,
     85             Rect bounds) {
     86         mTmpTransform.fillIn(taskView);
     87         Task task = taskView.getTask();
     88         Bitmap buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
     89         return Collections.singletonList(new AppTransitionAnimationSpecCompat(task.key.id, buffer,
     90                 bounds));
     91     }
     92 
     93     /**
     94      * Composes the animation specs for all the tasks in the target stack.
     95      */
     96     public List<AppTransitionAnimationSpecCompat> composeAnimationSpecs(final Task task,
     97             final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
     98         // Calculate the offscreen task rect (for tasks that are not backed by views)
     99         TaskView taskView = stackView.getChildViewForTask(task);
    100         TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
    101         Rect offscreenTaskRect = new Rect();
    102         stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
    103 
    104         // If this is a full screen stack, the transition will be towards the single, full screen
    105         // task. We only need the transition spec for this task.
    106 
    107         // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
    108         // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
    109         if (windowingMode == WINDOWING_MODE_FULLSCREEN
    110                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
    111                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
    112                 || activityType == ACTIVITY_TYPE_ASSISTANT
    113                 || windowingMode == WINDOWING_MODE_UNDEFINED) {
    114             List<AppTransitionAnimationSpecCompat> specs = new ArrayList<>();
    115             if (taskView == null) {
    116                 specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
    117             } else {
    118                 mTmpTransform.fillIn(taskView);
    119                 stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
    120                 AppTransitionAnimationSpecCompat spec = composeAnimationSpec(stackView, taskView,
    121                         mTmpTransform, true /* addHeaderBitmap */);
    122                 if (spec != null) {
    123                     specs.add(spec);
    124                 }
    125             }
    126             return specs;
    127         }
    128         return Collections.emptyList();
    129     }
    130 
    131     /**
    132      * Composes a single animation spec for the given {@link Task}
    133      */
    134     private static AppTransitionAnimationSpecCompat composeOffscreenAnimationSpec(Task task,
    135             Rect taskRect) {
    136         return new AppTransitionAnimationSpecCompat(task.key.id, null, taskRect);
    137     }
    138 
    139     public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
    140         float scale = transform.scale;
    141         int fromWidth = (int) (transform.rect.width() * scale);
    142         int fromHeight = (int) (transform.rect.height() * scale);
    143         if (fromWidth == 0 || fromHeight == 0) {
    144             Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
    145                     " at transform: " + transform);
    146 
    147             return RecentsTransition.drawViewIntoHardwareBitmap(1, 1, null, 1f, 0x00ffffff);
    148         } else {
    149             if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
    150                 return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, null, 1f,
    151                         0xFFff0000);
    152             } else {
    153                 return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, taskView,
    154                         scale, 0);
    155             }
    156         }
    157     }
    158 
    159     private static Bitmap composeHeaderBitmap(TaskView taskView,
    160             TaskViewTransform transform) {
    161         float scale = transform.scale;
    162         int headerWidth = (int) (transform.rect.width());
    163         int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
    164         if (headerWidth == 0 || headerHeight == 0) {
    165             return null;
    166         }
    167 
    168         if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
    169             return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight, null, 1f,
    170                     0xFFff0000);
    171         } else {
    172             return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight,
    173                     taskView.mHeaderView, scale, 0);
    174         }
    175     }
    176 }
    177