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 17 package com.android.systemui.pip.tv; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorInflater; 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.graphics.drawable.Drawable; 24 import android.util.AttributeSet; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.widget.ImageView; 28 import android.widget.RelativeLayout; 29 import android.widget.TextView; 30 31 import com.android.systemui.R; 32 33 /** 34 * A view containing PIP controls including fullscreen, close, and media controls. 35 */ 36 public class PipControlButtonView extends RelativeLayout { 37 38 private OnFocusChangeListener mFocusChangeListener; 39 private ImageView mIconImageView; 40 ImageView mButtonImageView; 41 private TextView mDescriptionTextView; 42 private Animator mTextFocusGainAnimator; 43 private Animator mButtonFocusGainAnimator; 44 private Animator mTextFocusLossAnimator; 45 private Animator mButtonFocusLossAnimator; 46 47 private final OnFocusChangeListener mInternalFocusChangeListener = 48 new OnFocusChangeListener() { 49 @Override 50 public void onFocusChange(View v, boolean hasFocus) { 51 if (hasFocus) { 52 startFocusGainAnimation(); 53 } else { 54 startFocusLossAnimation(); 55 } 56 57 if (mFocusChangeListener != null) { 58 mFocusChangeListener.onFocusChange(PipControlButtonView.this, hasFocus); 59 } 60 } 61 }; 62 PipControlButtonView(Context context)63 public PipControlButtonView(Context context) { 64 this(context, null, 0, 0); 65 } 66 PipControlButtonView(Context context, AttributeSet attrs)67 public PipControlButtonView(Context context, AttributeSet attrs) { 68 this(context, attrs, 0, 0); 69 } 70 PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr)71 public PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr) { 72 this(context, attrs, defStyleAttr, 0); 73 } 74 PipControlButtonView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)75 public PipControlButtonView( 76 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 77 super(context, attrs, defStyleAttr, defStyleRes); 78 LayoutInflater inflater = (LayoutInflater) getContext() 79 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 80 inflater.inflate(R.layout.tv_pip_control_button, this); 81 82 mIconImageView = findViewById(R.id.icon); 83 mButtonImageView = findViewById(R.id.button); 84 mDescriptionTextView = findViewById(R.id.desc); 85 86 int[] values = new int[] {android.R.attr.src, android.R.attr.text}; 87 TypedArray typedArray = 88 context.obtainStyledAttributes(attrs, values, defStyleAttr, defStyleRes); 89 90 setImageResource(typedArray.getResourceId(0, 0)); 91 setText(typedArray.getResourceId(1, 0)); 92 93 typedArray.recycle(); 94 } 95 96 @Override onFinishInflate()97 public void onFinishInflate() { 98 super.onFinishInflate(); 99 mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener); 100 101 mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), 102 R.anim.tv_pip_controls_focus_gain_animation); 103 mTextFocusGainAnimator.setTarget(mDescriptionTextView); 104 mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(), 105 R.anim.tv_pip_controls_focus_gain_animation); 106 mButtonFocusGainAnimator.setTarget(mButtonImageView); 107 108 mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(), 109 R.anim.tv_pip_controls_focus_loss_animation); 110 mTextFocusLossAnimator.setTarget(mDescriptionTextView); 111 mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(), 112 R.anim.tv_pip_controls_focus_loss_animation); 113 mButtonFocusLossAnimator.setTarget(mButtonImageView); 114 } 115 116 @Override setOnClickListener(OnClickListener listener)117 public void setOnClickListener(OnClickListener listener) { 118 mButtonImageView.setOnClickListener(listener); 119 } 120 121 @Override setOnFocusChangeListener(OnFocusChangeListener listener)122 public void setOnFocusChangeListener(OnFocusChangeListener listener) { 123 mFocusChangeListener = listener; 124 } 125 126 /** 127 * Sets the drawable for the button with the given drawable. 128 */ setImageDrawable(Drawable d)129 public void setImageDrawable(Drawable d) { 130 mIconImageView.setImageDrawable(d); 131 } 132 133 /** 134 * Sets the drawable for the button with the given resource id. 135 */ setImageResource(int resId)136 public void setImageResource(int resId) { 137 if (resId != 0) { 138 mIconImageView.setImageResource(resId); 139 } 140 } 141 142 /** 143 * Sets the text for description the with the given string. 144 */ setText(CharSequence text)145 public void setText(CharSequence text) { 146 mButtonImageView.setContentDescription(text); 147 mDescriptionTextView.setText(text); 148 } 149 150 /** 151 * Sets the text for description the with the given resource id. 152 */ setText(int resId)153 public void setText(int resId) { 154 if (resId != 0) { 155 mButtonImageView.setContentDescription(getContext().getString(resId)); 156 mDescriptionTextView.setText(resId); 157 } 158 } 159 cancelAnimator(Animator animator)160 private static void cancelAnimator(Animator animator) { 161 if (animator.isStarted()) { 162 animator.cancel(); 163 } 164 } 165 166 /** 167 * Starts the focus gain animation. 168 */ startFocusGainAnimation()169 public void startFocusGainAnimation() { 170 cancelAnimator(mButtonFocusLossAnimator); 171 cancelAnimator(mTextFocusLossAnimator); 172 mTextFocusGainAnimator.start(); 173 if (mButtonImageView.getAlpha() < 1f) { 174 // If we had faded out the ripple drawable, run our manual focus change animation. 175 // See the comment at {@link #startFocusLossAnimation()} for the reason of manual 176 // animator. 177 mButtonFocusGainAnimator.start(); 178 } 179 } 180 181 /** 182 * Starts the focus loss animation. 183 */ startFocusLossAnimation()184 public void startFocusLossAnimation() { 185 cancelAnimator(mButtonFocusGainAnimator); 186 cancelAnimator(mTextFocusGainAnimator); 187 mTextFocusLossAnimator.start(); 188 if (mButtonImageView.hasFocus()) { 189 // Button uses ripple that has the default animation for the focus changes. 190 // Howevever, it doesn't expose the API to fade out while it is focused, 191 // so we should manually run the fade out animation when PIP controls row loses focus. 192 mButtonFocusLossAnimator.start(); 193 } 194 } 195 196 /** 197 * Resets to initial state. 198 */ reset()199 public void reset() { 200 cancelAnimator(mButtonFocusGainAnimator); 201 cancelAnimator(mTextFocusGainAnimator); 202 cancelAnimator(mButtonFocusLossAnimator); 203 cancelAnimator(mTextFocusLossAnimator); 204 mButtonImageView.setAlpha(1f); 205 mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f); 206 } 207 } 208