Merge "PIP: Apply the latest Recents row animation when PIP control is focused" into nyc-dev

This commit is contained in:
Jaewan Kim
2016-04-06 06:35:40 +00:00
committed by Android (Google) Code Review
12 changed files with 243 additions and 108 deletions

View File

@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
<item android:drawable="@drawable/ic_close_white" />
<item android:drawable="@drawable/ic_close_white"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item>
<layer-list>
<item android:drawable="@drawable/ic_close_white"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item android:drawable="@drawable/ic_close_white" />
</selector>

View File

@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
<item android:drawable="@drawable/ic_fullscreen_white_24dp" />
<item android:drawable="@drawable/ic_fullscreen_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item>
<layer-list>
<item android:drawable="@drawable/ic_fullscreen_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item android:drawable="@drawable/ic_fullscreen_white_24dp" />
</selector>

View File

@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
<item android:drawable="@drawable/ic_pause_white_24dp" />
<item android:drawable="@drawable/ic_pause_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item>
<layer-list>
<item android:drawable="@drawable/ic_pause_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item android:drawable="@drawable/ic_pause_white_24dp" />
</selector>

View File

@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
<item android:drawable="@drawable/ic_play_arrow_white_24dp" />
<item android:drawable="@drawable/ic_play_arrow_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item>
<layer-list>
<item android:drawable="@drawable/ic_play_arrow_white_24dp"
android:top="@dimen/tv_pip_button_icon_padding"
android:bottom="@dimen/tv_pip_button_icon_padding"
android:left="@dimen/tv_pip_button_icon_padding"
android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
<item android:drawable="@drawable/ic_play_arrow_white_24dp" />
</selector>

View File

@@ -54,5 +54,7 @@
<!-- Extra space around the PIP and its outline in PIP onboarding activity -->
<dimen name="tv_pip_bounds_space">3dp</dimen>
<!-- Extra space around the PIP control button icon to match with the focused circle -->
<dimen name="tv_pip_button_icon_padding">5dp</dimen>
</resources>

View File

@@ -53,7 +53,6 @@ import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.tv.animations.FocusAnimationHolder;
import com.android.systemui.recents.tv.views.RecentsTvView;
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -79,7 +78,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
private boolean mIgnoreAltTabRelease;
private RecentsTvView mRecentsView;
private FocusAnimationHolder mRecentsFocusAnimationHolder;
private View mPipView;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
@@ -133,13 +131,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
mRecentsFocusAnimationHolder.startFocusLoseAnimation();
mPipRecentsOverlayManager.requestFocus(
mTaskStackViewAdapter.getItemCount() > 0);
} else {
mRecentsFocusAnimationHolder.startFocusGainAnimation();
}
handlePipViewFocusChange(hasFocus);
}
};
@@ -288,11 +280,10 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
mPipView = findViewById(R.id.pip);
// Place mPipView at the PIP bounds for fine tuned focus handling.
Rect pipBounds = mPipManager.getPipBounds();
Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
lp.width = pipBounds.width();
lp.height = pipBounds.height();
@@ -513,11 +504,30 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
if (mPipManager.isPipShown()) {
mPipView.setVisibility(View.VISIBLE);
mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
mPipView.requestFocus();
if (mPipView.hasFocus()) {
// This can happen only if the activity is resumed. Ask for reset.
handlePipViewFocusChange(true);
} else {
mPipView.requestFocus();
}
} else {
mPipView.setVisibility(View.GONE);
mPipRecentsOverlayManager.removePipRecentsOverlayView();
mRecentsFocusAnimationHolder.reset();
}
}
/**
* Handles the PIP view's focus change.
* This starts the relevant recents row animation
* and give focus to the recents overlay if needed.
*/
private void handlePipViewFocusChange(boolean hasFocus) {
mRecentsView.startRecentsRowFocusAnimation(!hasFocus);
if (hasFocus) {
// When PIP view has focus, recents overlay view will takes the focus
// as if it's the part of the Recents UI.
mPipRecentsOverlayManager.requestFocus(
mTaskStackViewAdapter.getItemCount() > 0);
}
}
}

