Merge "PIP: Apply the animation spec for the PIP in Recents" into nyc-dev
This commit is contained in:
@@ -17,13 +17,13 @@
|
|||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="translationY"
|
android:propertyName="scaleX"
|
||||||
android:valueTo="10dp"
|
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" />
|
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="alpha"
|
android:propertyName="scaleY"
|
||||||
android:valueTo="1.0"
|
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" />
|
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
||||||
</set>
|
</set>
|
||||||
@@ -17,13 +17,13 @@
|
|||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="translationY"
|
android:propertyName="scaleX"
|
||||||
android:valueTo="0dp"
|
android:valueTo="0.7"
|
||||||
android:interpolator="@android:interpolator/fast_out_linear_in"
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
||||||
<objectAnimator
|
<objectAnimator
|
||||||
android:propertyName="alpha"
|
android:propertyName="scaleY"
|
||||||
android:valueTo="0.0"
|
android:valueTo="0.7"
|
||||||
android:interpolator="@android:interpolator/fast_out_linear_in"
|
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||||
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
android:duration="@integer/recents_tv_pip_focus_anim_duration" />
|
||||||
</set>
|
</set>
|
||||||
@@ -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" />
|
||||||
@@ -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" />
|
||||||
@@ -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" />
|
||||||
@@ -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" />
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
<size
|
<size
|
||||||
android:width="36dp"
|
android:width="34dp"
|
||||||
android:height="36dp" />
|
android:height="34dp" />
|
||||||
<solid
|
<solid
|
||||||
android:color="#4DFFFFFF" />
|
android:color="#4DFFFFFF" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
@@ -31,18 +31,12 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:layoutDirection="rtl" />
|
android:layoutDirection="rtl" />
|
||||||
|
|
||||||
|
<!-- Placeholder view to give focus to the PIP menus. -->
|
||||||
<View
|
<View
|
||||||
android:id="@+id/pip_shade"
|
android:id="@+id/pip"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:visibility="gone"
|
android:focusable="true"
|
||||||
android:background="#76000000" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<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" />
|
|
||||||
|
|
||||||
</com.android.systemui.recents.tv.views.RecentsTvView>
|
</com.android.systemui.recents.tv.views.RecentsTvView>
|
||||||
|
|||||||
@@ -17,13 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<com.android.systemui.tv.pip.PipControlsView
|
<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge 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">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
@@ -98,4 +93,4 @@
|
|||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="#EEEEEE" />
|
android:textColor="#EEEEEE" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.android.systemui.tv.pip.PipControlsView>
|
</merge>
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
android:gravity="top|center_horizontal"
|
android:gravity="top|center_horizontal"
|
||||||
android:clipChildren="false">
|
android:clipChildren="false">
|
||||||
|
|
||||||
<include
|
<com.android.systemui.tv.pip.PipControlsView
|
||||||
layout="@layout/tv_pip_controls"
|
android:id="@+id/pip_controls"
|
||||||
android:clipChildren="false" />
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -38,25 +38,4 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:text="@string/pip_hold_home" />
|
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>
|
</RelativeLayout>
|
||||||
|
|||||||
33
packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
Normal file
33
packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
Normal file
@@ -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>
|
||||||
@@ -38,9 +38,6 @@
|
|||||||
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
|
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
|
||||||
<dimen name="recents_tv_selected_item_z_delta">10dp</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 -->
|
<!-- Values for text on recents cards on tv -->
|
||||||
<dimen name="recents_tv_title_text_size">12sp</dimen>
|
<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_icon_bottom_margin">1dip</dimen>
|
||||||
<dimen name="recents_tv_dismiss_text_size">12sp</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>
|
</resources>
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.recents.tv;
|
package com.android.systemui.recents.tv;
|
||||||
|
|
||||||
import android.animation.AnimatorInflater;
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityOptions;
|
import android.app.ActivityOptions;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -55,11 +53,12 @@ import com.android.systemui.recents.model.RecentsTaskLoadPlan;
|
|||||||
import com.android.systemui.recents.model.RecentsTaskLoader;
|
import com.android.systemui.recents.model.RecentsTaskLoader;
|
||||||
import com.android.systemui.recents.model.Task;
|
import com.android.systemui.recents.model.Task;
|
||||||
import com.android.systemui.recents.model.TaskStack;
|
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.RecentsTvView;
|
||||||
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
|
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
|
||||||
import com.android.systemui.statusbar.BaseStatusBar;
|
import com.android.systemui.statusbar.BaseStatusBar;
|
||||||
import com.android.systemui.tv.pip.PipManager;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -80,15 +79,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
private boolean mIgnoreAltTabRelease;
|
private boolean mIgnoreAltTabRelease;
|
||||||
|
|
||||||
private RecentsTvView mRecentsView;
|
private RecentsTvView mRecentsView;
|
||||||
private PipControlsView mPipControlsView;
|
private FocusAnimationHolder mRecentsFocusAnimationHolder;
|
||||||
private View mPipShadeView;
|
private View mPipView;
|
||||||
private AnimatorSet mPipControlsViewFadeInAnimator;
|
|
||||||
private AnimatorSet mPipControlsViewFadeOutAnimator;
|
|
||||||
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
|
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
|
||||||
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
|
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
|
||||||
|
|
||||||
private PipManager mPipManager;
|
private final PipManager mPipManager = PipManager.getInstance();
|
||||||
private PipManager.Listener mPipListener = new PipManager.Listener() {
|
private final PipManager.Listener mPipListener = new PipManager.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPipEntered() {
|
public void onPipEntered() {
|
||||||
updatePipUI();
|
updatePipUI();
|
||||||
@@ -113,10 +110,38 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPipResizeAboutToStart() { }
|
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
|
* A common Runnable to finish Recents by launching Home with an animation depending on the
|
||||||
@@ -248,7 +273,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mPipManager = PipManager.getInstance();
|
mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
|
||||||
|
|
||||||
// Register this activity with the event bus
|
// Register this activity with the event bus
|
||||||
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
|
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
|
||||||
@@ -263,21 +288,19 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||||
mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
|
mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
|
||||||
mPipControlsView.setListener(new PipControlsView.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onClosed() {
|
|
||||||
dismissRecentsToLaunchTargetTaskOrHome();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mPipShadeView = findViewById(R.id.pip_shade);
|
|
||||||
|
|
||||||
mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
|
mPipView = findViewById(R.id.pip);
|
||||||
R.anim.tv_pip_controls_fade_in);
|
// Place mPipView at the PIP bounds for fine tuned focus handling.
|
||||||
mPipControlsViewFadeInAnimator.setTarget(mPipControlsView);
|
Rect pipBounds = mPipManager.getPipBounds();
|
||||||
mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
|
LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
|
||||||
R.anim.tv_pip_controls_fade_out);
|
lp.width = pipBounds.width();
|
||||||
mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView);
|
lp.height = pipBounds.height();
|
||||||
|
lp.leftMargin = pipBounds.left;
|
||||||
|
lp.topMargin = pipBounds.top;
|
||||||
|
mPipView.setLayoutParams(lp);
|
||||||
|
|
||||||
|
mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
|
||||||
|
|
||||||
getWindow().getAttributes().privateFlags |=
|
getWindow().getAttributes().privateFlags |=
|
||||||
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
|
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
|
||||||
@@ -289,7 +312,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||||
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
|
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
|
||||||
|
|
||||||
updatePipUI();
|
|
||||||
mPipManager.addListener(mPipListener);
|
mPipManager.addListener(mPipListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,9 +343,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
SystemServicesProxy ssp = Recents.getSystemServices();
|
SystemServicesProxy ssp = Recents.getSystemServices();
|
||||||
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
|
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
|
||||||
|
|
||||||
mPipManager.onRecentsStarted();
|
updatePipUI();
|
||||||
// Give focus to the recents row whenever its visible to an user.
|
|
||||||
mRecentsView.requestFocus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -332,11 +352,22 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
|
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mPipRecentsOverlayManager.onRecentsResumed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
mPipRecentsOverlayManager.onRecentsPaused();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
|
||||||
mPipManager.onRecentsStopped();
|
|
||||||
mIgnoreAltTabRelease = false;
|
mIgnoreAltTabRelease = false;
|
||||||
// Notify that recents is now hidden
|
// Notify that recents is now hidden
|
||||||
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
|
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
|
||||||
@@ -480,25 +511,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
|||||||
|
|
||||||
private void updatePipUI() {
|
private void updatePipUI() {
|
||||||
if (mPipManager.isPipShown()) {
|
if (mPipManager.isPipShown()) {
|
||||||
mPipControlsView.setAlpha(0);
|
mPipView.setVisibility(View.VISIBLE);
|
||||||
mPipControlsView.setVisibility(View.VISIBLE);
|
mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
|
||||||
mPipShadeView.setVisibility(View.INVISIBLE);
|
mPipView.requestFocus();
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
mPipControlsView.setVisibility(View.GONE);
|
mPipView.setVisibility(View.GONE);
|
||||||
mPipShadeView.setVisibility(View.GONE);
|
mPipRecentsOverlayManager.removePipRecentsOverlayView();
|
||||||
|
mRecentsFocusAnimationHolder.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
package com.android.systemui.tv.pip;
|
package com.android.systemui.tv.pip;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.session.MediaController;
|
import android.media.session.MediaController;
|
||||||
import android.media.session.PlaybackState;
|
import android.media.session.PlaybackState;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View.OnFocusChangeListener;
|
import android.view.View.OnFocusChangeListener;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -40,28 +41,29 @@ import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
|
|||||||
/**
|
/**
|
||||||
* A view containing PIP controls including fullscreen, close, and media controls.
|
* 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.
|
* An interface to listen user action.
|
||||||
*/
|
*/
|
||||||
public interface Listener {
|
public abstract static interface Listener {
|
||||||
/**
|
/**
|
||||||
* Called when an user clicks close PIP button.
|
* Called when an user clicks close PIP button.
|
||||||
*/
|
*/
|
||||||
void onClosed();
|
public abstract void onClosed();
|
||||||
}
|
};
|
||||||
|
|
||||||
private final PipManager mPipManager = PipManager.getInstance();
|
|
||||||
private MediaController mMediaController;
|
private MediaController mMediaController;
|
||||||
private Listener mListener;
|
|
||||||
|
|
||||||
private View mFullButtonView;
|
final PipManager mPipManager = PipManager.getInstance();
|
||||||
private View mFullDescriptionView;
|
Listener mListener;
|
||||||
private View mPlayPauseView;
|
|
||||||
private ImageView mPlayPauseButtonImageView;
|
View mFullButtonView;
|
||||||
private TextView mPlayPauseDescriptionTextView;
|
View mFullDescriptionView;
|
||||||
private View mCloseButtonView;
|
View mPlayPauseView;
|
||||||
private View mCloseDescriptionView;
|
ImageView mPlayPauseButtonImageView;
|
||||||
|
TextView mPlayPauseDescriptionTextView;
|
||||||
|
View mCloseButtonView;
|
||||||
|
View mCloseDescriptionView;
|
||||||
|
|
||||||
private boolean mHasFocus;
|
private boolean mHasFocus;
|
||||||
private OnFocusChangeListener mOnChildFocusChangeListener;
|
private OnFocusChangeListener mOnChildFocusChangeListener;
|
||||||
@@ -73,6 +75,13 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
|
||||||
|
@Override
|
||||||
|
public void onMediaControllerChanged() {
|
||||||
|
updateMediaController();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public PipControlsView(Context context) {
|
public PipControlsView(Context context) {
|
||||||
this(context, null, 0, 0);
|
this(context, null, 0, 0);
|
||||||
}
|
}
|
||||||
@@ -87,6 +96,12 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener
|
|||||||
|
|
||||||
public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, 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
|
@Override
|
||||||
@@ -161,13 +176,13 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener
|
|||||||
public void onAttachedToWindow() {
|
public void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
updateMediaController();
|
updateMediaController();
|
||||||
mPipManager.addListener(this);
|
mPipManager.addMediaListener(mPipMediaListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromWindow() {
|
public void onDetachedFromWindow() {
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
mPipManager.removeListener(this);
|
mPipManager.removeMediaListener(mPipMediaListener);
|
||||||
if (mMediaController != null) {
|
if (mMediaController != null) {
|
||||||
mMediaController.unregisterCallback(mMediaControllerCallback);
|
mMediaController.unregisterCallback(mMediaControllerCallback);
|
||||||
}
|
}
|
||||||
@@ -230,24 +245,4 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener
|
|||||||
public void setListener(Listener listener) {
|
public void setListener(Listener listener) {
|
||||||
mListener = 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() { }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,27 @@ public class PipManager {
|
|||||||
|
|
||||||
private static final int MAX_RUNNING_TASKS_COUNT = 10;
|
private static final int MAX_RUNNING_TASKS_COUNT = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State when there's no PIP.
|
||||||
|
*/
|
||||||
public static final int STATE_NO_PIP = 0;
|
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;
|
public static final int STATE_PIP_OVERLAY = 1;
|
||||||
|
/**
|
||||||
|
* State when PIP menu dialog is shown.
|
||||||
|
*/
|
||||||
public static final int STATE_PIP_MENU = 2;
|
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 TASK_ID_NO_PIP = -1;
|
||||||
private static final int INVALID_RESOURCE_TYPE = -1;
|
private static final int INVALID_RESOURCE_TYPE = -1;
|
||||||
@@ -90,11 +108,13 @@ public class PipManager {
|
|||||||
private int mSuspendPipResizingReason;
|
private int mSuspendPipResizingReason;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
private PipRecentsOverlayManager mPipRecentsOverlayManager;
|
||||||
private IActivityManager mActivityManager;
|
private IActivityManager mActivityManager;
|
||||||
private MediaSessionManager mMediaSessionManager;
|
private MediaSessionManager mMediaSessionManager;
|
||||||
private int mState = STATE_NO_PIP;
|
private int mState = STATE_NO_PIP;
|
||||||
private final Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
private List<Listener> mListeners = new ArrayList<>();
|
private List<Listener> mListeners = new ArrayList<>();
|
||||||
|
private List<MediaListener> mMediaListeners = new ArrayList<>();
|
||||||
private Rect mCurrentPipBounds;
|
private Rect mCurrentPipBounds;
|
||||||
private Rect mPipBounds;
|
private Rect mPipBounds;
|
||||||
private Rect mMenuModePipBounds;
|
private Rect mMenuModePipBounds;
|
||||||
@@ -107,9 +127,6 @@ public class PipManager {
|
|||||||
private MediaController mPipMediaController;
|
private MediaController mPipMediaController;
|
||||||
private boolean mOnboardingShown;
|
private boolean mOnboardingShown;
|
||||||
|
|
||||||
private boolean mIsRecentsShown;
|
|
||||||
private boolean mIsPipFocusedInRecent;
|
|
||||||
|
|
||||||
private final Runnable mResizePinnedStackRunnable = new Runnable() {
|
private final Runnable mResizePinnedStackRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -178,6 +195,7 @@ public class PipManager {
|
|||||||
mOnboardingShown = Prefs.getBoolean(
|
mOnboardingShown = Prefs.getBoolean(
|
||||||
mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
|
mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
|
||||||
|
|
||||||
|
mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
|
||||||
mMediaSessionManager =
|
mMediaSessionManager =
|
||||||
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
||||||
}
|
}
|
||||||
@@ -231,7 +249,7 @@ public class PipManager {
|
|||||||
/**
|
/**
|
||||||
* Moves the PIPed activity to the fullscreen and closes PIP system UI.
|
* Moves the PIPed activity to the fullscreen and closes PIP system UI.
|
||||||
*/
|
*/
|
||||||
public void movePipToFullscreen() {
|
void movePipToFullscreen() {
|
||||||
mState = STATE_NO_PIP;
|
mState = STATE_NO_PIP;
|
||||||
mPipTaskId = TASK_ID_NO_PIP;
|
mPipTaskId = TASK_ID_NO_PIP;
|
||||||
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
||||||
@@ -247,8 +265,11 @@ public class PipManager {
|
|||||||
*/
|
*/
|
||||||
private void showPipOverlay() {
|
private void showPipOverlay() {
|
||||||
if (DEBUG) Log.d(TAG, "showPipOverlay()");
|
if (DEBUG) Log.d(TAG, "showPipOverlay()");
|
||||||
mState = STATE_PIP_OVERLAY;
|
Intent intent = new Intent(mContext, PipOverlayActivity.class);
|
||||||
PipOverlayActivity.showPipOverlay(mContext);
|
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 @@ public class PipManager {
|
|||||||
* Resize the Pip to the appropriate size for the input state.
|
* 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.
|
* @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);
|
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
|
||||||
|
boolean wasRecentsShown =
|
||||||
|
(mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED);
|
||||||
mState = state;
|
mState = state;
|
||||||
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
||||||
mListeners.get(i).onPipResizeAboutToStart();
|
mListeners.get(i).onPipResizeAboutToStart();
|
||||||
@@ -291,7 +314,6 @@ public class PipManager {
|
|||||||
mSuspendPipResizingReason);
|
mSuspendPipResizingReason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int animationDurationMs = -1;
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case STATE_NO_PIP:
|
case STATE_NO_PIP:
|
||||||
mCurrentPipBounds = null;
|
mCurrentPipBounds = null;
|
||||||
@@ -300,25 +322,24 @@ public class PipManager {
|
|||||||
mCurrentPipBounds = mMenuModePipBounds;
|
mCurrentPipBounds = mMenuModePipBounds;
|
||||||
break;
|
break;
|
||||||
case STATE_PIP_OVERLAY:
|
case STATE_PIP_OVERLAY:
|
||||||
if (mIsRecentsShown) {
|
mCurrentPipBounds = mPipBounds;
|
||||||
if (mCurrentPipBounds == mRecentsFocusedPipBounds
|
break;
|
||||||
|| mCurrentPipBounds == mRecentsFocusedPipBounds) {
|
case STATE_PIP_RECENTS:
|
||||||
animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
|
mCurrentPipBounds = mRecentsPipBounds;
|
||||||
}
|
break;
|
||||||
if (mIsPipFocusedInRecent) {
|
case STATE_PIP_RECENTS_FOCUSED:
|
||||||
mCurrentPipBounds = mRecentsFocusedPipBounds;
|
mCurrentPipBounds = mRecentsFocusedPipBounds;
|
||||||
} else {
|
|
||||||
mCurrentPipBounds = mRecentsPipBounds;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mCurrentPipBounds = mPipBounds;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mCurrentPipBounds = mPipBounds;
|
mCurrentPipBounds = mPipBounds;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
int animationDurationMs = -1;
|
||||||
|
if (wasRecentsShown
|
||||||
|
&& (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
|
||||||
|
animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
|
||||||
|
}
|
||||||
mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
|
mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
|
||||||
true, true, true, animationDurationMs);
|
true, true, true, animationDurationMs);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -327,67 +348,18 @@ public class PipManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current PIP bound for activities to sync their UI with PIP.
|
* Returns the default PIP bound.
|
||||||
*/
|
*/
|
||||||
public Rect getPipBounds() {
|
public Rect getPipBounds() {
|
||||||
return mCurrentPipBounds;
|
return mPipBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when Recents is started.
|
* Returns the focused PIP bound while Recents is shown.
|
||||||
* PIPed activity will be resized accordingly and overlay will show available buttons.
|
* This is used to place PIP controls in Recents.
|
||||||
*/
|
*/
|
||||||
public void onRecentsStarted() {
|
public Rect getRecentsFocusedPipBounds() {
|
||||||
mIsRecentsShown = true;
|
return mRecentsFocusedPipBounds;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,6 +368,10 @@ public class PipManager {
|
|||||||
*/
|
*/
|
||||||
private void showPipMenu() {
|
private void showPipMenu() {
|
||||||
if (DEBUG) Log.d(TAG, "showPipMenu()");
|
if (DEBUG) Log.d(TAG, "showPipMenu()");
|
||||||
|
if (mPipRecentsOverlayManager.isRecentsShown()) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Ignore showing PIP menu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
mState = STATE_PIP_MENU;
|
mState = STATE_PIP_MENU;
|
||||||
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
for (int i = mListeners.size() - 1; i >= 0; --i) {
|
||||||
mListeners.get(i).onShowPipMenu();
|
mListeners.get(i).onShowPipMenu();
|
||||||
@@ -405,14 +381,34 @@ public class PipManager {
|
|||||||
mContext.startActivity(intent);
|
mContext.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link Listener} to PipManager.
|
||||||
|
*/
|
||||||
public void addListener(Listener listener) {
|
public void addListener(Listener listener) {
|
||||||
mListeners.add(listener);
|
mListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a {@link Listener} from PipManager.
|
||||||
|
*/
|
||||||
public void removeListener(Listener listener) {
|
public void removeListener(Listener listener) {
|
||||||
mListeners.remove(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() {
|
private void launchPipOnboardingActivityIfNeeded() {
|
||||||
if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
|
if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
|
||||||
mOnboardingShown = true;
|
mOnboardingShown = true;
|
||||||
@@ -485,8 +481,8 @@ public class PipManager {
|
|||||||
}
|
}
|
||||||
if (mPipMediaController != mediaController) {
|
if (mPipMediaController != mediaController) {
|
||||||
mPipMediaController = mediaController;
|
mPipMediaController = mediaController;
|
||||||
for (int i = mListeners.size() - 1; i >= 0; i--) {
|
for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
|
||||||
mListeners.get(i).onMediaControllerChanged();
|
mMediaListeners.get(i).onMediaControllerChanged();
|
||||||
}
|
}
|
||||||
if (mPipMediaController == null) {
|
if (mPipMediaController == null) {
|
||||||
mHandler.postDelayed(mClosePipRunnable,
|
mHandler.postDelayed(mClosePipRunnable,
|
||||||
@@ -530,7 +526,7 @@ public class PipManager {
|
|||||||
return PLAYBACK_STATE_UNAVAILABLE;
|
return PLAYBACK_STATE_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskStackListener mTaskStackListener = new TaskStackListener() {
|
private TaskStackListener mTaskStackListener = new TaskStackListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTaskStackChanged() {
|
public void onTaskStackChanged() {
|
||||||
if (mState != STATE_NO_PIP) {
|
if (mState != STATE_NO_PIP) {
|
||||||
@@ -582,10 +578,10 @@ public class PipManager {
|
|||||||
mMediaSessionManager.addOnActiveSessionsChangedListener(
|
mMediaSessionManager.addOnActiveSessionsChangedListener(
|
||||||
mActiveMediaSessionListener, null);
|
mActiveMediaSessionListener, null);
|
||||||
updateMediaController(mMediaSessionManager.getActiveSessions(null));
|
updateMediaController(mMediaSessionManager.getActiveSessions(null));
|
||||||
if (mIsRecentsShown) {
|
if (mPipRecentsOverlayManager.isRecentsShown()) {
|
||||||
// If an activity becomes PIPed again after the fullscreen, the Recents is shown
|
// 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.
|
// 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--) {
|
for (int i = mListeners.size() - 1; i >= 0; i--) {
|
||||||
mListeners.get(i).onPipEntered();
|
mListeners.get(i).onPipEntered();
|
||||||
@@ -604,7 +600,18 @@ public class PipManager {
|
|||||||
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
|
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case STATE_PIP_OVERLAY:
|
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;
|
break;
|
||||||
case STATE_PIP_MENU:
|
case STATE_PIP_MENU:
|
||||||
showPipMenu();
|
showPipMenu();
|
||||||
@@ -621,7 +628,7 @@ public class PipManager {
|
|||||||
* Invoked when an activity is pinned and PIP manager is set corresponding information.
|
* Invoked when an activity is pinned and PIP manager is set corresponding information.
|
||||||
* Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
|
* Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
|
||||||
* because there's no guarantee for the PIP manager be return relavent information
|
* 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();
|
void onPipEntered();
|
||||||
/** Invoked when a PIPed activity is closed. */
|
/** Invoked when a PIPed activity is closed. */
|
||||||
@@ -632,6 +639,12 @@ public class PipManager {
|
|||||||
void onMoveToFullscreen();
|
void onMoveToFullscreen();
|
||||||
/** Invoked when we are above to start resizing the Pip. */
|
/** Invoked when we are above to start resizing the Pip. */
|
||||||
void onPipResizeAboutToStart();
|
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. */
|
/** Invoked when the MediaController on PIPed activity is changed. */
|
||||||
void onMediaControllerChanged();
|
void onMediaControllerChanged();
|
||||||
}
|
}
|
||||||
@@ -645,4 +658,11 @@ public class PipManager {
|
|||||||
}
|
}
|
||||||
return sPipManager;
|
return sPipManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an instance of {@link PipRecentsOverlayManager}.
|
||||||
|
*/
|
||||||
|
public PipRecentsOverlayManager getPipRecentsOverlayManager() {
|
||||||
|
return mPipRecentsOverlayManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,6 @@ import android.app.Activity;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
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.
|
* Activity to show the PIP menu to control PIP.
|
||||||
@@ -36,7 +30,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
|
|||||||
private final PipManager mPipManager = PipManager.getInstance();
|
private final PipManager mPipManager = PipManager.getInstance();
|
||||||
|
|
||||||
private PipControlsView mPipControlsView;
|
private PipControlsView mPipControlsView;
|
||||||
private boolean mPipMovedToFullscreen;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
@@ -47,17 +40,10 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
|
|||||||
mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
|
mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restorePipAndFinish() {
|
|
||||||
if (!mPipMovedToFullscreen) {
|
|
||||||
mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
restorePipAndFinish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -68,11 +54,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
|
|||||||
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
|
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
restorePipAndFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPipEntered() { }
|
public void onPipEntered() { }
|
||||||
|
|
||||||
@@ -86,31 +67,13 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMoveToFullscreen() {
|
public void onMoveToFullscreen() {
|
||||||
mPipMovedToFullscreen = true;
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMediaControllerChanged() { }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPipResizeAboutToStart() {
|
public void onPipResizeAboutToStart() {
|
||||||
finish();
|
finish();
|
||||||
mPipManager.suspendPipResizing(
|
mPipManager.suspendPipResizing(
|
||||||
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,4 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPipResizeAboutToStart() { }
|
public void onPipResizeAboutToStart() { }
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMediaControllerChanged() { }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,14 +35,6 @@ import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
|||||||
public class PipOverlayActivity extends Activity implements PipManager.Listener {
|
public class PipOverlayActivity extends Activity implements PipManager.Listener {
|
||||||
private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
|
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 PipManager mPipManager = PipManager.getInstance();
|
||||||
private final Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
private View mGuideOverlayView;
|
private View mGuideOverlayView;
|
||||||
@@ -54,47 +46,17 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
@Override
|
||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
setContentView(R.layout.tv_pip_overlay);
|
setContentView(R.layout.tv_pip_overlay);
|
||||||
mGuideOverlayView = findViewById(R.id.guide_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);
|
mPipManager.addListener(this);
|
||||||
|
|
||||||
sPipOverlayActivity = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.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.removeCallbacks(mHideGuideOverlayRunnable);
|
||||||
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
|
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
|
||||||
}
|
}
|
||||||
@@ -109,7 +71,6 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
sPipOverlayActivity = null;
|
|
||||||
mHandler.removeCallbacksAndMessages(null);
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
mPipManager.removeListener(this);
|
mPipManager.removeListener(this);
|
||||||
mPipManager.resumePipResizing(
|
mPipManager.resumePipResizing(
|
||||||
@@ -140,32 +101,4 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
|
|||||||
mPipManager.suspendPipResizing(
|
mPipManager.suspendPipResizing(
|
||||||
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user