| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.systemui.recents.tv.views; |
| |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.os.Handler; |
| import android.support.v7.widget.RecyclerView; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.WindowInsets; |
| import android.widget.FrameLayout; |
| |
| import com.android.systemui.R; |
| import com.android.systemui.recents.Recents; |
| import com.android.systemui.recents.RecentsActivity; |
| import com.android.systemui.recents.RecentsActivityLaunchState; |
| import com.android.systemui.recents.RecentsConfiguration; |
| import com.android.systemui.recents.events.EventBus; |
| import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; |
| import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; |
| import com.android.systemui.recents.events.activity.LaunchTvTaskEvent; |
| import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; |
| import com.android.systemui.recents.misc.SystemServicesProxy; |
| import com.android.systemui.recents.model.Task; |
| import com.android.systemui.recents.model.TaskStack; |
| import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder; |
| import android.support.v7.widget.RecyclerView.OnScrollListener; |
| import static android.app.ActivityManager.StackId.INVALID_STACK_ID; |
| |
| /** |
| * Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView. |
| */ |
| public class RecentsTvView extends FrameLayout { |
| |
| private static final String TAG = "RecentsTvView"; |
| private static final boolean DEBUG = false; |
| |
| private TaskStack mStack; |
| private TaskStackHorizontalGridView mTaskStackHorizontalView; |
| private View mEmptyView; |
| private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder; |
| private boolean mAwaitingFirstLayout = true; |
| private Rect mSystemInsets = new Rect(); |
| private RecentsTvTransitionHelper mTransitionHelper; |
| private Handler mHandler; |
| private OnScrollListener mScrollListener; |
| public RecentsTvView(Context context) { |
| this(context, null); |
| } |
| |
| public RecentsTvView(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr) { |
| this(context, attrs, defStyleAttr, 0); |
| } |
| |
| public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
| super(context, attrs, defStyleAttr, defStyleRes); |
| |
| setWillNotDraw(false); |
| |
| LayoutInflater inflater = LayoutInflater.from(context); |
| mEmptyView = inflater.inflate(R.layout.recents_tv_empty, this, false); |
| addView(mEmptyView); |
| mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null); |
| |
| mHandler = new Handler(); |
| mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler); |
| } |
| |
| public void setTaskStack(TaskStack stack) { |
| RecentsConfiguration config = Recents.getConfiguration(); |
| RecentsActivityLaunchState launchState = config.getLaunchState(); |
| mStack = stack; |
| |
| if (mTaskStackHorizontalView != null) { |
| mTaskStackHorizontalView.reset(); |
| mTaskStackHorizontalView.setStack(stack); |
| } else { |
| mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list); |
| mTaskStackHorizontalView.setStack(stack); |
| } |
| |
| if (stack.getStackTaskCount() > 0) { |
| hideEmptyView(); |
| } else { |
| showEmptyView(); |
| } |
| |
| requestLayout(); |
| } |
| |
| public boolean launchFocusedTask() { |
| if (mTaskStackHorizontalView != null) { |
| Task task = mTaskStackHorizontalView.getFocusedTask(); |
| if (task != null) { |
| launchTaskFomRecents(task, true); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** Launches the task that recents was launched from if possible */ |
| public boolean launchPreviousTask(boolean animate) { |
| if (mTaskStackHorizontalView != null) { |
| TaskStack stack = mTaskStackHorizontalView.getStack(); |
| Task task = stack.getLaunchTarget(); |
| if (task != null) { |
| launchTaskFomRecents(task, animate); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Launch the given task from recents with animation. If the task is not focused, this will |
| * attempt to scroll to focus the task before launching. |
| * @param task |
| */ |
| private void launchTaskFomRecents(final Task task, boolean animate) { |
| if (!animate) { |
| SystemServicesProxy ssp = Recents.getSystemServices(); |
| ssp.startActivityFromRecents(getContext(), task.key, task.title, null); |
| return; |
| } |
| mTaskStackHorizontalView.requestFocus(); |
| Task focusedTask = mTaskStackHorizontalView.getFocusedTask(); |
| if (focusedTask != null && task != focusedTask) { |
| if (mScrollListener != null) { |
| mTaskStackHorizontalView.removeOnScrollListener(mScrollListener); |
| } |
| mScrollListener = new OnScrollListener() { |
| @Override |
| public void onScrollStateChanged(RecyclerView recyclerView, int newState) { |
| super.onScrollStateChanged(recyclerView, newState); |
| if (newState == RecyclerView.SCROLL_STATE_IDLE) { |
| TaskCardView cardView = mTaskStackHorizontalView.getChildViewForTask(task); |
| if (cardView != null) { |
| mTransitionHelper.launchTaskFromRecents(mStack, task, |
| mTaskStackHorizontalView, cardView, null, INVALID_STACK_ID); |
| } else { |
| // This should not happen normally. If this happens then the data in |
| // the grid view was altered during the scroll. Log error and launch |
| // task with no animation. |
| Log.e(TAG, "Card view for task : " + task + ", returned null."); |
| SystemServicesProxy ssp = Recents.getSystemServices(); |
| ssp.startActivityFromRecents(getContext(), task.key, task.title, null); |
| } |
| mTaskStackHorizontalView.removeOnScrollListener(mScrollListener); |
| } |
| } |
| }; |
| mTaskStackHorizontalView.addOnScrollListener(mScrollListener); |
| mTaskStackHorizontalView.setSelectedPositionSmooth( |
| ((TaskStackHorizontalViewAdapter) mTaskStackHorizontalView.getAdapter()) |
| .getPositionOfTask(task)); |
| } else { |
| mTransitionHelper.launchTaskFromRecents(mStack, task, mTaskStackHorizontalView, |
| mTaskStackHorizontalView.getChildViewForTask(task), null, |
| INVALID_STACK_ID); |
| } |
| } |
| |
| /** |
| * Starts the focus change animation. |
| */ |
| public void startRecentsRowFocusAnimation(boolean hasFocus) { |
| if (mEmptyView.getVisibility() == View.VISIBLE) { |
| mEmptyViewFocusAnimationHolder.getFocusChangeAnimator(hasFocus).start(); |
| } else { |
| mTaskStackHorizontalView.startRecentsRowFocusAnimation(hasFocus); |
| } |
| } |
| |
| /** |
| * Hides the task stack and shows the empty view. |
| */ |
| public void showEmptyView() { |
| mEmptyView.setVisibility(View.VISIBLE); |
| mTaskStackHorizontalView.setVisibility(View.GONE); |
| } |
| |
| /** |
| * Shows the task stack and hides the empty view. |
| */ |
| public void hideEmptyView() { |
| mEmptyView.setVisibility(View.GONE); |
| mTaskStackHorizontalView.setVisibility(View.VISIBLE); |
| } |
| |
| /** |
| * Returns the last known system insets. |
| */ |
| public Rect getSystemInsets() { |
| return mSystemInsets; |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); |
| super.onAttachedToWindow(); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| EventBus.getDefault().unregister(this); |
| } |
| |
| @Override |
| public WindowInsets onApplyWindowInsets(WindowInsets insets) { |
| mSystemInsets.set(insets.getSystemWindowInsets()); |
| requestLayout(); |
| return insets; |
| } |
| |
| /**** EventBus Events ****/ |
| |
| public final void onBusEvent(LaunchTvTaskEvent event) { |
| mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView, |
| event.taskView, event.targetTaskBounds, event.targetTaskStack); |
| } |
| |
| public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { |
| // If we are going home, cancel the previous task's window transition |
| EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null)); |
| } |
| |
| public final void onBusEvent(RecentsVisibilityChangedEvent event) { |
| if (!event.visible) { |
| // Reset the view state |
| mAwaitingFirstLayout = true; |
| } |
| } |
| |
| public TaskStackHorizontalGridView setTaskStackViewAdapter( |
| TaskStackHorizontalViewAdapter taskStackViewAdapter) { |
| if (mTaskStackHorizontalView != null) { |
| mTaskStackHorizontalView.setAdapter(taskStackViewAdapter); |
| taskStackViewAdapter.setTaskStackHorizontalGridView(mTaskStackHorizontalView); |
| } |
| return mTaskStackHorizontalView; |
| } |
| |
| public TaskStackHorizontalGridView getGridView() { |
| return mTaskStackHorizontalView; |
| } |
| } |