Home | History | Annotate | Download | only in deskclock
      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 
     31     public CircleButtonsLinearLayout(Context context) {
     32         this(context, null);
     33         mContext = context;
     34     }
     35 
     36     public CircleButtonsLinearLayout(Context context, AttributeSet attrs) {
     37         super(context, attrs);
     38         mContext = context;
     39     }
     40 
     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
     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 
     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 }