• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.deskclock;
2 
3 import android.content.Context;
4 import android.util.AttributeSet;
5 import android.widget.FrameLayout;
6 import android.widget.ImageButton;
7 import android.widget.LinearLayout;
8 import android.widget.TextView;
9 
10 /**
11  * TODO: Insert description here. (generated by sblitz)
12  */
13 public class CircleButtonsLinearLayout extends LinearLayout {
14     private Context mContext;
15     private int mCircleTimerViewId;
16     private int mLeftButtonId;
17     private int mRightButtonId;
18     private int mStopButtonId;
19     private int mLabelId;
20     private int mLabelTextId;
21     private float mLeftButtonPadding;
22     private float mRightButtonPadding;
23     private float mStrokeSize;
24     private float mDiamOffset;
25     private CircleTimerView mCtv;
26     private ImageButton mLeft, mRight;
27     private TextView mStop;
28     private FrameLayout mLabel;
29     private TextView mLabelText;
30 
CircleButtonsLinearLayout(Context context)31     public CircleButtonsLinearLayout(Context context) {
32         this(context, null);
33         mContext = context;
34     }
35 
CircleButtonsLinearLayout(Context context, AttributeSet attrs)36     public CircleButtonsLinearLayout(Context context, AttributeSet attrs) {
37         super(context, attrs);
38         mContext = context;
39     }
40 
setCircleTimerViewIds(int circleTimerViewId, int leftButtonId, int rightButtonId, int stopButtonId, int leftButtonPaddingDimenId, int rightButtonPaddingDimenId, int labelId, int labelTextId)41     public void setCircleTimerViewIds(int circleTimerViewId, int leftButtonId, int rightButtonId,
42             int stopButtonId, int leftButtonPaddingDimenId, int rightButtonPaddingDimenId,
43             int labelId, int labelTextId) {
44         mCircleTimerViewId = circleTimerViewId;
45         mLeftButtonId = leftButtonId;
46         mRightButtonId = rightButtonId;
47         mStopButtonId = stopButtonId;
48         mLabelId = labelId;
49         mLabelTextId = labelTextId;
50         mLeftButtonPadding = mContext.getResources().getDimension(leftButtonPaddingDimenId);
51         mRightButtonPadding = mContext.getResources().getDimension(rightButtonPaddingDimenId);
52 
53         float diamondStrokeSize =
54                 mContext.getResources().getDimension(R.dimen.circletimer_diamond_size);
55         float markerStrokeSize =
56                 mContext.getResources().getDimension(R.dimen.circletimer_marker_size);
57         mStrokeSize = mContext.getResources().getDimension(R.dimen.circletimer_circle_size);
58         mDiamOffset =
59                 Utils.calculateRadiusOffset(mStrokeSize, diamondStrokeSize, markerStrokeSize) * 2;
60     }
61 
62     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)63     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
64         // We must call onMeasure both before and after re-measuring our views because the circle
65         // may not always be drawn here yet. The first onMeasure will force the circle to be drawn,
66         // and the second will force our re-measurements to take effect.
67         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
68         remeasureViews();
69         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
70     }
71 
remeasureViews()72     protected void remeasureViews() {
73         if (mCtv == null) {
74             mCtv = (CircleTimerView) findViewById(mCircleTimerViewId);
75             if (mCtv == null) {
76                 return;
77             }
78             mLeft = (ImageButton) findViewById(mLeftButtonId);
79             mRight = (ImageButton) findViewById(mRightButtonId);
80             mStop = (TextView) findViewById(mStopButtonId);
81             mLabel = (FrameLayout) findViewById(mLabelId);
82             mLabelText = (TextView) findViewById(mLabelTextId);
83         }
84 
85         int frameWidth = mCtv.getMeasuredWidth();
86         int frameHeight = mCtv.getMeasuredHeight();
87         int minBound = Math.min(frameWidth, frameHeight);
88         int circleDiam = (int) (minBound - mDiamOffset);
89 
90         MarginLayoutParams stopParams = (MarginLayoutParams) mStop.getLayoutParams();
91         stopParams.bottomMargin = circleDiam/6;
92         if (minBound == frameWidth) {
93             stopParams.bottomMargin += (frameHeight-frameWidth)/2;
94         }
95 
96         if (mLabel != null) {
97             // label will be null if this is a stopwatch, which does not have a label.
98             MarginLayoutParams labelParams = (MarginLayoutParams) mLabel.getLayoutParams();
99             labelParams.topMargin = circleDiam/6;
100             if (minBound == frameWidth) {
101                 labelParams.topMargin += (frameHeight-frameWidth)/2;
102             }
103             /* The following formula has been simplified based on the following:
104              * Our goal is to calculate the maximum width for the label frame.
105              * We may do this with the following diagram to represent the top half of the circle:
106              *                 ___
107              *            .     |     .
108              *        ._________|         .
109              *     .       ^    |            .
110              *   /         x    |              \
111              *  |_______________|_______________|
112              *
113              *  where x represents the value we would like to calculate, and the final width of the
114              *  label will be w = 2 * x.
115              *
116              *  We may find x by drawing a right triangle from the center of the circle:
117              *                 ___
118              *            .     |     .
119              *        ._________|         .
120              *     .    .       |            .
121              *   /          .   | }y           \
122              *  |_____________.t|_______________|
123              *
124              *  where t represents the angle of that triangle, and y is the height of that triangle.
125              *
126              *  If r = radius of the circle, we know the following trigonometric identities:
127              *        cos(t) = y / r
128              *  and   sin(t) = x / r
129              *     => r * sin(t) = x
130              *  and   sin^2(t) = 1 - cos^2(t)
131              *     => sin(t) = +/- sqrt(1 - cos^2(t))
132              *  (note: because we need the positive value, we may drop the +/-).
133              *
134              *  To calculate the final width, we may combine our formulas:
135              *        w = 2 * x
136              *     => w = 2 * r * sin(t)
137              *     => w = 2 * r * sqrt(1 - cos^2(t))
138              *     => w = 2 * r * sqrt(1 - (y / r)^2)
139              *
140              *  Simplifying even further, to mitigate the complexity of the final formula:
141              *        sqrt(1 - (y / r)^2)
142              *     => sqrt(1 - (y^2 / r^2))
143              *     => sqrt((r^2 / r^2) - (y^2 / r^2))
144              *     => sqrt((r^2 - y^2) / (r^2))
145              *     => sqrt(r^2 - y^2) / sqrt(r^2)
146              *     => sqrt(r^2 - y^2) / r
147              *     => sqrt((r + y)*(r - y)) / r
148              *
149              * Placing this back in our formula, we end up with, as our final, reduced equation:
150              *        w = 2 * r * sqrt(1 - (y / r)^2)
151              *     => w = 2 * r * sqrt((r + y)*(r - y)) / r
152              *     => w = 2 * sqrt((r + y)*(r - y))
153              */
154             // Radius of the circle.
155             int r = circleDiam / 2;
156             // Y value of the top of the label, calculated from the center of the circle.
157             int y = frameHeight / 2 - labelParams.topMargin;
158             // New maximum width of the label.
159             double w = 2 * Math.sqrt((r + y) * (r - y));
160 
161             mLabelText.setMaxWidth((int) w);
162         }
163 
164         int sideMarginOffset = (int) ((frameWidth - circleDiam - mStrokeSize) / 2)
165                 - (int) mContext.getResources().getDimension(R.dimen.timer_button_extra_offset);
166         int leftMarginOffset = Math.max(0, sideMarginOffset - (int) mLeftButtonPadding);
167         int rightMarginOffset = Math.max(0, sideMarginOffset - (int) mRightButtonPadding);
168         int bottomMarginOffset = (frameHeight - minBound) / 2;
169         MarginLayoutParams leftParams = (MarginLayoutParams) mLeft.getLayoutParams();
170         leftParams.leftMargin = leftMarginOffset;
171         leftParams.bottomMargin = bottomMarginOffset;
172         MarginLayoutParams rightParams = (MarginLayoutParams) mRight.getLayoutParams();
173         rightParams.rightMargin = rightMarginOffset;
174         rightParams.bottomMargin = bottomMarginOffset;
175     }
176 }