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