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;
+ }
+}