1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.systemui.tv.pip; 18 19import android.animation.Animator; 20import android.animation.AnimatorInflater; 21import android.animation.AnimatorListenerAdapter; 22import android.animation.AnimatorSet; 23import android.content.Context; 24import android.graphics.Rect; 25import android.util.AttributeSet; 26import android.view.KeyEvent; 27import android.view.View; 28import android.view.View.OnFocusChangeListener; 29import android.widget.FrameLayout; 30 31import com.android.systemui.R; 32 33import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING; 34import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED; 35import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE; 36 37/** 38 * An FrameLayout that contains {@link PipControlsView} with its scrim. 39 */ 40public class PipRecentsControlsView extends FrameLayout { 41 /** 42 * An interface to listen user action. 43 */ 44 public interface Listener extends PipControlsView.Listener { 45 /** 46 * Called when an user presses BACK key and up. 47 */ 48 abstract void onBackPressed(); 49 } 50 51 private final PipManager mPipManager = PipManager.getInstance(); 52 private Listener mListener; 53 private PipControlsView mPipControlsView; 54 private View mScrim; 55 private Animator mFocusGainAnimator; 56 private AnimatorSet mFocusLossAnimatorSet; 57 58 public PipRecentsControlsView(Context context) { 59 this(context, null, 0, 0); 60 } 61 62 public PipRecentsControlsView(Context context, AttributeSet attrs) { 63 this(context, attrs, 0, 0); 64 } 65 66 public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) { 67 this(context, attrs, defStyleAttr, 0); 68 } 69 70 public PipRecentsControlsView( 71 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 72 super(context, attrs, defStyleAttr, defStyleRes); 73 } 74 75 @Override 76 public void onFinishInflate() { 77 super.onFinishInflate(); 78 79 mPipControlsView = (PipControlsView) findViewById(R.id.pip_control_contents); 80 mScrim = findViewById(R.id.scrim); 81 82 mFocusGainAnimator = loadAnimator(mPipControlsView, 83 R.anim.tv_pip_controls_in_recents_focus_gain_animation); 84 85 mFocusLossAnimatorSet = new AnimatorSet(); 86 mFocusLossAnimatorSet.playSequentially( 87 loadAnimator(mPipControlsView, 88 R.anim.tv_pip_controls_in_recents_focus_loss_animation), 89 loadAnimator(mScrim, R.anim.tv_pip_controls_in_recents_scrim_fade_in_animation)); 90 91 Rect pipBounds = mPipManager.getRecentsFocusedPipBounds(); 92 setPadding(0, pipBounds.bottom, 0, 0); 93 } 94 95 private Animator loadAnimator(View view, int animatorResId) { 96 Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId); 97 animator.setTarget(view); 98 return animator; 99 } 100 101 /** 102 * Starts focus gain animation. 103 */ 104 public void startFocusGainAnimation() { 105 // Hides the scrim view as soon as possible, before the PIP resize animation starts. 106 // If we don't, PIP will be moved down a bit and a gap between the scrim and PIP will be 107 // shown at the bottom of the PIP. 108 mScrim.setAlpha(0); 109 PipControlButtonView focus = mPipControlsView.getFocusedButton(); 110 if (focus != null) { 111 focus.startFocusGainAnimation(); 112 } 113 startAnimator(mFocusGainAnimator, mFocusLossAnimatorSet); 114 } 115 116 /** 117 * Starts focus loss animation. 118 */ 119 public void startFocusLossAnimation() { 120 PipControlButtonView focus = mPipControlsView.getFocusedButton(); 121 if (focus != null) { 122 focus.startFocusLossAnimation(); 123 } 124 startAnimator(mFocusLossAnimatorSet, mFocusGainAnimator); 125 } 126 127 /** 128 * Resets the view to the initial state. (i.e. end of the focus gain) 129 */ 130 public void reset() { 131 cancelAnimator(mFocusGainAnimator); 132 cancelAnimator(mFocusLossAnimatorSet); 133 134 // Reset to initial state (i.e. end of focused) 135 mScrim.setAlpha(0); 136 mPipControlsView.setTranslationY(0); 137 mPipControlsView.setScaleX(1); 138 mPipControlsView.setScaleY(1); 139 mPipControlsView.reset(); 140 } 141 142 private static void startAnimator(Animator animator, Animator previousAnimator) { 143 cancelAnimator(previousAnimator); 144 if (!animator.isStarted()) { 145 animator.start(); 146 } 147 } 148 149 private static void cancelAnimator(Animator animator) { 150 if (animator.isStarted()) { 151 animator.cancel(); 152 } 153 } 154 155 /** 156 * Sets listeners. 157 */ 158 public void setListener(Listener listener) { 159 mPipControlsView.setListener(listener); 160 } 161 162 @Override 163 public boolean dispatchKeyEvent(KeyEvent event) { 164 if (!event.isCanceled()) { 165 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 166 && event.getAction() == KeyEvent.ACTION_UP) { 167 if (mPipControlsView.mListener != null) { 168 ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed(); 169 } 170 return true; 171 } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { 172 if (event.getAction() == KeyEvent.ACTION_DOWN) { 173 mPipManager.getPipRecentsOverlayManager().clearFocus(); 174 } 175 // Consume the down event always to prevent warning logs from ViewRootImpl. 176 return true; 177 } 178 } 179 return super.dispatchKeyEvent(event); 180 } 181} 182