1 /*
2  * Copyright (C) 2017 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.tv.menu;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.Canvas;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 import android.graphics.drawable.LayerDrawable;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import com.android.tv.R;
28 
29 /** A progress bar control which has two progresses which start in the middle of the control. */
30 public class PlaybackProgressBar extends View {
31     private final LayerDrawable mProgressDrawable;
32     private final Drawable mPrimaryDrawable;
33     private final Drawable mSecondaryDrawable;
34     private long mMax = 100;
35     private long mProgressStart = 0;
36     private long mProgressEnd = 0;
37     private long mProgress = 0;
38 
PlaybackProgressBar(Context context)39     public PlaybackProgressBar(Context context) {
40         this(context, null);
41     }
42 
PlaybackProgressBar(Context context, AttributeSet attrs)43     public PlaybackProgressBar(Context context, AttributeSet attrs) {
44         this(context, attrs, 0);
45     }
46 
PlaybackProgressBar(Context context, AttributeSet attrs, int defStyleAttr)47     public PlaybackProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
48         this(context, attrs, defStyleAttr, 0);
49     }
50 
PlaybackProgressBar( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)51     public PlaybackProgressBar(
52             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
53         super(context, attrs, defStyleAttr, defStyleRes);
54         TypedArray a =
55                 context.obtainStyledAttributes(
56                         attrs, R.styleable.PlaybackProgressBar, defStyleAttr, defStyleRes);
57         mProgressDrawable =
58                 (LayerDrawable) a.getDrawable(R.styleable.PlaybackProgressBar_progressDrawable);
59         mPrimaryDrawable = mProgressDrawable.findDrawableByLayerId(android.R.id.progress);
60         mSecondaryDrawable =
61                 mProgressDrawable.findDrawableByLayerId(android.R.id.secondaryProgress);
62         a.recycle();
63         refreshProgress();
64     }
65 
66     @Override
onDraw(Canvas canvas)67     protected void onDraw(Canvas canvas) {
68         final int saveCount = canvas.save();
69         canvas.translate(getPaddingLeft(), getPaddingTop());
70         mProgressDrawable.draw(canvas);
71         canvas.restoreToCount(saveCount);
72     }
73 
74     @Override
onSizeChanged(int w, int h, int oldw, int oldh)75     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
76         super.onSizeChanged(w, h, oldw, oldh);
77         refreshProgress();
78     }
79 
setMax(long max)80     public void setMax(long max) {
81         if (max < 0) {
82             max = 0;
83         }
84         if (max != mMax) {
85             mMax = max;
86             if (mProgressStart > max) {
87                 mProgressStart = max;
88             }
89             if (mProgressEnd > max) {
90                 mProgressEnd = max;
91             }
92             if (mProgress > max) {
93                 mProgress = max;
94             }
95             refreshProgress();
96         }
97     }
98 
99     /** Sets the start and end position of the progress. */
setProgressRange(long start, long end)100     public void setProgressRange(long start, long end) {
101         start = constrain(start, 0, mMax);
102         end = constrain(end, start, mMax);
103         mProgress = constrain(mProgress, start, end);
104         if (start != mProgressStart || end != mProgressEnd) {
105             mProgressStart = start;
106             mProgressEnd = end;
107             setProgressLevels();
108         }
109     }
110 
111     /** Sets the progress position. */
setProgress(long progress)112     public void setProgress(long progress) {
113         progress = constrain(progress, mProgressStart, mProgressEnd);
114         if (progress != mProgress) {
115             mProgress = progress;
116             setProgressLevels();
117         }
118     }
119 
constrain(long value, long min, long max)120     private long constrain(long value, long min, long max) {
121         return Math.min(Math.max(value, min), max);
122     }
123 
refreshProgress()124     private void refreshProgress() {
125         int width = getWidth() - getPaddingStart() - getPaddingEnd();
126         int height = getHeight() - getPaddingTop() - getPaddingBottom();
127         mProgressDrawable.setBounds(0, 0, width, height);
128         setProgressLevels();
129     }
130 
setProgressLevels()131     private void setProgressLevels() {
132         boolean progressUpdated = setProgressBound(mPrimaryDrawable, mProgressStart, mProgress);
133         progressUpdated |= setProgressBound(mSecondaryDrawable, mProgress, mProgressEnd);
134         if (progressUpdated) {
135             postInvalidate();
136         }
137     }
138 
setProgressBound(Drawable drawable, long start, long end)139     private boolean setProgressBound(Drawable drawable, long start, long end) {
140         Rect oldBounds = drawable.getBounds();
141         if (mMax == 0) {
142             if (!isEqualRect(oldBounds, 0, 0, 0, 0)) {
143                 drawable.setBounds(0, 0, 0, 0);
144                 return true;
145             }
146             return false;
147         }
148         int width = mProgressDrawable.getBounds().width();
149         int height = mProgressDrawable.getBounds().height();
150         int left = (int) (width * start / mMax);
151         int right = (int) (width * end / mMax);
152         if (!isEqualRect(oldBounds, left, 0, right, height)) {
153             drawable.setBounds(left, 0, right, height);
154             return true;
155         }
156         return false;
157     }
158 
isEqualRect(Rect rect, int left, int top, int right, int bottom)159     private boolean isEqualRect(Rect rect, int left, int top, int right, int bottom) {
160         return rect.left == left && rect.top == top && rect.right == right && rect.bottom == bottom;
161     }
162 }
163