PIP: Apply the animation spec for the PIP in Recents

Bug: 27540465
Change-Id: Ibdd4a4e3fd194978ccbc29235a1c620ebddff942
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
similarity index 78%
rename from packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
index 89e4aac..ebc6a4a7 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
@@ -17,13 +17,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android">
 
     <objectAnimator
-        android:propertyName="translationY"
-        android:valueTo="10dp"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:propertyName="scaleX"
+        android:valueTo="1.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
     <objectAnimator
-        android:propertyName="alpha"
+        android:propertyName="scaleY"
         android:valueTo="1.0"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
 </set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
similarity index 76%
rename from packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
index c73fed6..95499bd 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
@@ -17,13 +17,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android">
 
     <objectAnimator
-        android:propertyName="translationY"
-        android:valueTo="0dp"
-        android:interpolator="@android:interpolator/fast_out_linear_in"
+        android:propertyName="scaleX"
+        android:valueTo="0.7"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
     <objectAnimator
-        android:propertyName="alpha"
-        android:valueTo="0.0"
-        android:interpolator="@android:interpolator/fast_out_linear_in"
+        android:propertyName="scaleY"
+        android:valueTo="0.7"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
 </set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..7555bdd
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:propertyName="translationY"
+    android:valueTo="0dp"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
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_lose_animation.xml
new file mode 100644
index 0000000..b40ccd4
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:propertyName="translationY"
+    android:valueTo="-57dp"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..681ff91
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:propertyName="alpha"
+    android:valueTo="1"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..e6deb0f
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:propertyName="alpha"
+    android:valueTo="0"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
index 5cabb77..405ea0c 100644
--- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -17,8 +17,8 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
     <size
-        android:width="36dp"
-        android:height="36dp" />
+        android:width="34dp"
+        android:height="34dp" />
     <solid
         android:color="#4DFFFFFF" />
 </shape>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 0f8c77c..28ea66d 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -31,18 +31,12 @@
         android:focusable="true"
         android:layoutDirection="rtl" />
 
+    <!-- Placeholder view to give focus to the PIP menus. -->
     <View
-        android:id="@+id/pip_shade"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone"
-        android:background="#76000000" />
-
-    <include
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:layout_marginTop="132dp"
-        layout="@layout/tv_pip_controls" />
+        android:id="@+id/pip"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:focusable="true"
+        android:visibility="gone" />
 
 </com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 2e0c9e7..563441f 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -17,13 +17,8 @@
 */
 -->
 
-<com.android.systemui.tv.pip.PipControlsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/pip_controls"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:orientation="horizontal">
+<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <LinearLayout
         android:layout_width="100dp"
@@ -98,4 +93,4 @@
             android:textSize="12sp"
             android:textColor="#EEEEEE" />
     </LinearLayout>
-</com.android.systemui.tv.pip.PipControlsView>
+</merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index c2c83ff..2647a99 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,7 +26,8 @@
     android:gravity="top|center_horizontal"
     android:clipChildren="false">
 
-    <include
-        layout="@layout/tv_pip_controls"
-        android:clipChildren="false" />
+    <com.android.systemui.tv.pip.PipControlsView
+        android:id="@+id/pip_controls"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index c5c7e84..64bf3b5 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -38,25 +38,4 @@
         android:gravity="center"
         android:maxLines="2"
         android:text="@string/pip_hold_home" />
-    <LinearLayout
-        android:id="@+id/guide_buttons"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_centerHorizontal="true"
-        android:orientation="horizontal">
-        <ImageView
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_fullscreen_white_24dp" />
-        <ImageView
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_close_white" />
-        <ImageView
-            android:id="@+id/guide_button_play_pause"
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_pause_white_24dp" />
-    </LinearLayout>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
new file mode 100644
index 0000000..1e464d8
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="top|center_horizontal">
+
+    <com.android.systemui.tv.pip.PipRecentsControlsView
+        android:id="@+id/pip_controls"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent" />
+
+    <View
+        android:id="@+id/recents"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        android:focusable="true" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 953dd65..337513d 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -38,9 +38,6 @@
     <dimen name="recents_tv_unselected_item_z">6dp</dimen>
     <dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
 
-    <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
-    <dimen name="tv_pip_bounds_space">3dp</dimen>
-
     <!-- Values for text on recents cards on tv -->
     <dimen name="recents_tv_title_text_size">12sp</dimen>
 
@@ -52,4 +49,10 @@
     <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
     <dimen name="recents_tv_dismiss_text_size">12sp</dimen>
 
+    <!-- Values for PIP in recents -->
+    <dimen name="recents_tv_pip_controls_margin_top">10dp</dimen>
+
+    <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
+    <dimen name="tv_pip_bounds_space">3dp</dimen>
+
 </resources>
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 134b90c..483f9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.recents.tv;
 
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Intent;
@@ -55,11 +53,12 @@
 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;
 import com.android.systemui.tv.pip.PipManager;
