diff options
| author | 2016-05-27 17:23:57 +0000 | |
|---|---|---|
| committer | 2016-05-27 17:23:58 +0000 | |
| commit | f8c504db7f2e0e35c5fa19cad46681dc17db7488 (patch) | |
| tree | 9a0782c46ea50764584e9a6aaef74460a2ea3c49 | |
| parent | 4f620d423f82dd43ed79178e0091132dd49ca2a9 (diff) | |
| parent | a9e0621f33233c43f29e876b6c1055cf1dc855fc (diff) | |
Merge "PIP: Improve PIP control row's focus change animation in Recents" into nyc-dev
19 files changed, 372 insertions, 343 deletions
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml index 257bf35c8e76..257bf35c8e76 100644 --- a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml +++ b/packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml index e032008b3750..e032008b3750 100644 --- a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml +++ b/packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml index b571aa569f0d..b571aa569f0d 100644 --- a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml +++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml diff --git a/packages/SystemUI/res/layout-television/recents_on_tv.xml b/packages/SystemUI/res/layout-television/recents_on_tv.xml index 82b9f8c3c48c..2b78beef3708 100644 --- a/packages/SystemUI/res/layout-television/recents_on_tv.xml +++ b/packages/SystemUI/res/layout-television/recents_on_tv.xml @@ -21,6 +21,7 @@ android:clipChildren="false" android:clipToPadding="false" android:background="@drawable/recents_tv_background_gradient"> + <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView android:id="@+id/task_list" android:layout_width="wrap_content" @@ -32,13 +33,13 @@ android:focusable="true" android:layoutDirection="rtl" /> - <!-- Placeholder view to give focus to the PIP menus. --> + <!-- Placeholder view to give focus to the PIP menus in talkback mode --> <View android:id="@+id/pip" android:layout_width="1dp" android:layout_height="1dp" android:focusable="true" - android:visibility="visible" /> + android:visibility="gone" /> <!-- Placeholder to dismiss during talkback. --> <ImageView diff --git a/packages/SystemUI/res/layout/tv_pip_control_button.xml b/packages/SystemUI/res/layout/tv_pip_control_button.xml index f18a5da03570..096dda881693 100644 --- a/packages/SystemUI/res/layout/tv_pip_control_button.xml +++ b/packages/SystemUI/res/layout/tv_pip_control_button.xml @@ -23,15 +23,25 @@ <ImageView android:id="@+id/button" android:layout_width="34dp" android:layout_height="34dp" - android:padding="5dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" android:focusable="true" - android:src="@drawable/ic_fullscreen_white_24dp" - android:background="@drawable/tv_pip_button_focused" - android:layerType="software" /> + android:src="@drawable/tv_pip_button_focused" + android:importantForAccessibility="yes" /> + + <ImageView android:id="@+id/icon" + android:layout_width="34dp" + android:layout_height="34dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:padding="5dp" + android:importantForAccessibility="no" /> <TextView android:id="@+id/desc" android:layout_width="100dp" android:layout_height="wrap_content" + android:layout_below="@id/icon" + android:layout_centerHorizontal="true" android:layout_marginTop="3dp" android:gravity="center" android:text="@string/pip_fullscreen" diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml index 557bbe66b29b..f157fd59b23d 100644 --- a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml +++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml @@ -15,7 +15,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="top|center_horizontal" android:orientation="vertical"> @@ -33,7 +33,8 @@ android:layout_height="32dp" android:translationY="-46dp" android:layout_gravity="top|center_horizontal" - android:background="@drawable/tv_pip_recents_overlay_scrim" /> + android:background="@drawable/tv_pip_recents_overlay_scrim" + android:alpha="0" /> <com.android.systemui.tv.pip.PipControlsView android:id="@+id/pip_control_contents" android:layout_width="wrap_content" @@ -42,6 +43,8 @@ android:layout_gravity="top|center_horizontal" /> </com.android.systemui.tv.pip.PipRecentsControlsView> + <!-- Placeholder view to handle focus change between Recents row and PIP controls + in talkback mode --> <View android:id="@+id/recents" android:layout_width="1dp" diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java index 1a559585e5c0..ecb12d3c9c5c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java @@ -81,6 +81,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { private long mLastTabKeyEventTime; private boolean mIgnoreAltTabRelease; private boolean mLaunchedFromHome; + private boolean mTalkBackEnabled; private RecentsTvView mRecentsView; private View mPipView; @@ -133,15 +134,22 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { @Override public void onRecentsFocused() { - mRecentsView.requestFocus(); - mRecentsView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + if (mTalkBackEnabled) { + mTaskStackHorizontalGridView.requestFocus(); + mTaskStackHorizontalGridView.sendAccessibilityEvent( + AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + mTaskStackHorizontalGridView.startFocusGainAnimation(); } }; + private final View.OnFocusChangeListener mPipViewFocusChangeListener = new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { - handlePipViewFocusChange(hasFocus); + if (hasFocus) { + requestPipControlsFocus(); + } } }; @@ -194,17 +202,18 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { loadOpts.numVisibleTaskThumbnails = numVisibleTasks; loader.loadTasks(this, plan, loadOpts); - - mRecentsView.setTaskStack(stack); List stackTasks = stack.getStackTasks(); Collections.reverse(stackTasks); if (mTaskStackViewAdapter == null) { mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks); mTaskStackHorizontalGridView = mRecentsView .setTaskStackViewAdapter(mTaskStackViewAdapter); + mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder( + getApplicationContext(), mTaskStackHorizontalGridView); } else { mTaskStackViewAdapter.setNewStackTasks(stackTasks); } + mRecentsView.init(stack); if (launchState.launchedToTaskId != -1) { ArrayList<Task> tasks = stack.getStackTasks(); @@ -305,6 +314,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); mPipView = findViewById(R.id.pip); + mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener); // Place mPipView at the PIP bounds for fine tuned focus handling. Rect pipBounds = mPipManager.getRecentsFocusedPipBounds(); LayoutParams lp = (LayoutParams) mPipView.getLayoutParams(); @@ -342,7 +352,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { if(mLaunchedFromHome) { mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown()); } - mTaskStackViewAdapter.setResetAddedCards(true); EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent()); } @@ -353,19 +362,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { // Update the recent tasks updateRecentsTasks(); - mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder( - getApplicationContext(), mTaskStackHorizontalGridView); - if(mTaskStackHorizontalGridView != null && - mTaskStackHorizontalGridView.getChildCount() > 0) { - if(mLaunchedFromHome) { - mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues(); - } else { - mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues(); - } - } else { - mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); - } - // If this is a new instance from a configuration change, then we have to manually trigger // the enter animation state, or if recents was relaunched by AM, without going through // the normal mechanisms @@ -387,9 +383,11 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { } else { mTaskStackHorizontalGridView.setSelectedPosition(0); } + mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); View dismissPlaceholder = findViewById(R.id.dismiss_placeholder); - if (ssp.isTouchExplorationEnabled()) { + mTalkBackEnabled = ssp.isTouchExplorationEnabled(); + if (mTalkBackEnabled) { dismissPlaceholder.setAccessibilityTraversalBefore(R.id.task_list); dismissPlaceholder.setAccessibilityTraversalAfter(R.id.dismiss_placeholder); mTaskStackHorizontalGridView.setAccessibilityTraversalAfter(R.id.dismiss_placeholder); @@ -408,14 +406,29 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { } }); } - updatePipUI(); + + // Initialize PIP UI + if (mPipManager.isPipShown()) { + if (mTalkBackEnabled) { + // If talkback is on, use the mPipView to handle focus changes + // between recents row and PIP controls. + mPipView.setVisibility(View.VISIBLE); + } else { + mPipView.setVisibility(View.GONE); + } + // 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); + } else { + mPipView.setVisibility(View.GONE); + mPipRecentsOverlayManager.removePipRecentsOverlayView(); + } } @Override public void onPause() { super.onPause(); mPipRecentsOverlayManager.onRecentsPaused(); - mTaskStackViewAdapter.setResetAddedCards(false); } @Override @@ -534,6 +547,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { public final void onBusEvent(AllTaskViewsDismissedEvent event) { if (mPipManager.isPipShown()) { mRecentsView.showEmptyView(); + mPipRecentsOverlayManager.requestFocus(false); } else { dismissRecentsToHome(false); } @@ -547,10 +561,14 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { @Override public boolean onPreDraw() { mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); - if(mLaunchedFromHome) { - mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues(); + // Sets the initial values for enter animation. + // Animation will be started in {@link #onEnterAnimationComplete()} + if (mLaunchedFromHome) { + mHomeRecentsEnterExitAnimationHolder + .setEnterFromHomeStartingAnimationValues(mPipManager.isPipShown()); } else { - mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues(); + mHomeRecentsEnterExitAnimationHolder + .setEnterFromAppStartingAnimationValues(mPipManager.isPipShown()); } // We post to make sure that this information is delivered after this traversals is // finished. @@ -564,35 +582,25 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener { } private void updatePipUI() { - if (mPipManager.isPipShown()) { - mPipView.setVisibility(View.VISIBLE); - mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener); - 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); + if (!mPipManager.isPipShown()) { mPipRecentsOverlayManager.removePipRecentsOverlayView(); + mTaskStackHorizontalGridView.startFocusLossAnimation(); + } else { + Log.w(TAG, "An activity entered PIP mode while Recents is shown"); } } /** - * Handles the PIP view's focus change. + * Requests the focus to the PIP controls. * 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); - } else { - mPipRecentsOverlayManager.clearFocus(); + public void requestPipControlsFocus() { + if (!mPipManager.isPipShown()) { + return; } + + mTaskStackHorizontalGridView.startFocusLossAnimation(); + mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java index 92718a3b5ac1..9faaa4bdccb6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java @@ -74,19 +74,35 @@ public class HomeRecentsEnterExitAnimationHolder { } } - public void setEnterFromHomeStartingAnimationValues() { + /** + * Sets the initial values Recents enter animation + * when Recents is started from the Launcher. + */ + public void setEnterFromHomeStartingAnimationValues(boolean isPipShown) { for(int i = 0; i < mGridView.getChildCount(); i++) { TaskCardView view = (TaskCardView) mGridView.getChildAt(i); view.setTranslationX(0); view.setAlpha(0.0f); + view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f); + if (isPipShown && view.hasFocus()) { + view.getViewFocusAnimator().changeSize(false); + } } } - public void setEnterFromAppStartingAnimationValues() { + /** + * Sets the initial values Recents enter animation + * when Recents is started from an app. + */ + public void setEnterFromAppStartingAnimationValues(boolean isPipShown) { for(int i = 0; i < mGridView.getChildCount(); i++) { TaskCardView view = (TaskCardView) mGridView.getChildAt(i); view.setTranslationX(0); - view.setAlpha(1.0f); + view.setAlpha(isPipShown ? mDimAlpha : 1f); + view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f); + if (isPipShown && view.hasFocus()) { + view.getViewFocusAnimator().changeSize(false); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java index 45c15379485b..8a4cf399b813 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java @@ -29,11 +29,11 @@ import com.android.systemui.recents.tv.views.TaskCardView; * Recents row's focus animation with PIP controls. */ public class RecentsRowFocusAnimationHolder { - private View mView; - private View mTitleView; + private final View mView; + private final View mTitleView; private AnimatorSet mFocusGainAnimatorSet; - private AnimatorSet mFocusLoseAnimatorSet; + private AnimatorSet mFocusLossAnimatorSet; public RecentsRowFocusAnimationHolder(View view, View titleView) { mView = view; @@ -50,28 +50,45 @@ public class RecentsRowFocusAnimationHolder { mFocusGainAnimatorSet.setDuration(duration); mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mFocusLoseAnimatorSet = new AnimatorSet(); - mFocusLoseAnimatorSet.playTogether( + mFocusLossAnimatorSet = new AnimatorSet(); + mFocusLossAnimatorSet.playTogether( // Animation doesn't start from the current value (1f) sometimes, // so specify the desired initial value here. ObjectAnimator.ofFloat(mView, "alpha", 1f, dimAlpha), ObjectAnimator.ofFloat(mTitleView, "alpha", 0f)); - mFocusLoseAnimatorSet.setDuration(duration); - mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mFocusLossAnimatorSet.setDuration(duration); + mFocusLossAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); } /** - * Returns the Recents row's focus change animation. + * Starts the Recents row's focus gain animation. */ - public Animator getFocusChangeAnimator(boolean hasFocus) { - return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet; + public void startFocusGainAnimation() { + cancelAnimator(mFocusLossAnimatorSet); + mFocusGainAnimatorSet.start(); } /** - * Resets the views to the initial state immediately. + * Starts the Recents row's focus loss animation. + */ + public void startFocusLossAnimation() { + cancelAnimator(mFocusGainAnimatorSet); + mFocusLossAnimatorSet.start(); + } + + /** + * Resets the views immediately and ends the animations. */ public void reset() { + cancelAnimator(mFocusLossAnimatorSet); + cancelAnimator(mFocusGainAnimatorSet); mView.setAlpha(1f); mTitleView.setAlpha(1f); } + + private static void cancelAnimator(Animator animator) { + if (animator.isStarted()) { + animator.cancel(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java index 82185990c828..72fd7a4114ac 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java @@ -89,7 +89,7 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { }); } - public void setFocusProgress(float level) { + private void setFocusProgress(float level) { mFocusProgress = level; float scale = mUnselectedScale + (level * mSelectedScaleDelta); @@ -107,33 +107,19 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { mTargetView.getDismissIconView().setZ(z); } - public float getFocusProgress() { - return mFocusProgress; - } - - public void animateFocus(boolean focused) { + private void animateFocus(boolean focused) { if (mFocusAnimation.isStarted()) { mFocusAnimation.cancel(); } float target = focused ? 1.0f : 0.0f; - if (getFocusProgress() != target) { - mFocusAnimation.setFloatValues(getFocusProgress(), target); + if (mFocusProgress != target) { + mFocusAnimation.setFloatValues(mFocusProgress, target); mFocusAnimation.start(); } } - public void setFocusImmediate(boolean focused) { - if (mFocusAnimation.isStarted()) { - mFocusAnimation.cancel(); - } - - float target = focused ? 1.0f : 0.0f; - - setFocusProgress(target); - } - @Override public void onFocusChange(View v, boolean hasFocus) { if (v != mTargetView) { @@ -142,21 +128,27 @@ public class ViewFocusAnimator implements View.OnFocusChangeListener { changeSize(hasFocus); } - protected void changeSize(boolean hasFocus) { + /** + * Changes the size of the {@link TaskCardView} to show its focused state. + */ + public void changeSize(boolean hasFocus) { ViewGroup.LayoutParams lp = mTargetView.getLayoutParams(); int width = lp.width; int height = lp.height; if (width < 0 && height < 0) { mTargetView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - height = mTargetView.getMeasuredHeight(); } if (mTargetView.isAttachedToWindow() && mTargetView.hasWindowFocus() && mTargetView.getVisibility() == View.VISIBLE) { animateFocus(hasFocus); } else { - setFocusImmediate(hasFocus); + // Set focus immediately. + if (mFocusAnimation.isStarted()) { + mFocusAnimation.cancel(); + } + setFocusProgress(hasFocus ? 1.0f : 0.0f); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java index 06b24418a572..318b7f91fabf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java @@ -59,7 +59,7 @@ public class RecentsTvView extends FrameLayout { private boolean mAwaitingFirstLayout = true; private Rect mSystemInsets = new Rect(); private RecentsTvTransitionHelper mTransitionHelper; - private Handler mHandler; + private final Handler mHandler = new Handler(); private OnScrollListener mScrollListener; public RecentsTvView(Context context) { this(context, null); @@ -81,9 +81,7 @@ public class RecentsTvView extends FrameLayout { 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); } @@ -91,20 +89,18 @@ public class RecentsTvView extends FrameLayout { protected void onFinishInflate() { super.onFinishInflate(); mDismissPlaceholder = findViewById(R.id.dismiss_placeholder); + mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list); } - public void setTaskStack(TaskStack stack) { + /** + * Initialize the view. + */ + public void init(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); - } + mTaskStackHorizontalView.init(stack); if (stack.getStackTaskCount() > 0) { hideEmptyView(); @@ -112,6 +108,7 @@ public class RecentsTvView extends FrameLayout { showEmptyView(); } + // Layout with the new stack requestLayout(); } @@ -189,17 +186,6 @@ public class RecentsTvView extends FrameLayout { } /** - * 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() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java index 758f93a5f5d5..72a589fe3c1a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java @@ -40,16 +40,18 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.tv.RecentsTvActivity; 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; public class TaskCardView extends LinearLayout { private static final String TAG = "TaskCardView"; private View mThumbnailView; private View mDismissIconView; + private View mInfoFieldView; private TextView mTitleTextView; private ImageView mBadgeView; private Task mTask; @@ -79,14 +81,14 @@ public class TaskCardView extends LinearLayout { protected void onFinishInflate() { super.onFinishInflate(); mThumbnailView = findViewById(R.id.card_view_thumbnail); + mInfoFieldView = findViewById(R.id.card_info_field); mTitleTextView = (TextView) findViewById(R.id.card_title_text); mBadgeView = (ImageView) findViewById(R.id.card_extra_badge); mDismissIconView = findViewById(R.id.dismiss_icon); mDismissAnimationsHolder = new DismissAnimationsHolder(this); - View title = findViewById(R.id.card_info_field); mCornerRadius = getResources().getDimensionPixelSize( R.dimen.recents_task_view_rounded_corners_radius); - mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title); + mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, mInfoFieldView); SystemServicesProxy ssp = Recents.getSystemServices(); if (!ssp.isTouchExplorationEnabled()) { mDismissIconView.setVisibility(VISIBLE); @@ -102,6 +104,9 @@ public class TaskCardView extends LinearLayout { mBadgeView.setImageDrawable(task.icon); setThumbnailView(); setContentDescription(task.titleDescription); + mDismissState = false; + mDismissAnimationsHolder.reset(); + mRecentsRowFocusAnimationHolder.reset(); } public Task getTask() { @@ -196,40 +201,37 @@ public class TaskCardView extends LinearLayout { } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { + public boolean dispatchKeyEvent(KeyEvent event) { + // Override dispatchKeyEvent() instead of onKeyDown() to prevent warning from ViewRootImpl. + switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_DOWN : { - if (!isInDismissState()) { + if (!isInDismissState() && event.getAction() == KeyEvent.ACTION_DOWN) { setDismissState(true); return true; } break; } case KeyEvent.KEYCODE_DPAD_UP : { - if (isInDismissState()) { - setDismissState(false); - return true; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (isInDismissState()) { + setDismissState(false); + } else { + ((RecentsTvActivity) getContext()).requestPipControlsFocus(); + } } - break; + return true; } - //Eat right and left key presses when we are in dismiss state - case KeyEvent.KEYCODE_DPAD_LEFT : { - if (isInDismissState()) { - return true; - } - break; - } + // Eat right and left key presses when we are in dismiss state + case KeyEvent.KEYCODE_DPAD_LEFT : case KeyEvent.KEYCODE_DPAD_RIGHT : { if (isInDismissState()) { return true; } break; } - default: - break; } - return super.onKeyDown(keyCode, event); + return super.dispatchKeyEvent(event); } private void setDismissState(boolean dismissState) { @@ -252,20 +254,12 @@ public class TaskCardView extends LinearLayout { mDismissAnimationsHolder.startDismissAnimation(listener); } - public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() { - return mRecentsRowFocusAnimationHolder; + public ViewFocusAnimator getViewFocusAnimator() { + return mViewFocusAnimator; } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - setDismissState(false); - } - - public void reset() { - mDismissState = false; - mRecentsRowFocusAnimationHolder.reset(); - mDismissAnimationsHolder.reset(); + public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() { + return mRecentsRowFocusAnimationHolder; } private void setThumbnailView() { @@ -332,6 +326,10 @@ public class TaskCardView extends LinearLayout { return mThumbnailView; } + public View getInfoFieldView() { + return mInfoFieldView; + } + public View getDismissIconView() { return mDismissIconView; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java index 3fb339e8f98a..f9b8700c1d46 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java @@ -18,8 +18,6 @@ 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; @@ -39,14 +37,6 @@ import com.android.systemui.recents.views.AnimationProps; 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 Task mFocusedTask; private AnimatorSet mRecentsRowFocusAnimation; @@ -74,38 +64,15 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T } /** - * 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(); - } - - /** - * @param task - Task to reset - */ - private void resetFocusedTask(Task task) { - mFocusedTask = null; - } - - /** - * Sets the task stack. + * Initializes the grid view. * @param stack */ - public void setStack(TaskStack stack) { - //Set new stack + public void init(TaskStack stack) { + // Set new stack mStack = stack; if (mStack != null) { mStack.setCallbacks(this); } - //Layout with new stack - requestLayout(); } /** @@ -126,13 +93,6 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T } /** - * @return - The focused task card view. - */ - public TaskCardView getFocusedTaskCardView() { - return ((TaskCardView)findFocus()); - } - - /** * @param task * @return Child view for given task */ @@ -146,32 +106,31 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T return null; } + /** - * Starts the focus change animation. + * Starts the Recents row's focus gain 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); + public void startFocusGainAnimation() { + for (int i = 0; i < getChildCount(); i++) { + TaskCardView v = (TaskCardView) getChildAt(i); + if (v.hasFocus()) { + v.getViewFocusAnimator().changeSize(true); } - return; - } - if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) { - mRecentsRowFocusAnimation.cancel(); + v.getRecentsRowFocusAnimationHolder().startFocusGainAnimation(); } - 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)); + } + + /** + * Starts the Recents row's focus loss animation. + */ + public void startFocusLossAnimation() { + for (int i = 0; i < getChildCount(); i++) { + TaskCardView v = (TaskCardView) getChildAt(i); + if (v.hasFocus()) { + v.getViewFocusAnimator().changeSize(false); + } + v.getRecentsRowFocusAnimationHolder().startFocusLossAnimation(); } - mRecentsRowFocusAnimation.start(); } @Override @@ -185,7 +144,7 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T AnimationProps animation, boolean fromDockGesture) { ((TaskStackHorizontalViewAdapter) getAdapter()).removeTask(removedTask); if (mFocusedTask == removedTask) { - resetFocusedTask(removedTask); + mFocusedTask = null; } // If there are no remaining tasks, then just close recents if (mStack.getStackTaskCount() == 0) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java index b6b86b409948..236d0778fadd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java @@ -43,17 +43,13 @@ public class TaskStackHorizontalViewAdapter extends private static final String TAG = "TaskStackViewAdapter"; private List<Task> mTaskList; private TaskStackHorizontalGridView mGridView; - private boolean mResetAddedCards; public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ private TaskCardView mTaskCardView; private Task mTask; - private boolean mShouldReset; public ViewHolder(View v) { super(v); - if(v instanceof TaskCardView) { - mTaskCardView = (TaskCardView) v; - } + mTaskCardView = (TaskCardView) v; } public void init(Task task) { @@ -90,7 +86,6 @@ public class TaskStackHorizontalViewAdapter extends public void onAnimationEnd(Animator animation) { removeTask(task); EventBus.getDefault().send(new DeleteTaskDataEvent(task)); - mShouldReset = true; } @Override @@ -131,23 +126,6 @@ public class TaskStackHorizontalViewAdapter extends } @Override - public void onViewAttachedToWindow(ViewHolder holder) { - if (mResetAddedCards) { - holder.mTaskCardView.reset(); - } - } - - @Override - public void onViewDetachedFromWindow(ViewHolder holder) { - // We only want to reset on view detach if this is the last task being dismissed. - // This is so that we do not reset when shifting to apps etc, as it is not needed. - if (holder.mShouldReset) { - holder.mTaskCardView.reset(); - holder.mShouldReset = false; - } - } - - @Override public int getItemCount() { return mTaskList.size(); } @@ -178,8 +156,4 @@ public class TaskStackHorizontalViewAdapter extends mTaskList.add(position, task); notifyItemInserted(position); } - - public void setResetAddedCards(boolean reset) { - mResetAddedCards = reset; - } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java index bcf2f67944a6..80c593c076e9 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java @@ -21,12 +21,11 @@ import android.animation.AnimatorInflater; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.View.OnFocusChangeListener; import android.view.View; import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import com.android.systemui.R; @@ -34,31 +33,28 @@ import com.android.systemui.R; /** * A view containing PIP controls including fullscreen, close, and media controls. */ -public class PipControlButtonView extends LinearLayout { +public class PipControlButtonView extends RelativeLayout { private OnFocusChangeListener mFocusChangeListener; - private ImageView mButtonImageView; + private ImageView mIconImageView; + ImageView mButtonImageView; private TextView mDescriptionTextView; - private Animator mFocusGainAnimator; - private Animator mFocusLoseAnimator; + private Animator mTextFocusGainAnimator; + private Animator mButtonFocusGainAnimator; + private Animator mTextFocusLossAnimator; + private Animator mButtonFocusLossAnimator; private final OnFocusChangeListener mInternalFocusChangeListener = new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { - if (mFocusLoseAnimator.isStarted()) { - mFocusLoseAnimator.cancel(); - } - mFocusGainAnimator.start(); + startFocusGainAnimation(); } else { - if (mFocusGainAnimator.isStarted()) { - mFocusGainAnimator.cancel(); - } - mFocusLoseAnimator.start(); + startFocusLossAnimation(); } if (mFocusChangeListener != null) { - mFocusChangeListener.onFocusChange(v, hasFocus); + mFocusChangeListener.onFocusChange(PipControlButtonView.this, hasFocus); } } }; @@ -82,9 +78,7 @@ public class PipControlButtonView extends LinearLayout { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.tv_pip_control_button, this); - setOrientation(LinearLayout.VERTICAL); - setGravity(Gravity.CENTER); - + mIconImageView = (ImageView) findViewById(R.id.icon); mButtonImageView = (ImageView) findViewById(R.id.button); mDescriptionTextView = (TextView) findViewById(R.id.desc); @@ -103,12 +97,19 @@ public class PipControlButtonView extends LinearLayout { super.onFinishInflate(); mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener); - mFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), - R.anim.tv_pip_controls_text_focus_gain_animation); - mFocusGainAnimator.setTarget(mDescriptionTextView); - mFocusLoseAnimator = AnimatorInflater.loadAnimator(getContext(), - R.anim.tv_pip_controls_text_focus_lose_animation); - mFocusLoseAnimator.setTarget(mDescriptionTextView); + mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), + R.anim.tv_pip_controls_focus_gain_animation); + mTextFocusGainAnimator.setTarget(mDescriptionTextView); + mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), + R.anim.tv_pip_controls_focus_gain_animation); + mButtonFocusGainAnimator.setTarget(mButtonImageView); + + mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(), + R.anim.tv_pip_controls_focus_loss_animation); + mTextFocusLossAnimator.setTarget(mDescriptionTextView); + mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(), + R.anim.tv_pip_controls_focus_loss_animation); + mButtonFocusLossAnimator.setTarget(mButtonImageView); } @Override @@ -125,7 +126,7 @@ public class PipControlButtonView extends LinearLayout { * Sets the drawable for the button with the given resource id. */ public void setImageResource(int resId) { - mButtonImageView.setImageResource(resId); + mIconImageView.setImageResource(resId); } /** @@ -136,8 +137,51 @@ public class PipControlButtonView extends LinearLayout { mDescriptionTextView.setText(resId); } - @Override - public boolean isFocused() { - return mButtonImageView.isFocused(); + private static void cancelAnimator(Animator animator) { + if (animator.isStarted()) { + animator.cancel(); + } + } + + /** + * Starts the focus gain animation. + */ + public void startFocusGainAnimation() { + cancelAnimator(mButtonFocusLossAnimator); + cancelAnimator(mTextFocusLossAnimator); + mTextFocusGainAnimator.start(); + if (mButtonImageView.getAlpha() < 1f) { + // If we had faded out the ripple drawable, run our manual focus change animation. + // See the comment at {@link #startFocusLossAnimation()} for the reason of manual + // animator. + mButtonFocusGainAnimator.start(); + } + } + + /** + * Starts the focus loss animation. + */ + public void startFocusLossAnimation() { + cancelAnimator(mButtonFocusGainAnimator); + cancelAnimator(mTextFocusGainAnimator); + mTextFocusLossAnimator.start(); + if (mButtonImageView.hasFocus()) { + // Button uses ripple that has the default animation for the focus changes. + // Howevever, it doesn't expose the API to fade out while it is focused, + // so we should manually run the fade out animation when PIP controls row loses focus. + mButtonFocusLossAnimator.start(); + } + } + + /** + * Resets to initial state. + */ + public void reset() { + cancelAnimator(mButtonFocusGainAnimator); + cancelAnimator(mTextFocusGainAnimator); + cancelAnimator(mButtonFocusLossAnimator); + cancelAnimator(mTextFocusLossAnimator); + mButtonImageView.setAlpha(1f); + mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f); } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java index 5614bf9c566b..71740ce1b391 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java @@ -61,8 +61,7 @@ public class PipControlsView extends LinearLayout { private PipControlButtonView mCloseButtonView; private PipControlButtonView mPlayPauseButtonView; - private boolean mHasFocus; - private OnFocusChangeListener mOnChildFocusChangeListener; + private PipControlButtonView mFocusedChild; private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { @Override @@ -80,8 +79,12 @@ public class PipControlsView extends LinearLayout { private final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() { @Override - public void onFocusChange(View v, boolean hasFocus) { - onChildViewFocusChanged(); + public void onFocusChange(View view, boolean hasFocus) { + if (hasFocus) { + mFocusedChild = (PipControlButtonView) view; + } else if (mFocusedChild == view) { + mFocusedChild = null; + } } }; @@ -200,23 +203,13 @@ public class PipControlsView extends LinearLayout { } /** - * Sets a listener to be invoked when {@link android.view.View.hasFocus()} is changed. + * Resets to initial state. */ - public void setOnChildFocusChangeListener(OnFocusChangeListener listener) { - mOnChildFocusChangeListener = listener; - } - - private void onChildViewFocusChanged() { - // At this moment, hasFocus() returns true although there's no focused child. - boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused()) - || (mPlayPauseButtonView != null && mPlayPauseButtonView.isFocused()) - || (mCloseButtonView != null && mCloseButtonView.isFocused()); - if (mHasFocus != hasFocus) { - mHasFocus = hasFocus; - if (mOnChildFocusChangeListener != null) { - mOnChildFocusChangeListener.onFocusChange(getFocusedChild(), mHasFocus); - } - } + public void reset() { + mFullButtonView.reset(); + mCloseButtonView.reset(); + mPlayPauseButtonView.reset(); + mFullButtonView.requestFocus(); } /** @@ -225,4 +218,11 @@ public class PipControlsView extends LinearLayout { public void setListener(Listener listener) { mListener = listener; } + + /** + * Returns the focused control button view to animate focused button. + */ + PipControlButtonView getFocusedButton() { + return mFocusedChild; + } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java index 476598d3afc7..30622d29b9a9 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java @@ -126,7 +126,6 @@ public class PipManager { private int mSuspendPipResizingReason; private Context mContext; - private SystemServicesProxy mSystemServiceProxy; private PipRecentsOverlayManager mPipRecentsOverlayManager; private IActivityManager mActivityManager; private MediaSessionManager mMediaSessionManager; @@ -213,8 +212,7 @@ public class PipManager { mPipBounds = mDefaultPipBounds; mActivityManager = ActivityManagerNative.getDefault(); - mSystemServiceProxy = SystemServicesProxy.getInstance(context); - mSystemServiceProxy.registerTaskStackListener(mTaskStackListener); + SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED); mContext.registerReceiver(mBroadcastReceiver, intentFilter); diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java index df44dc1c2362..ffe96afae3a3 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java @@ -48,12 +48,12 @@ public class PipRecentsControlsView extends FrameLayout { abstract void onBackPressed(); } - final PipManager mPipManager = PipManager.getInstance(); + private final PipManager mPipManager = PipManager.getInstance(); private Listener mListener; private PipControlsView mPipControlsView; private View mScrim; private Animator mFocusGainAnimator; - private AnimatorSet mFocusLoseAnimatorSet; + private AnimatorSet mFocusLossAnimatorSet; public PipRecentsControlsView(Context context) { this(context, null, 0, 0); @@ -80,12 +80,12 @@ public class PipRecentsControlsView extends FrameLayout { mScrim = findViewById(R.id.scrim); mFocusGainAnimator = loadAnimator(mPipControlsView, - R.anim.tv_pip_controls_in_recents_focus_gain_animation); + R.anim.tv_pip_controls_in_recents_focus_gain_animation); - mFocusLoseAnimatorSet = new AnimatorSet(); - mFocusLoseAnimatorSet.playSequentially( + mFocusLossAnimatorSet = new AnimatorSet(); + mFocusLossAnimatorSet.playSequentially( loadAnimator(mPipControlsView, - R.anim.tv_pip_controls_in_recents_focus_lose_animation), + R.anim.tv_pip_controls_in_recents_focus_loss_animation), loadAnimator(mScrim, R.anim.tv_pip_controls_in_recents_scrim_fade_in_animation)); Rect pipBounds = mPipManager.getRecentsFocusedPipBounds(); @@ -99,21 +99,29 @@ public class PipRecentsControlsView extends FrameLayout { } /** - * Starts focus gaining animation. + * Starts focus gain animation. */ public void startFocusGainAnimation() { // Hides the scrim view as soon as possible, before the PIP resize animation starts. // If we don't, PIP will be moved down a bit and a gap between the scrim and PIP will be // shown at the bottom of the PIP. mScrim.setAlpha(0); - startAnimator(mFocusGainAnimator, mFocusLoseAnimatorSet); + PipControlButtonView focus = mPipControlsView.getFocusedButton(); + if (focus != null) { + focus.startFocusGainAnimation(); + } + startAnimator(mFocusGainAnimator, mFocusLossAnimatorSet); } /** - * Starts focus losing animation. + * Starts focus loss animation. */ - public void startFocusLoseAnimation() { - startAnimator(mFocusLoseAnimatorSet, mFocusGainAnimator); + public void startFocusLossAnimation() { + PipControlButtonView focus = mPipControlsView.getFocusedButton(); + if (focus != null) { + focus.startFocusLossAnimation(); + } + startAnimator(mFocusLossAnimatorSet, mFocusGainAnimator); } /** @@ -121,14 +129,14 @@ public class PipRecentsControlsView extends FrameLayout { */ public void reset() { cancelAnimator(mFocusGainAnimator); - cancelAnimator(mFocusLoseAnimatorSet); + cancelAnimator(mFocusLossAnimatorSet); // Reset to initial state (i.e. end of focused) - requestFocus(); + mScrim.setAlpha(0); mPipControlsView.setTranslationY(0); mPipControlsView.setScaleX(1); mPipControlsView.setScaleY(1); - mScrim.setAlpha(0); + mPipControlsView.reset(); } private static void startAnimator(Animator animator, Animator previousAnimator) { @@ -153,13 +161,20 @@ public class PipRecentsControlsView extends FrameLayout { @Override public boolean dispatchKeyEvent(KeyEvent event) { - if (!event.isCanceled() - && event.getKeyCode() == KeyEvent.KEYCODE_BACK - && event.getAction() == KeyEvent.ACTION_UP) { - if (mPipControlsView.mListener != null) { - ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed(); + if (!event.isCanceled()) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK + && event.getAction() == KeyEvent.ACTION_UP) { + if (mPipControlsView.mListener != null) { + ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed(); + } + return true; + } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + mPipManager.getPipRecentsOverlayManager().clearFocus(); + } + // Consume the down event always to prevent warning logs from ViewRootImpl. + return true; } - return true; } return super.dispatchKeyEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java index 6e4a5938d7b5..895b8a228a00 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java @@ -18,15 +18,20 @@ package com.android.systemui.tv.pip; import android.content.Context; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.WindowManager.LayoutParams; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; import com.android.systemui.R; +import com.android.systemui.recents.misc.SystemServicesProxy; +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.TOP; +import static android.view.View.MeasureSpec.UNSPECIFIED; import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY; import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS; import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED; @@ -42,13 +47,16 @@ public class PipRecentsOverlayManager { private final PipManager mPipManager = PipManager.getInstance(); private final WindowManager mWindowManager; + private final SystemServicesProxy mSystemServicesProxy; private View mOverlayView; private PipRecentsControlsView mPipControlsView; private View mRecentsView; + private boolean mTalkBackEnabled; - private final LayoutParams mPipRecentsControlsViewLayoutParams; - private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams; + private LayoutParams mPipRecentsControlsViewLayoutParams; + private LayoutParams mPipRecentsControlsViewFocusedLayoutParams; + private boolean mHasFocusableInRecents; private boolean mIsPipRecentsOverlayShown; private boolean mIsRecentsShown; private boolean mIsPipFocusedInRecent; @@ -72,18 +80,7 @@ public class PipRecentsOverlayManager { PipRecentsOverlayManager(Context context) { mWindowManager = (WindowManager) context.getSystemService(WindowManager.class); - - mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - LayoutParams.TYPE_SYSTEM_DIALOG, - LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE, - PixelFormat.TRANSLUCENT); - mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - LayoutParams.TYPE_SYSTEM_DIALOG, - 0, - PixelFormat.TRANSLUCENT); - + mSystemServicesProxy = SystemServicesProxy.getInstance(context); initViews(context); } @@ -101,6 +98,20 @@ public class PipRecentsOverlayManager { } } }); + + mOverlayView.measure(UNSPECIFIED, UNSPECIFIED); + mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams( + mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(), + LayoutParams.TYPE_SYSTEM_DIALOG, + LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + mPipRecentsControlsViewLayoutParams.gravity = TOP | CENTER_HORIZONTAL; + mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams( + mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(), + LayoutParams.TYPE_SYSTEM_DIALOG, + 0, + PixelFormat.TRANSLUCENT); + mPipRecentsControlsViewFocusedLayoutParams.gravity = TOP | CENTER_HORIZONTAL; } /** @@ -111,9 +122,10 @@ public class PipRecentsOverlayManager { if (mIsPipRecentsOverlayShown) { return; } + mTalkBackEnabled = mSystemServicesProxy.isTouchExplorationEnabled(); + mRecentsView.setVisibility(mTalkBackEnabled ? View.VISIBLE : View.GONE); mIsPipRecentsOverlayShown = true; mIsPipFocusedInRecent = true; - mPipControlsView.reset(); mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams); } @@ -126,50 +138,46 @@ public class PipRecentsOverlayManager { return; } mWindowManager.removeView(mOverlayView); + // Resets the controls view when its removed. + // If not, changing focus in reset will be show animation when Recents is resumed. + mPipControlsView.reset(); mIsPipRecentsOverlayShown = false; } /** * Request focus to the PIP Recents overlay. - * 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 allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task) + * @param hasFocusableInRecents {@code true} if Recents can have focus. (i.e. Has a recent task) */ - public void requestFocus(boolean allowRecentsFocusable) { - mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE); + public void requestFocus(boolean hasFocusableInRecents) { + mHasFocusableInRecents = hasFocusableInRecents; if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent || !mPipManager.isPipShown()) { return; } mIsPipFocusedInRecent = true; - mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED); - - mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams); - mPipControlsView.requestFocus(); - mPipControlsView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); mPipControlsView.startFocusGainAnimation(); + mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams); + mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED); + if (mTalkBackEnabled) { + mPipControlsView.requestFocus(); + mPipControlsView.sendAccessibilityEvent( + AccessibilityEvent.TYPE_VIEW_FOCUSED); + } } /** * Request focus to the PIP Recents overlay. - * 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}. */ public void clearFocus() { if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent - || !mPipManager.isPipShown()) { + || !mPipManager.isPipShown() || !mHasFocusableInRecents) { return; } - if (!mRecentsView.hasFocus()) { - // Let mRecentsView's focus listener handle clearFocus(). - mRecentsView.requestFocus(); - } mIsPipFocusedInRecent = false; - mPipManager.resizePinnedStack(STATE_PIP_RECENTS); + mPipControlsView.startFocusLossAnimation(); mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams); - mPipControlsView.startFocusLoseAnimation(); + mPipManager.resizePinnedStack(STATE_PIP_RECENTS); if (mCallback != null) { mCallback.onRecentsFocused(); } |