• 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.content.Context;
19 import android.graphics.Rect;
20 import android.os.Handler;
21 import android.support.v7.widget.RecyclerView;
22 import android.util.AttributeSet;
23 import android.util.Log;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.WindowInsets;
27 import android.widget.FrameLayout;
28 
29 import com.android.systemui.R;
30 import com.android.systemui.recents.Recents;
31 import com.android.systemui.recents.RecentsActivity;
32 import com.android.systemui.recents.RecentsActivityLaunchState;
33 import com.android.systemui.recents.RecentsConfiguration;
34 import com.android.systemui.recents.events.EventBus;
35 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
36 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
37 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
38 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
39 import com.android.systemui.recents.misc.SystemServicesProxy;
40 import com.android.systemui.recents.model.Task;
41 import com.android.systemui.recents.model.TaskStack;
42 import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
43 import android.support.v7.widget.RecyclerView.OnScrollListener;
44 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
45 
46 /**
47  * Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView.
48  */
49 public class RecentsTvView extends FrameLayout {
50 
51     private static final String TAG = "RecentsTvView";
52     private static final boolean DEBUG = false;
53 
54     private TaskStack mStack;
55     private TaskStackHorizontalGridView mTaskStackHorizontalView;
56     private View mEmptyView;
57     private View mDismissPlaceholder;
58     private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
59     private boolean mAwaitingFirstLayout = true;
60     private Rect mSystemInsets = new Rect();
61     private RecentsTvTransitionHelper mTransitionHelper;
62     private final Handler mHandler = new Handler();
63     private OnScrollListener mScrollListener;
RecentsTvView(Context context)64     public RecentsTvView(Context context) {
65         this(context, null);
66     }
67 
RecentsTvView(Context context, AttributeSet attrs)68     public RecentsTvView(Context context, AttributeSet attrs) {
69         this(context, attrs, 0);
70     }
71 
RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr)72     public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr) {
73         this(context, attrs, defStyleAttr, 0);
74     }
75 
RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)76     public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
77         super(context, attrs, defStyleAttr, defStyleRes);
78 
79         setWillNotDraw(false);
80 
81         LayoutInflater inflater = LayoutInflater.from(context);
82         mEmptyView = inflater.inflate(R.layout.recents_tv_empty, this, false);
83         addView(mEmptyView);
84 
85         mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
86     }
87 
88     @Override
onFinishInflate()89     protected void onFinishInflate() {
90         super.onFinishInflate();
91         mDismissPlaceholder = findViewById(R.id.dismiss_placeholder);
92         mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
93     }
94 
95     /**
96      * Initialize the view.
97      */
init(TaskStack stack)98     public void init(TaskStack stack) {
99         RecentsConfiguration config = Recents.getConfiguration();
100         RecentsActivityLaunchState launchState = config.getLaunchState();
101         mStack = stack;
102 
103         mTaskStackHorizontalView.init(stack);
104 
105         if (stack.getStackTaskCount() > 0) {
106             hideEmptyView();
107         } else {
108             showEmptyView();
109         }
110 
111         // Layout with the new stack
112         requestLayout();
113     }
114 
launchFocusedTask()115     public boolean launchFocusedTask() {
116         if (mTaskStackHorizontalView != null) {
117             Task task = mTaskStackHorizontalView.getFocusedTask();
118             if (task != null) {
119                 launchTaskFomRecents(task, true);
120                 return true;
121             }
122         }
123         return false;
124     }
125 
126     /** Launches the task that recents was launched from if possible */
launchPreviousTask(boolean animate)127     public boolean launchPreviousTask(boolean animate) {
128         if (mTaskStackHorizontalView != null) {
129             TaskStack stack = mTaskStackHorizontalView.getStack();
130             Task task = stack.getLaunchTarget();
131             if (task != null) {
132                 launchTaskFomRecents(task, animate);
133                 return true;
134             }
135         }
136         return false;
137     }
138 
139     /**
140      * Launch the given task from recents with animation. If the task is not focused, this will
141      * attempt to scroll to focus the task before launching.
142      * @param task
143      */
launchTaskFomRecents(final Task task, boolean animate)144     private void launchTaskFomRecents(final Task task, boolean animate) {
145         if (!animate) {
146             SystemServicesProxy ssp = Recents.getSystemServices();
147             ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
148             return;
149         }
150         mTaskStackHorizontalView.requestFocus();
151         Task focusedTask = mTaskStackHorizontalView.getFocusedTask();
152         if (focusedTask != null && task != focusedTask) {
153             if (mScrollListener != null) {
154                 mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
155             }
156             mScrollListener = new OnScrollListener() {
157                 @Override
158                 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
159                     super.onScrollStateChanged(recyclerView, newState);
160                     if (newState == RecyclerView.SCROLL_STATE_IDLE) {
161                         TaskCardView cardView = mTaskStackHorizontalView.getChildViewForTask(task);
162                         if (cardView != null) {
163                             mTransitionHelper.launchTaskFromRecents(mStack, task,
164                                     mTaskStackHorizontalView, cardView, null, INVALID_STACK_ID);
165                         } else {
166                             // This should not happen normally. If this happens then the data in
167                             // the grid view was altered during the scroll. Log error and launch
168                             // task with no animation.
169                             Log.e(TAG, "Card view for task : " + task + ", returned null.");
170                             SystemServicesProxy ssp = Recents.getSystemServices();
171                             ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
172                         }
173                         mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
174                     }
175                 }
176             };
177             mTaskStackHorizontalView.addOnScrollListener(mScrollListener);
178             mTaskStackHorizontalView.setSelectedPositionSmooth(
179                     ((TaskStackHorizontalViewAdapter) mTaskStackHorizontalView.getAdapter())
180                             .getPositionOfTask(task));
181         } else {
182             mTransitionHelper.launchTaskFromRecents(mStack, task, mTaskStackHorizontalView,
183                     mTaskStackHorizontalView.getChildViewForTask(task), null,
184                     INVALID_STACK_ID);
185         }
186     }
187 
188     /**
189      * Hides the task stack and shows the empty view.
190      */
showEmptyView()191     public void showEmptyView() {
192         mEmptyView.setVisibility(View.VISIBLE);
193         mTaskStackHorizontalView.setVisibility(View.GONE);
194         if (Recents.getSystemServices().isTouchExplorationEnabled()) {
195             mDismissPlaceholder.setVisibility(View.GONE);
196         }
197     }
198 
199     /**
200      * Shows the task stack and hides the empty view.
201      */
hideEmptyView()202     public void hideEmptyView() {
203         mEmptyView.setVisibility(View.GONE);
204         mTaskStackHorizontalView.setVisibility(View.VISIBLE);
205         if (Recents.getSystemServices().isTouchExplorationEnabled()) {
206             mDismissPlaceholder.setVisibility(View.VISIBLE);
207         }
208     }
209 
210     /**
211      * Returns the last known system insets.
212      */
getSystemInsets()213     public Rect getSystemInsets() {
214         return mSystemInsets;
215     }
216 
217     @Override
onAttachedToWindow()218     protected void onAttachedToWindow() {
219         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
220         super.onAttachedToWindow();
221     }
222 
223     @Override
onDetachedFromWindow()224     protected void onDetachedFromWindow() {
225         super.onDetachedFromWindow();
226         EventBus.getDefault().unregister(this);
227     }
228 
229     @Override
onApplyWindowInsets(WindowInsets insets)230     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
231         mSystemInsets.set(insets.getSystemWindowInsets());
232         requestLayout();
233         return insets;
234     }
235 
236     /**** EventBus Events ****/
237 
onBusEvent(LaunchTvTaskEvent event)238     public final void onBusEvent(LaunchTvTaskEvent event) {
239         mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView,
240                 event.taskView, event.targetTaskBounds, event.targetTaskStack);
241     }
242 
onBusEvent(DismissRecentsToHomeAnimationStarted event)243     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
244         // If we are going home, cancel the previous task's window transition
245         EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
246     }
247 
onBusEvent(RecentsVisibilityChangedEvent event)248     public final void onBusEvent(RecentsVisibilityChangedEvent event) {
249         if (!event.visible) {
250             // Reset the view state
251             mAwaitingFirstLayout = true;
252         }
253     }
254 
setTaskStackViewAdapter( TaskStackHorizontalViewAdapter taskStackViewAdapter)255     public TaskStackHorizontalGridView setTaskStackViewAdapter(
256             TaskStackHorizontalViewAdapter taskStackViewAdapter) {
257         if (mTaskStackHorizontalView != null) {
258             mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
259             taskStackViewAdapter.setTaskStackHorizontalGridView(mTaskStackHorizontalView);
260         }
261         return mTaskStackHorizontalView;
262     }
263 
getGridView()264     public TaskStackHorizontalGridView getGridView() {
265         return mTaskStackHorizontalView;
266     }
267 }
268