-import com.android.systemui.tv.pip.PipControlsView;
+import com.android.systemui.tv.pip.PipRecentsOverlayManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -80,15 +79,13 @@
     private boolean mIgnoreAltTabRelease;
 
     private RecentsTvView mRecentsView;
-    private PipControlsView mPipControlsView;
-    private View mPipShadeView;
-    private AnimatorSet mPipControlsViewFadeInAnimator;
-    private AnimatorSet mPipControlsViewFadeOutAnimator;
+    private FocusAnimationHolder mRecentsFocusAnimationHolder;
+    private View mPipView;
     private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
 
-    private PipManager mPipManager;
-    private PipManager.Listener mPipListener = new PipManager.Listener() {
+    private final PipManager mPipManager = PipManager.getInstance();
+    private final PipManager.Listener mPipListener = new PipManager.Listener() {
         @Override
         public void onPipEntered() {
             updatePipUI();
@@ -113,10 +110,38 @@
 
         @Override
         public void onPipResizeAboutToStart() { }
-
-        @Override
-        public void onMediaControllerChanged() { }
     };
+    private PipRecentsOverlayManager mPipRecentsOverlayManager;
+    private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback =
+            new PipRecentsOverlayManager.Callback() {
+                @Override
+                public void onClosed() {
+                    dismissRecentsToLaunchTargetTaskOrHome();
+                }
+
+                @Override
+                public void onBackPressed() {
+                    RecentsTvActivity.this.onBackPressed();
+                }
+
+                @Override
+                public void onRecentsFocused() {
+                    mRecentsView.requestFocus();
+                }
+            };
+    private final View.OnFocusChangeListener mPipViewFocusChangeListener =
+            new View.OnFocusChangeListener() {
+                @Override
+                public void onFocusChange(View v, boolean hasFocus) {
+                    if (hasFocus) {
+                        mRecentsFocusAnimationHolder.startFocusLoseAnimation();
+                        mPipRecentsOverlayManager.requestFocus(
+                                mTaskStackViewAdapter.getItemCount() > 0);
+                    } else {
+                        mRecentsFocusAnimationHolder.startFocusGainAnimation();
+                    }
+                }
+            };
 
     /**
      * A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -248,7 +273,7 @@
             finish();
             return;
         }
-        mPipManager = PipManager.getInstance();
+        mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
 
         // Register this activity with the event bus
         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -263,21 +288,19 @@
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
-        mPipControlsView.setListener(new PipControlsView.Listener() {
-            @Override
-            public void onClosed() {
-                dismissRecentsToLaunchTargetTaskOrHome();
-            }
-        });
-        mPipShadeView = findViewById(R.id.pip_shade);
+        mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
 
-        mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
-                R.anim.tv_pip_controls_fade_in);
-        mPipControlsViewFadeInAnimator.setTarget(mPipControlsView);
-        mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
-                R.anim.tv_pip_controls_fade_out);
-        mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView);
+        mPipView = findViewById(R.id.pip);
+        // Place mPipView at the PIP bounds for fine tuned focus handling.
+        Rect pipBounds = mPipManager.getPipBounds();
+        LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
+        lp.width = pipBounds.width();
+        lp.height = pipBounds.height();
+        lp.leftMargin = pipBounds.left;
+        lp.topMargin = pipBounds.top;
+        mPipView.setLayoutParams(lp);
+
+        mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
 
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -289,7 +312,6 @@
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
 
-        updatePipUI();
         mPipManager.addListener(mPipListener);
     }
 
@@ -321,9 +343,7 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
 
-        mPipManager.onRecentsStarted();
-        // Give focus to the recents row whenever its visible to an user.
-        mRecentsView.requestFocus();
+        updatePipUI();
     }
 
     @Override
@@ -333,10 +353,21 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        mPipRecentsOverlayManager.onRecentsResumed();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mPipRecentsOverlayManager.onRecentsPaused();
+    }
+
+    @Override
     protected void onStop() {
         super.onStop();
 
-        mPipManager.onRecentsStopped();
         mIgnoreAltTabRelease = false;
         // Notify that recents is now hidden
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
@@ -480,25 +511,13 @@
 
     private void updatePipUI() {
         if (mPipManager.isPipShown()) {
-            mPipControlsView.setAlpha(0);
-            mPipControlsView.setVisibility(View.VISIBLE);
-            mPipShadeView.setVisibility(View.INVISIBLE);
-            mPipControlsView.setOnChildFocusChangeListener(new View.OnFocusChangeListener() {
-                @Override
-                public void onFocusChange(View v, boolean hasFocus) {
-                    mPipManager.onPipViewFocusChangedInRecents(hasFocus);
-                    if (hasFocus) {
-                        mPipControlsViewFadeInAnimator.start();
-                    } else {
-                        mPipControlsViewFadeOutAnimator.start();
-                    }
-                    mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-                }
-            });
-            mPipShadeView.setVisibility(View.GONE);
+            mPipView.setVisibility(View.VISIBLE);
+            mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
+            mPipView.requestFocus();
         } else {
-            mPipControlsView.setVisibility(View.GONE);
-            mPipShadeView.setVisibility(View.GONE);
+            mPipView.setVisibility(View.GONE);
+            mPipRecentsOverlayManager.removePipRecentsOverlayView();
+            mRecentsFocusAnimationHolder.reset();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
new file mode 100644
index 0000000..864540c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
@@ -0,0 +1,63 @@
+/*
+ * 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);
+    }
+}
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 15ad1f1..3f87611 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.tv.pip;
 
-import android.app.Activity;
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
 import android.view.View;
+import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View.OnFocusChangeListener;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -40,28 +41,29 @@
 /**
  * A view containing PIP controls including fullscreen, close, and media controls.
  */
-public class PipControlsView extends LinearLayout implements PipManager.Listener {
+public class PipControlsView extends LinearLayout {
     /**
      * An interface to listen user action.
      */
-    public interface Listener {
+    public abstract static interface Listener {
         /**
          * Called when an user clicks close PIP button.
          */
-        void onClosed();
-    }
+        public abstract void onClosed();
+    };
 
-    private final PipManager mPipManager = PipManager.getInstance();
     private MediaController mMediaController;
-    private Listener mListener;
 
-    private View mFullButtonView;
-    private View mFullDescriptionView;
-    private View mPlayPauseView;
-    private ImageView mPlayPauseButtonImageView;
-    private TextView mPlayPauseDescriptionTextView;
-    private View mCloseButtonView;
-    private View mCloseDescriptionView;
+    final PipManager mPipManager = PipManager.getInstance();
+    Listener mListener;
+
+    View mFullButtonView;
+    View mFullDescriptionView;
+    View mPlayPauseView;
+    ImageView mPlayPauseButtonImageView;
+    TextView mPlayPauseDescriptionTextView;
+    View mCloseButtonView;
+    View mCloseDescriptionView;
 
     private boolean mHasFocus;
     private OnFocusChangeListener mOnChildFocusChangeListener;
@@ -73,6 +75,13 @@
         }
     };
 
+    private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
+        @Override
+        public void onMediaControllerChanged() {
+            updateMediaController();
+        }
+    };
+
     public PipControlsView(Context context) {
         this(context, null, 0, 0);
     }
@@ -87,6 +96,12 @@
 
     public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        LayoutInflater inflater = (LayoutInflater) getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.tv_pip_controls, this);
