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