• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.recents.tv.views;
17 
18 import android.annotation.Nullable;
19 import android.app.ActivityOptions;
20 import android.content.Context;
21 import android.graphics.Bitmap;
22 import android.graphics.Rect;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.IRemoteCallback;
26 import android.os.RemoteException;
27 import android.util.Log;
28 import android.view.WindowManagerGlobal;
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.systemui.recents.Recents;
31 import com.android.systemui.recents.events.EventBus;
32 import com.android.systemui.recents.events.activity.*;
33 import com.android.systemui.recents.misc.SystemServicesProxy;
34 import com.android.systemui.recents.model.Task;
35 import com.android.systemui.recents.model.TaskStack;
36 
37 
38 public class RecentsTvTransitionHelper {
39     private static final String TAG = "RecentsTvTransitionHelper";
40 
41     private Context mContext;
42     private Handler mHandler;
43 
RecentsTvTransitionHelper(Context context, Handler handler)44     public RecentsTvTransitionHelper(Context context, Handler handler) {
45         mContext = context;
46         mHandler = handler;
47     }
48 
launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, final TaskStackHorizontalGridView stackView, final TaskCardView taskView, final Rect bounds, int destinationStack)49     public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
50             final TaskStackHorizontalGridView stackView, final TaskCardView taskView,
51             final Rect bounds, int destinationStack) {
52         final ActivityOptions opts = ActivityOptions.makeBasic();
53         if (bounds != null) {
54             opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
55         }
56 
57         final ActivityOptions.OnAnimationStartedListener animStartedListener;
58         if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
59                 task.thumbnail.getHeight() > 0) {
60             animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
61                 @Override
62                 public void onAnimationStarted() {
63                     // If we are launching into another task, cancel the previous task's
64                     // window transition
65                     EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
66                     EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
67                 }
68             };
69         } else {
70             // This is only the case if the task is not on screen (scrolled offscreen for example)
71             animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
72                 @Override
73                 public void onAnimationStarted() {
74                     EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
75                 }
76             };
77         }
78 
79         if (taskView == null) {
80             // If there is no task view, then we do not need to worry about animating out occluding
81             // task views, and we can launch immediately
82             startTaskActivity(stack, task, taskView, opts, animStartedListener);
83         } else {
84             LaunchTvTaskStartedEvent launchStartedEvent = new LaunchTvTaskStartedEvent(taskView);
85             EventBus.getDefault().send(launchStartedEvent);
86             startTaskActivity(stack, task, taskView, opts, animStartedListener);
87         }
88     }
89 
startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView, ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener)90     private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView,
91             ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) {
92         SystemServicesProxy ssp = Recents.getSystemServices();
93         if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) {
94             // Keep track of the index of the task launch
95             int taskIndexFromFront = 0;
96             int taskIndex = stack.indexOfStackTask(task);
97             if (taskIndex > -1) {
98                 taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
99             }
100             EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
101         } else {
102             // Keep track of failed launches
103             EventBus.getDefault().send(new LaunchTaskFailedEvent());
104         }
105 
106         Rect taskRect = taskView.getFocusedThumbnailRect();
107         // Check both the rect and the thumbnail for null. The rect can be null if the user
108         // decides to disallow animations, so automatic scrolling does not happen properly.
109 
110         // The thumbnail can be null if the app was partially launched on TV. In this case
111         // we do not override the transition.
112         if (taskRect == null || task.thumbnail == null) {
113             return;
114         }
115 
116         IRemoteCallback.Stub callback = null;
117         if (animStartedListener != null) {
118             callback = new IRemoteCallback.Stub() {
119                 @Override
120                 public void sendResult(Bundle data) throws RemoteException {
121                     mHandler.post(new Runnable() {
122                         @Override
123                         public void run() {
124                             if (animStartedListener != null) {
125                                 animStartedListener.onAnimationStarted();
126                             }
127                         }
128                     });
129                 }
130             };
131         }
132         try {
133             Bitmap thumbnail = Bitmap.createScaledBitmap(task.thumbnail, taskRect.width(),
134                     taskRect.height(), false);
135             WindowManagerGlobal.getWindowManagerService()
136                     .overridePendingAppTransitionAspectScaledThumb(thumbnail, taskRect.left,
137                             taskRect.top, taskRect.width(), taskRect.height(), callback, true);
138         } catch (RemoteException e) {
139             Log.w(TAG, "Failed to override transition: " + e);
140         }
141     }
142 }
143