+
+        setOrientation(LinearLayout.HORIZONTAL);
+        setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
     }
 
     @Override
@@ -161,13 +176,13 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateMediaController();
-        mPipManager.addListener(this);
+        mPipManager.addMediaListener(mPipMediaListener);
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mPipManager.removeListener(this);
+        mPipManager.removeMediaListener(mPipMediaListener);
         if (mMediaController != null) {
             mMediaController.unregisterCallback(mMediaControllerCallback);
         }
@@ -230,24 +245,4 @@
     public void setListener(Listener listener) {
         mListener = listener;
     }
-
-    @Override
-    public void onPipEntered() { }
-
-    @Override
-    public void onPipActivityClosed() { }
-
-    @Override
-    public void onShowPipMenu() { }
-
-    @Override
-    public void onMoveToFullscreen() { }
-
-    @Override
-    public void onMediaControllerChanged() {
-        updateMediaController();
-    }
-
-    @Override
-    public void onPipResizeAboutToStart() { }
 }
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 68e0883..b5c1f57 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -62,9 +62,27 @@
 
     private static final int MAX_RUNNING_TASKS_COUNT = 10;
 
+    /**
+     * State when there's no PIP.
+     */
     public static final int STATE_NO_PIP = 0;
+    /**
+     * State when PIP is shown with an overlay message on top of it.
+     * This is used as default PIP state.
+     */
     public static final int STATE_PIP_OVERLAY = 1;
+    /**
+     * State when PIP menu dialog is shown.
+     */
     public static final int STATE_PIP_MENU = 2;
+    /**
+     * State when PIP is shown in Recents.
+     */
+    public static final int STATE_PIP_RECENTS = 3;
+    /**
+     * State when PIP is shown in Recents and it's focused to allow an user to control.
+     */
+    public static final int STATE_PIP_RECENTS_FOCUSED = 4;
 
     private static final int TASK_ID_NO_PIP = -1;
     private static final int INVALID_RESOURCE_TYPE = -1;