View File

@@ -1,63 +0,0 @@
/*
* 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.animations;
import android.content.res.Resources;
import android.view.View;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.tv.views.TaskCardView;
/**
* Collections of Recents row's animation depending on the PIP's focus.
*/
public class FocusAnimationHolder {
private final float DIM_ALPHA = 0.5f;
private View mRecentsRowView;
private int mCardYDelta;
private long mDuration;
public FocusAnimationHolder(View recentsRowView) {
mRecentsRowView = recentsRowView;
Resources res = recentsRowView.getResources();
mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
mDuration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
}
public void startFocusGainAnimation() {
mRecentsRowView.animate()
.setDuration(mDuration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
.translationY(0);
}
public void startFocusLoseAnimation() {
mRecentsRowView.animate()
.setDuration(mDuration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(DIM_ALPHA)
.translationY(mCardYDelta);
}
public void reset() {
mRecentsRowView.setTransitionAlpha(1f);
mRecentsRowView.setTranslationY(0);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.animations;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.res.Resources;
import android.view.View;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.tv.views.TaskCardView;
/**
* Recents row's focus animation with PIP controls.
*/
public class RecentsRowFocusAnimationHolder {
private static final float DIM_ALPHA = 0.5f;
private View mView;
private View mTitleView;
private AnimatorSet mFocusGainAnimatorSet;
private AnimatorSet mFocusLoseAnimatorSet;
public RecentsRowFocusAnimationHolder(View view, View titleView) {
mView = view;
mTitleView = titleView;
Resources res = view.getResources();
int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
mFocusGainAnimatorSet = new AnimatorSet();
mFocusGainAnimatorSet.playTogether(
ObjectAnimator.ofFloat(mView, "alpha", 1f),
ObjectAnimator.ofFloat(mTitleView, "alpha", 1f));
mFocusGainAnimatorSet.setDuration(duration);
mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mFocusLoseAnimatorSet = new AnimatorSet();
mFocusLoseAnimatorSet.playTogether(
ObjectAnimator.ofFloat(mView, "alpha", DIM_ALPHA),
ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
mFocusLoseAnimatorSet.setDuration(duration);
mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
}
/**
* Returns the Recents row's focus change animation.
*/
public Animator getFocusChangeAnimator(boolean hasFocus) {
return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet;
}
/**
* Resets the views to the initial state immediately.
*/
public void reset() {
mView.setAlpha(1f);
mTitleView.setAlpha(1f);
}
}

View File

@@ -37,6 +37,7 @@ import com.android.systemui.recents.events.component.RecentsVisibilityChangedEve
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 java.util.ArrayList;
import java.util.List;
@@ -52,6 +53,7 @@ public class RecentsTvView extends FrameLayout {
private TaskStack mStack;
private TaskStackHorizontalGridView mTaskStackHorizontalView;
private View mEmptyView;
private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
private RecentsTvTransitionHelper mTransitionHelper;
@@ -77,6 +79,8 @@ public class RecentsTvView extends FrameLayout {
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null);
mHandler = new Handler();
mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
@@ -94,7 +98,6 @@ public class RecentsTvView extends FrameLayout {
mTaskStackHorizontalView.setStack(stack);
}
if (stack.getStackTaskCount() > 0) {
hideEmptyView();
} else {
@@ -134,33 +137,40 @@ public class RecentsTvView extends FrameLayout {
public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
if (mTaskStackHorizontalView != null) {
// Iterate the stack views and try and find the given task.
List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
int taskViewCount = taskViews.size();
for (int j = 0; j < taskViewCount; j++) {
TaskCardView tv = taskViews.get(j);
if (tv.getTask() == task) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
return true;
}
if (mTaskStackHorizontalView.getChildViewForTask(task) != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
return true;
}
}
return false;
}
/**
* 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);
mEmptyView.bringToFront();
mTaskStackHorizontalView.setVisibility(View.GONE);
}
/**
* Shows the task stack and hides the empty view.
*/
public void hideEmptyView() {
mEmptyView.setVisibility(View.INVISIBLE);
mEmptyView.setVisibility(View.GONE);
mTaskStackHorizontalView.setVisibility(View.VISIBLE);
}
/**

View File

@@ -24,6 +24,7 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -31,6 +32,7 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
import com.android.systemui.recents.model.Task;
@@ -44,6 +46,7 @@ public class TaskCardView extends LinearLayout {
private ViewFocusAnimator mViewFocusAnimator;
private DismissAnimationsHolder mDismissAnimationsHolder;
private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
public TaskCardView(Context context) {
this(context, null);
@@ -65,6 +68,8 @@ public class TaskCardView extends LinearLayout {
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
mDismissAnimationsHolder = new DismissAnimationsHolder(this);
View title = findViewById(R.id.card_info_field);
mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
}
public void init(Task task) {
@@ -193,6 +198,10 @@ public class TaskCardView extends LinearLayout {
mDismissAnimationsHolder.startDismissAnimation(listener);
}
public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
return mRecentsRowFocusAnimationHolder;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();

View File

@@ -15,7 +15,11 @@
*/
package com.android.systemui.recents.tv.views;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
import android.view.View;
@@ -36,10 +40,19 @@ import java.util.List;
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
private static final int ANIMATION_DELAY_MS = 50;
private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_START_RECENT_ROW_FOCUS_ANIMATION) {
startRecentsRowFocusAnimation(msg.arg1 == 1);
}
}
};
private TaskStack mStack;
private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
private AnimatorSet mRecentsRowFocusAnimation;
public TaskStackHorizontalGridView(Context context) {
this(context, null);
@@ -62,10 +75,18 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
/**
* Resets this view for reuse.
*/
public void reset() {
for (int i = 0; i < getChildCount(); i++) {
((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder().reset();
}
if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
mRecentsRowFocusAnimation.cancel();
}
mHandler.removeCallbacksAndMessages(null);
requestLayout();
}
@@ -119,10 +140,8 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T
* @return Child view for given task
*/
public TaskCardView getChildViewForTask(Task task) {
List<TaskCardView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
TaskCardView tv = taskViews.get(i);
for (int i = 0; i < getChildCount(); i++) {
TaskCardView tv = (TaskCardView) getChildAt(i);
if (tv.getTask() == task) {
return tv;
}
@@ -130,12 +149,36 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T
return null;
}
public List<TaskCardView> getTaskViews() {
return mTaskViews;
/**
* Starts the focus change animation.
*/
public void startRecentsRowFocusAnimation(final boolean hasFocus) {
if (getChildCount() == 0) {
// Animation request may happen before view is attached.
// Post again with small dealy so animation can be run again later.
if (getAdapter().getItemCount() > 0) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(
MSG_START_RECENT_ROW_FOCUS_ANIMATION, hasFocus ? 1 : 0),
ANIMATION_DELAY_MS);
}
return;
}
if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
mRecentsRowFocusAnimation.cancel();
}
Animator animator = ((TaskCardView) getChildAt(0)).getRecentsRowFocusAnimationHolder()
.getFocusChangeAnimator(hasFocus);
mRecentsRowFocusAnimation = new AnimatorSet();
AnimatorSet.Builder builder = mRecentsRowFocusAnimation.play(animator);
for (int i = 1; i < getChildCount(); i++) {
builder.with(((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder()
.getFocusChangeAnimator(hasFocus));
}
mRecentsRowFocusAnimation.start();
}
@Override
public void onStackTaskAdded(TaskStack stack, Task newTask){
public void onStackTaskAdded(TaskStack stack, Task newTask) {
getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
}

View File

@@ -131,9 +131,9 @@ public class PipRecentsOverlayManager {
* Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
* is focused.
* This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
* @param hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
* @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
*/
public void requestFocus(boolean hasRecentsFocusable) {
public void requestFocus(boolean allowRecentsFocusable) {
if (!mIsRecentsShown || mIsPipFocusedInRecent) {
return;
}
@@ -143,7 +143,7 @@ public class PipRecentsOverlayManager {
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
mPipControlsView.requestFocus();
mPipControlsView.startFocusGainAnimation();
mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE);
mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
}
/**