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 android.view;
18
19import android.graphics.Canvas;
20import android.graphics.Color;
21import android.graphics.Paint;
22import android.graphics.Rect;
23import android.graphics.RectF;
24
25/**
26 * Helper class for drawing round scroll bars on round Wear devices.
27 */
28class RoundScrollbarRenderer {
29    // The range of the scrollbar position represented as an angle in degrees.
30    private static final int SCROLLBAR_ANGLE_RANGE = 90;
31    private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16;
32    private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6;
33    private static final float WIDTH_PERCENTAGE = 0.02f;
34    private static final int DEFAULT_THUMB_COLOR = 0x4CFFFFFF;
35    private static final int DEFAULT_TRACK_COLOR = 0x26FFFFFF;
36
37    private final Paint mThumbPaint = new Paint();
38    private final Paint mTrackPaint = new Paint();
39    private final RectF mRect = new RectF();
40    private final View mParent;
41
42    public RoundScrollbarRenderer(View parent) {
43        // Paints for the round scrollbar.
44        // Set up the thumb paint
45        mThumbPaint.setAntiAlias(true);
46        mThumbPaint.setStrokeCap(Paint.Cap.ROUND);
47        mThumbPaint.setStyle(Paint.Style.STROKE);
48
49        // Set up the track paint
50        mTrackPaint.setAntiAlias(true);
51        mTrackPaint.setStrokeCap(Paint.Cap.ROUND);
52        mTrackPaint.setStyle(Paint.Style.STROKE);
53
54        mParent = parent;
55    }
56
57    public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) {
58        if (alpha == 0) {
59            return;
60        }
61        // Get information about the current scroll state of the parent view.
62        float maxScroll = mParent.computeVerticalScrollRange();
63        float scrollExtent = mParent.computeVerticalScrollExtent();
64        if (scrollExtent <= 0 || maxScroll <= scrollExtent) {
65            return;
66        }
67        float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset());
68        float linearThumbLength = mParent.computeVerticalScrollExtent();
69        float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE;
70        mThumbPaint.setStrokeWidth(thumbWidth);
71        mTrackPaint.setStrokeWidth(thumbWidth);
72
73        setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha));
74        setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha));
75
76        // Normalize the sweep angle for the scroll bar.
77        float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE;
78        sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE);
79        // Normalize the start angle so that it falls on the track.
80        float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle))
81                / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2;
82        startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2,
83                SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle);
84
85        // Draw the track and the scroll bar.
86        mRect.set(
87                bounds.left - thumbWidth / 2,
88                bounds.top,
89                bounds.right - thumbWidth / 2,
90                bounds.bottom);
91
92        canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false,
93                mTrackPaint);
94        canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
95    }
96
97    private static float clamp(float val, float min, float max) {
98        if (val < min) {
99            return min;
100        } else if (val > max) {
101            return max;
102        } else {
103            return val;
104        }
105    }
106
107    private static int applyAlpha(int color, float alpha) {
108        int alphaByte = (int) (Color.alpha(color) * alpha);
109        return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color));
110    }
111
112    private void setThumbColor(int thumbColor) {
113        if (mThumbPaint.getColor() != thumbColor) {
114            mThumbPaint.setColor(thumbColor);
115        }
116    }
117
118    private void setTrackColor(int trackColor) {
119        if (mTrackPaint.getColor() != trackColor) {
120            mTrackPaint.setColor(trackColor);
121        }
122    }
123}
124