@@ -90,11 +108,13 @@
     private int mSuspendPipResizingReason;
 
     private Context mContext;
+    private PipRecentsOverlayManager mPipRecentsOverlayManager;
     private IActivityManager mActivityManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
     private final Handler mHandler = new Handler();
     private List<Listener> mListeners = new ArrayList<>();
+    private List<MediaListener> mMediaListeners = new ArrayList<>();
     private Rect mCurrentPipBounds;
     private Rect mPipBounds;
     private Rect mMenuModePipBounds;
@@ -107,9 +127,6 @@
     private MediaController mPipMediaController;
     private boolean mOnboardingShown;
 
-    private boolean mIsRecentsShown;
-    private boolean mIsPipFocusedInRecent;
-
     private final Runnable mResizePinnedStackRunnable = new Runnable() {
         @Override
         public void run() {
@@ -178,6 +195,7 @@
         mOnboardingShown = Prefs.getBoolean(
                 mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
 
+        mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
         mMediaSessionManager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
     }
@@ -231,7 +249,7 @@
     /**
      * Moves the PIPed activity to the fullscreen and closes PIP system UI.
      */
-    public void movePipToFullscreen() {
+    void movePipToFullscreen() {
         mState = STATE_NO_PIP;
         mPipTaskId = TASK_ID_NO_PIP;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -247,8 +265,11 @@
      */
     private void showPipOverlay() {
         if (DEBUG) Log.d(TAG, "showPipOverlay()");
-        mState = STATE_PIP_OVERLAY;
-        PipOverlayActivity.showPipOverlay(mContext);
+        Intent intent = new Intent(mContext, PipOverlayActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(PINNED_STACK_ID);
+        mContext.startActivity(intent, options.toBundle());
     }
 
     /**
@@ -279,8 +300,10 @@
      * Resize the Pip to the appropriate size for the input state.
      * @param state In Pip state also used to determine the new size for the Pip.
      */
-    public void resizePinnedStack(int state) {
+    void resizePinnedStack(int state) {
         if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
+        boolean wasRecentsShown =
+                (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED);
         mState = state;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onPipResizeAboutToStart();
@@ -291,7 +314,6 @@
                             mSuspendPipResizingReason);
             return;
         }
-        int animationDurationMs = -1;
         switch (mState) {
             case STATE_NO_PIP:
                 mCurrentPipBounds = null;
@@ -300,25 +322,24 @@
                 mCurrentPipBounds = mMenuModePipBounds;
                 break;
             case STATE_PIP_OVERLAY:
-                if (mIsRecentsShown) {
-                    if (mCurrentPipBounds == mRecentsFocusedPipBounds
-                            || mCurrentPipBounds == mRecentsFocusedPipBounds) {
-                        animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
-                    }
-                    if (mIsPipFocusedInRecent) {
-                        mCurrentPipBounds = mRecentsFocusedPipBounds;
-                    } else {
-                        mCurrentPipBounds = mRecentsPipBounds;
-                    }
-                } else {
-                    mCurrentPipBounds = mPipBounds;
-                }
+                mCurrentPipBounds = mPipBounds;
+                break;
+            case STATE_PIP_RECENTS:
+                mCurrentPipBounds = mRecentsPipBounds;
+                break;
+            case STATE_PIP_RECENTS_FOCUSED:
+                mCurrentPipBounds = mRecentsFocusedPipBounds;
                 break;
             default:
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
         try {
+            int animationDurationMs = -1;
+            if (wasRecentsShown
+                    && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
+                animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+            }
             mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
                     true, true, true, animationDurationMs);
         } catch (RemoteException e) {
@@ -327,67 +348,18 @@
     }
 
     /**
-     * Returns the current PIP bound for activities to sync their UI with PIP.
+     * Returns the default PIP bound.
      */
     public Rect getPipBounds() {
-        return mCurrentPipBounds;
+        return mPipBounds;
     }
 
     /**
-     * Called when Recents is started.
-     * PIPed activity will be resized accordingly and overlay will show available buttons.
+     * Returns the focused PIP bound while Recents is shown.
+     * This is used to place PIP controls in Recents.
      */
-    public void onRecentsStarted() {
-        mIsRecentsShown = true;
-        mIsPipFocusedInRecent = false;
-        if (mState == STATE_NO_PIP) {
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Called when Recents is stopped.
-     * PIPed activity will be resized accordingly and overlay will hide available buttons.
-     */
-    public void onRecentsStopped() {
-        mIsRecentsShown = false;
-        mIsPipFocusedInRecent = false;
-        if (mState == STATE_NO_PIP) {
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Returns {@code true} if recents is shown.
-     */
-    boolean isRecentsShown() {
-        return mIsRecentsShown;
-    }
-
-    /**
-     * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
-     * is focused.
-     * This only resizes pinned stack so it looks like it's in Recents.
-     * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
-     */
-    public void onPipViewFocusChangedInRecents(boolean hasFocus) {
-        mIsPipFocusedInRecent = hasFocus;
-        if (mState != STATE_PIP_OVERLAY) {
-            Log.w(TAG, "There is no pinned stack to handle focus change.");
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Returns {@code true} if the PIP view in
-     * {@link com.android.systemui.recents.tv.RecentsTvActivity} is focused in Recents.
-     * This API is valid only when {@link isRecentsShown()} returns {@code true}.
-     */
-    boolean isPipViewFocusdInRecents() {
-        return mIsPipFocusedInRecent;
+    public Rect getRecentsFocusedPipBounds() {
+        return mRecentsFocusedPipBounds;
     }
 
     /**
@@ -396,6 +368,10 @@
      */
     private void showPipMenu() {
         if (DEBUG) Log.d(TAG, "showPipMenu()");
+        if (mPipRecentsOverlayManager.isRecentsShown()) {
+            if (DEBUG) Log.d(TAG, "Ignore showing PIP menu");
+            return;
+        }
         mState = STATE_PIP_MENU;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onShowPipMenu();
@@ -405,14 +381,34 @@
         mContext.startActivity(intent);
     }
 
+    /**
+     * Adds a {@link Listener} to PipManager.
+     */
     public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
+    /**
+     * Removes a {@link Listener} from PipManager.
+     */
     public void removeListener(Listener listener) {
         mListeners.remove(listener);
     }
 
+    /**
+     * Adds a {@link MediaListener} to PipManager.
+     */
+    public void addMediaListener(MediaListener listener) {
+        mMediaListeners.add(listener);
+    }
+
+    /**
+     * Removes a {@link MediaListener} from PipManager.
+     */
+    public void removeMediaListener(MediaListener listener) {
+        mMediaListeners.remove(listener);
+    }
+
     private void launchPipOnboardingActivityIfNeeded() {
         if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
             mOnboardingShown = true;
@@ -485,8 +481,8 @@
         }
         if (mPipMediaController != mediaController) {
             mPipMediaController = mediaController;
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onMediaControllerChanged();
+            for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
+                mMediaListeners.get(i).onMediaControllerChanged();
             }
             if (mPipMediaController == null) {
                 mHandler.postDelayed(mClosePipRunnable,
@@ -530,7 +526,7 @@
         return PLAYBACK_STATE_UNAVAILABLE;
     }
 
-    TaskStackListener mTaskStackListener = new TaskStackListener() {
+    private TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
             if (mState != STATE_NO_PIP) {
@@ -582,10 +578,10 @@
             mMediaSessionManager.addOnActiveSessionsChangedListener(
                     mActiveMediaSessionListener, null);
             updateMediaController(mMediaSessionManager.getActiveSessions(null));
-            if (mIsRecentsShown) {
+            if (mPipRecentsOverlayManager.isRecentsShown()) {
                 // If an activity becomes PIPed again after the fullscreen, the Recents is shown
                 // behind so we need to resize the pinned stack and show the correct overlay.
-                resizePinnedStack(STATE_PIP_OVERLAY);
+                resizePinnedStack(STATE_PIP_RECENTS);
             }
             for (int i = mListeners.size() - 1; i >= 0; i--) {
                 mListeners.get(i).onPipEntered();
@@ -604,7 +600,18 @@
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
             switch (mState) {
                 case STATE_PIP_OVERLAY:
-                    showPipOverlay();
+                    if (!mPipRecentsOverlayManager.isRecentsShown()) {
+                        showPipOverlay();
+                        break;
+                    } else {
+                        // This happens only if an activity is PIPed after the Recents is shown.
+                        // See {@link PipRecentsOverlayManager.requestFocus} for more details.
+                        resizePinnedStack(mState);
+                        break;
+                    }
+                case STATE_PIP_RECENTS:
+                case STATE_PIP_RECENTS_FOCUSED:
+                    mPipRecentsOverlayManager.addPipRecentsOverlayView();
                     break;
                 case STATE_PIP_MENU:
                     showPipMenu();
@@ -621,7 +628,7 @@
          * Invoked when an activity is pinned and PIP manager is set corresponding information.
          * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
          * because there's no guarantee for the PIP manager be return relavent information
-         * correctly. (e.g. {@link isPipShown}, {@link getPipBounds})
+         * correctly. (e.g. {@link isPipShown}).
          */
         void onPipEntered();
         /** Invoked when a PIPed activity is closed. */
@@ -632,6 +639,12 @@
         void onMoveToFullscreen();
         /** Invoked when we are above to start resizing the Pip. */
         void onPipResizeAboutToStart();
+    }
+
+    /**
+     * A listener interface to receive change in PIP's media controller
+     */
+    public interface MediaListener {
         /** Invoked when the MediaController on PIPed activity is changed. */
         void onMediaControllerChanged();
     }
@@ -645,4 +658,11 @@
         }
         return sPipManager;
     }
+
+    /**
+     * Gets an instance of {@link PipRecentsOverlayManager}.
+     */
+    public PipRecentsOverlayManager getPipRecentsOverlayManager() {
+        return mPipRecentsOverlayManager;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index ea9275f..c54e73a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -20,12 +20,6 @@
 import android.os.Bundle;
 
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.Recents;
-
-import static android.content.pm.PackageManager.FEATURE_LEANBACK;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 /**
  * Activity to show the PIP menu to control PIP.
@@ -36,7 +30,6 @@
     private final PipManager mPipManager = PipManager.getInstance();
 
     private PipControlsView mPipControlsView;
-    private boolean mPipMovedToFullscreen;
 
     @Override
     protected void onCreate(Bundle bundle) {
@@ -47,17 +40,10 @@
         mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
     }
 
-    private void restorePipAndFinish() {
-        if (!mPipMovedToFullscreen) {
-            mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
-        }
-        finish();
-    }
-
     @Override
     public void onPause() {
         super.onPause();
-        restorePipAndFinish();
+        finish();
     }
 
     @Override
@@ -69,11 +55,6 @@
     }
 
     @Override
-    public void onBackPressed() {
-        restorePipAndFinish();
-    }
-
-    @Override
     public void onPipEntered() { }
 
     @Override
@@ -86,31 +67,13 @@
 
     @Override
     public void onMoveToFullscreen() {
-        mPipMovedToFullscreen = true;
         finish();
     }
 
     @Override
-    public void onMediaControllerChanged() { }
-
-    @Override
     public void onPipResizeAboutToStart() {
         finish();
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
     }
-
-    @Override
-    public void finish() {
-        super.finish();
-        if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) {
-            SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
-            for (int i = services.length - 1; i >= 0; i--) {
-                if (services[i] instanceof Recents) {
-                    ((Recents) services[i]).showRecents(false, null);
-                    break;
-                }
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 79daf3d..86ceff4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -86,7 +86,4 @@
 
     @Override
     public void onPipResizeAboutToStart() { }
-
-    @Override
-    public void onMediaControllerChanged() { }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 12cb4cd..5472ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -35,14 +35,6 @@
 public class PipOverlayActivity extends Activity implements PipManager.Listener {
     private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
 
-    /**
-     * The single instance of PipOverlayActivity to prevent it from restarting.
-     * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
-     * restarted. It's because the activity may be started by the Launcher or an intent again,
-     * but we don't want do so for the PipOverlayActivity.
-     */
-    private static PipOverlayActivity sPipOverlayActivity;
-
     private final PipManager mPipManager = PipManager.getInstance();
     private final Handler mHandler = new Handler();
     private View mGuideOverlayView;
@@ -54,47 +46,17 @@
         }
     };
 
-    /**
-     * Launches the PIP overlay. This should be only called on the main thread.
-     */
-    public static void showPipOverlay(Context context) {
-        if (sPipOverlayActivity == null) {
-            Intent intent = new Intent(context, PipOverlayActivity.class);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchStackId(PINNED_STACK_ID);
-            context.startActivity(intent, options.toBundle());
-        }
-    }
-
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_overlay);
         mGuideOverlayView = findViewById(R.id.guide_overlay);
-        mGuideButtonsView = findViewById(R.id.guide_buttons);
-        mGuideButtonPlayPauseImageView = (ImageView) findViewById(R.id.guide_button_play_pause);
         mPipManager.addListener(this);
-
-        sPipOverlayActivity = this;
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-        // TODO: Implement animation for this
-        if (mPipManager.isRecentsShown()) {
-            mGuideOverlayView.setVisibility(View.GONE);
-            if (mPipManager.isPipViewFocusdInRecents()) {
-                mGuideButtonsView.setVisibility(View.GONE);
-            } else {
-                mGuideButtonsView.setVisibility(View.VISIBLE);
-                updateGuideButtonsView();
-            }
-        } else {
-            mGuideOverlayView.setVisibility(View.VISIBLE);
-            mGuideButtonsView.setVisibility(View.GONE);
-        }
         mHandler.removeCallbacks(mHideGuideOverlayRunnable);
         mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
     }
@@ -109,7 +71,6 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        sPipOverlayActivity = null;
         mHandler.removeCallbacksAndMessages(null);
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
@@ -140,32 +101,4 @@
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
     }
-
-    @Override
-    public void onMediaControllerChanged() {
-        updateGuideButtonsView();
-    }
-
-    @Override
-    public void finish() {
-        sPipOverlayActivity = null;
-        super.finish();
-    }
-
-    private void updateGuideButtonsView() {
-        switch (mPipManager.getPlaybackState()) {
-            case PipManager.PLAYBACK_STATE_PLAYING:
-                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
-                mGuideButtonPlayPauseImageView.setImageResource(R.drawable.ic_pause_white_24dp);
-                break;
-            case PipManager.PLAYBACK_STATE_PAUSED:
-                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
-                mGuideButtonPlayPauseImageView.setImageResource(
-                        R.drawable.ic_play_arrow_white_24dp);
-                break;
-            case PipManager.PLAYBACK_STATE_UNAVAILABLE:
-                mGuideButtonPlayPauseImageView.setVisibility(View.GONE);
-                break;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
new file mode 100644
index 0000000..8b8c105
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
@@ -0,0 +1,169 @@
+/*
+ * 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.tv.pip;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.systemui.R;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+/**
+ * An extended version of {@link PipControlsView} that supports animation in Recents.
+ */
+public class PipRecentsControlsView extends PipControlsView {
+    /**
+     * An interface to listen user action.
+     */
+    public interface Listener extends PipControlsView.Listener {
+        /**
+         * Called when an user presses BACK key and up.
+         */
+        abstract void onBackPressed();
+    }
+
+    private AnimatorSet mFocusGainAnimatorSet;
+    private AnimatorSet mFocusLoseAnimatorSet;
+
+    public PipRecentsControlsView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public PipRecentsControlsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public PipRecentsControlsView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+
+        int buttonsFocusGainAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_gain_animation;
+        int textFocusGainAnim = R.anim.tv_pip_controls_text_in_recents_focus_gain_animation;
+        mFocusGainAnimatorSet = new AnimatorSet();
+        mFocusGainAnimatorSet.playTogether(
+                loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_gain_animation),
+                loadAnimator(mFullButtonView,buttonsFocusGainAnim),
+                loadAnimator(mPlayPauseButtonImageView, buttonsFocusGainAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusGainAnim),
+                loadAnimator(mFullDescriptionView, textFocusGainAnim),
+                loadAnimator(mPlayPauseDescriptionTextView, textFocusGainAnim),
+                loadAnimator(mCloseDescriptionView, textFocusGainAnim));
+
+        int buttonsFocusLoseAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_lose_animation;
+        int textFocusLoseAnim = R.anim.tv_pip_controls_text_in_recents_focus_lose_animation;
+        mFocusLoseAnimatorSet = new AnimatorSet();
+        mFocusLoseAnimatorSet.playTogether(
+                loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_lose_animation),
+                loadAnimator(mFullButtonView, buttonsFocusLoseAnim),
+                loadAnimator(mPlayPauseButtonImageView, buttonsFocusLoseAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusLoseAnim),
+                loadAnimator(mFullDescriptionView, textFocusLoseAnim),
+                loadAnimator(mPlayPauseDescriptionTextView, textFocusLoseAnim),
+                loadAnimator(mCloseDescriptionView, textFocusLoseAnim));
+
+        Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
+        int pipControlsMarginTop = getContext().getResources().getDimensionPixelSize(
+                R.dimen.recents_tv_pip_controls_margin_top);
+        setPadding(0, pipBounds.bottom + pipControlsMarginTop, 0, 0);
+    }
+
+    private Animator loadAnimator(View view, int animatorResId) {
+        Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId);
+        animator.setTarget(view);
+        return animator;
+    }
+
+    /**
+     * Starts focus gaining animation.
+     */
+    public void startFocusGainAnimation() {
+        if (mFocusLoseAnimatorSet.isStarted()) {
+            mFocusLoseAnimatorSet.cancel();
+        }
+        mFocusGainAnimatorSet.start();
+    }
+
+    /**
+     * Starts focus losing animation.
+     */
+    public void startFocusLoseAnimation() {
+        if (mFocusGainAnimatorSet.isStarted()) {
+            mFocusGainAnimatorSet.cancel();
+        }
+        mFocusLoseAnimatorSet.start();
+    }
+
+    /**
+     * Resets the view to the initial state. (i.e. end of the focus gain)
+     */
+    public void reset() {
+        if (mFocusGainAnimatorSet.isStarted()) {
+            mFocusGainAnimatorSet.cancel();
+        }
+        if (mFocusLoseAnimatorSet.isStarted()) {
+            mFocusLoseAnimatorSet.cancel();
+        }
+
+        // Reset to initial state (i.e. end of focused)
+        requestFocus();
+        setTranslationY(0);
+        setScaleXY(mFullButtonView, 1);
+        setScaleXY(mPlayPauseButtonImageView, 1);
+        setScaleXY(mCloseButtonView, 1);
+        mFullDescriptionView.setAlpha(1);
+        mPlayPauseDescriptionTextView.setAlpha(1);
+        mCloseDescriptionView.setAlpha(1);
+    }
+
+    private void setScaleXY(View view, float scale) {
+        view.setScaleX(scale);
+        view.setScaleY(scale);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (!event.isCanceled()
+                && event.getKeyCode() == KeyEvent.KEYCODE_BACK
+                && event.getAction() == KeyEvent.ACTION_UP) {
+            if (mListener != null) {
+                ((PipRecentsControlsView.Listener) mListener).onBackPressed();
+            }
+            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
new file mode 100644
index 0000000..b90b727
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -0,0 +1,207 @@
+/*
+ * 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.tv.pip;
+
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+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;
+
+public class PipRecentsOverlayManager {
+    private static final String TAG = "PipRecentsOverlayManager";
+
+    public interface Callback {
+        void onClosed();
+        void onBackPressed();
+        void onRecentsFocused();
+    }
+
+    private final PipManager mPipManager = PipManager.getInstance();
+    private final WindowManager mWindowManager;
+    private final View mOverlayView;
+    private final PipRecentsControlsView mPipControlsView;
+    private final View mRecentsView;
+
+    private final LayoutParams mPipRecentsControlsViewLayoutParams;
+    private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
+
+    private boolean mIsPipRecentsOverlayShown;
+    private boolean mIsRecentsShown;
+    private boolean mIsPipFocusedInRecent;
+    private Callback mCallback;
+    private PipRecentsControlsView.Listener mPipControlsViewListener =
+            new PipRecentsControlsView.Listener() {
+                @Override
+                public void onClosed() {
+                    if (mCallback != null) {
+                        mCallback.onClosed();
+                    }
+                }
+
+                @Override
+                public void onBackPressed() {
+                    if (mCallback != null) {
+                        mCallback.onBackPressed();
+                    }
+                }
+            };
+
+    PipRecentsOverlayManager(Context context) {
+        mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
+        mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls);
+        mRecentsView = mOverlayView.findViewById(R.id.recents);
+        mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    clearFocus();
+                }
+            }
+        });
+
+        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);
+    }
+
+    /**
+     * Add Recents overlay view.
+     * This is expected to be called after the PIP animation is over.
+     */
+    void addPipRecentsOverlayView() {
+        if (mIsPipRecentsOverlayShown) {
+            return;
+        }
+        mIsPipRecentsOverlayShown = true;
+        mIsPipFocusedInRecent = true;
+        mPipControlsView.reset();
+        mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+    }
+
+    /**
+     * Remove Recents overlay view.
+     * This should be called when Recents or PIP is closed.
+     */
+    public void removePipRecentsOverlayView() {
+        if (!mIsPipRecentsOverlayShown) {
+            return;
+        }
+        mWindowManager.removeView(mOverlayView);
+        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 hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+     */
+    public void requestFocus(boolean hasRecentsFocusable) {
+        if (!mIsRecentsShown || mIsPipFocusedInRecent) {
+            return;
+        }
+        mIsPipFocusedInRecent = true;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+
+        mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+        mPipControlsView.requestFocus();
+        mPipControlsView.startFocusGainAnimation();
+        mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * 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}.
+     */
+    private void clearFocus() {
+        if (!mIsRecentsShown || !mIsPipFocusedInRecent) {
+            return;
+        }
+        mIsPipFocusedInRecent = false;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
+        mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
+        mPipControlsView.startFocusLoseAnimation();
+        if (mCallback != null) {
+            mCallback.onRecentsFocused();
+        }
+    }
+
+    public void setCallback(Callback listener) {
+        mCallback = listener;
+        mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null);
+    }
+
+    /**
+     * Called when Recents is resumed.
+     * PIPed activity will be resized accordingly and overlay will show available buttons.
+     */
+    public void onRecentsResumed() {
+        if (!mPipManager.isPipShown()) {
+            return;
+        }
+        mIsRecentsShown = true;
+        mIsPipFocusedInRecent = true;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+        // Overlay view will be added after the resize animation ends, if any.
+    }
+
+    /**
+     * Called when Recents is paused.
+     * PIPed activity will be resized accordingly and overlay will hide available buttons.
+     */
+    public void onRecentsPaused() {
+        mIsRecentsShown = false;
+        mIsPipFocusedInRecent = false;
+        removePipRecentsOverlayView();
+
+        if (mPipManager.isPipShown()) {
+            mPipManager.resizePinnedStack(STATE_PIP_OVERLAY);
+        }
+    }
+
+    /**
+     * Returns {@code true} if recents is shown.
+     */
+    boolean isRecentsShown() {
+        return mIsRecentsShown;
+    }
+}