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.pip.tv;
18
19import android.animation.Animator;
20import android.animation.AnimatorInflater;
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.drawable.Drawable;
24import android.util.AttributeSet;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.widget.ImageView;
28import android.widget.RelativeLayout;
29import android.widget.TextView;
30
31import com.android.systemui.R;
32
33/**
34 * A view containing PIP controls including fullscreen, close, and media controls.
35 */
36public 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
63    public PipControlButtonView(Context context) {
64        this(context, null, 0, 0);
65    }
66
67    public PipControlButtonView(Context context, AttributeSet attrs) {
68        this(context, attrs, 0, 0);
69    }
70
71    public PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
72        this(context, attrs, defStyleAttr, 0);
73    }
74
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
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
117    public void setOnClickListener(OnClickListener listener) {
118        mButtonImageView.setOnClickListener(listener);
119    }
120
121    @Override
122    public void setOnFocusChangeListener(OnFocusChangeListener listener) {
123        mFocusChangeListener = listener;
124    }
125
126    /**
127     * Sets the drawable for the button with the given drawable.
128     */
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     */
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     */
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     */
153    public void setText(int resId) {
154        if (resId != 0) {
155            mButtonImageView.setContentDescription(getContext().getString(resId));
156            mDescriptionTextView.setText(resId);
157        }
158    }
159
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     */
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     */
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     */
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