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