1 /*
2  * Copyright (C) 2013 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.datetimepicker.time;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.Typeface;
24 import android.graphics.Paint.Align;
25 import android.util.Log;
26 import android.view.View;
27 
28 import com.android.datetimepicker.R;
29 import com.android.datetimepicker.Utils;
30 
31 import java.text.DateFormatSymbols;
32 
33 /**
34  * Draw the two smaller AM and PM circles next to where the larger circle will be.
35  */
36 public class AmPmCirclesView extends View {
37     private static final String TAG = "AmPmCirclesView";
38 
39     // Alpha level for selected circle.
40     private static final int SELECTED_ALPHA = Utils.SELECTED_ALPHA;
41     private static final int SELECTED_ALPHA_THEME_DARK = Utils.SELECTED_ALPHA_THEME_DARK;
42 
43     private final Paint mPaint = new Paint();
44     private int mSelectedAlpha;
45     private int mUnselectedColor;
46     private int mAmPmTextColor;
47     private int mSelectedColor;
48     private float mCircleRadiusMultiplier;
49     private float mAmPmCircleRadiusMultiplier;
50     private String mAmText;
51     private String mPmText;
52     private boolean mIsInitialized;
53 
54     private static final int AM = TimePickerDialog.AM;
55     private static final int PM = TimePickerDialog.PM;
56 
57     private boolean mDrawValuesReady;
58     private int mAmPmCircleRadius;
59     private int mAmXCenter;
60     private int mPmXCenter;
61     private int mAmPmYCenter;
62     private int mAmOrPm;
63     private int mAmOrPmPressed;
64 
AmPmCirclesView(Context context)65     public AmPmCirclesView(Context context) {
66         super(context);
67         mIsInitialized = false;
68     }
69 
initialize(Context context, int amOrPm)70     public void initialize(Context context, int amOrPm) {
71         if (mIsInitialized) {
72             Log.e(TAG, "AmPmCirclesView may only be initialized once.");
73             return;
74         }
75 
76         Resources res = context.getResources();
77         mUnselectedColor = res.getColor(android.R.color.white);
78         mSelectedColor = res.getColor(R.color.blue);
79         mAmPmTextColor = res.getColor(R.color.ampm_text_color);
80         mSelectedAlpha = SELECTED_ALPHA;
81         String typefaceFamily = res.getString(R.string.sans_serif);
82         Typeface tf = Typeface.create(typefaceFamily, Typeface.NORMAL);
83         mPaint.setTypeface(tf);
84         mPaint.setAntiAlias(true);
85         mPaint.setTextAlign(Align.CENTER);
86 
87         mCircleRadiusMultiplier =
88                 Float.parseFloat(res.getString(R.string.circle_radius_multiplier));
89         mAmPmCircleRadiusMultiplier =
90                 Float.parseFloat(res.getString(R.string.ampm_circle_radius_multiplier));
91         String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
92         mAmText = amPmTexts[0];
93         mPmText = amPmTexts[1];
94 
95         setAmOrPm(amOrPm);
96         mAmOrPmPressed = -1;
97 
98         mIsInitialized = true;
99     }
100 
setTheme(Context context, boolean themeDark)101     /* package */ void setTheme(Context context, boolean themeDark) {
102         Resources res = context.getResources();
103         if (themeDark) {
104             mUnselectedColor = res.getColor(R.color.dark_gray);
105             mSelectedColor = res.getColor(R.color.red);
106             mAmPmTextColor = res.getColor(android.R.color.white);
107             mSelectedAlpha = SELECTED_ALPHA_THEME_DARK;
108         } else {
109             mUnselectedColor = res.getColor(android.R.color.white);
110             mSelectedColor = res.getColor(R.color.blue);
111             mAmPmTextColor = res.getColor(R.color.ampm_text_color);
112             mSelectedAlpha = SELECTED_ALPHA;
113         }
114     }
115 
setAmOrPm(int amOrPm)116     public void setAmOrPm(int amOrPm) {
117         mAmOrPm = amOrPm;
118     }
119 
setAmOrPmPressed(int amOrPmPressed)120     public void setAmOrPmPressed(int amOrPmPressed) {
121         mAmOrPmPressed = amOrPmPressed;
122     }
123 
124     /**
125      * Calculate whether the coordinates are touching the AM or PM circle.
126      */
getIsTouchingAmOrPm(float xCoord, float yCoord)127     public int getIsTouchingAmOrPm(float xCoord, float yCoord) {
128         if (!mDrawValuesReady) {
129             return -1;
130         }
131 
132         int squaredYDistance = (int) ((yCoord - mAmPmYCenter)*(yCoord - mAmPmYCenter));
133 
134         int distanceToAmCenter =
135                 (int) Math.sqrt((xCoord - mAmXCenter)*(xCoord - mAmXCenter) + squaredYDistance);
136         if (distanceToAmCenter <= mAmPmCircleRadius) {
137             return AM;
138         }
139 
140         int distanceToPmCenter =
141                 (int) Math.sqrt((xCoord - mPmXCenter)*(xCoord - mPmXCenter) + squaredYDistance);
142         if (distanceToPmCenter <= mAmPmCircleRadius) {
143             return PM;
144         }
145 
146         // Neither was close enough.
147         return -1;
148     }
149 
150     @Override
onDraw(Canvas canvas)151     public void onDraw(Canvas canvas) {
152         int viewWidth = getWidth();
153         if (viewWidth == 0 || !mIsInitialized) {
154             return;
155         }
156 
157         if (!mDrawValuesReady) {
158             int layoutXCenter = getWidth() / 2;
159             int layoutYCenter = getHeight() / 2;
160             int circleRadius =
161                     (int) (Math.min(layoutXCenter, layoutYCenter) * mCircleRadiusMultiplier);
162             mAmPmCircleRadius = (int) (circleRadius * mAmPmCircleRadiusMultiplier);
163             int textSize = mAmPmCircleRadius * 3 / 4;
164             mPaint.setTextSize(textSize);
165 
166             // Line up the vertical center of the AM/PM circles with the bottom of the main circle.
167             mAmPmYCenter = layoutYCenter - mAmPmCircleRadius / 2 + circleRadius;
168             // Line up the horizontal edges of the AM/PM circles with the horizontal edges
169             // of the main circle.
170             mAmXCenter = layoutXCenter - circleRadius + mAmPmCircleRadius;
171             mPmXCenter = layoutXCenter + circleRadius - mAmPmCircleRadius;
172 
173             mDrawValuesReady = true;
174         }
175 
176         // We'll need to draw either a lighter blue (for selection), a darker blue (for touching)
177         // or white (for not selected).
178         int amColor = mUnselectedColor;
179         int amAlpha = 255;
180         int pmColor = mUnselectedColor;
181         int pmAlpha = 255;
182         if (mAmOrPm == AM) {
183             amColor = mSelectedColor;
184             amAlpha = mSelectedAlpha;
185         } else if (mAmOrPm == PM) {
186             pmColor = mSelectedColor;
187             pmAlpha = mSelectedAlpha;
188         }
189         if (mAmOrPmPressed == AM) {
190             amColor = mSelectedColor;
191             amAlpha = mSelectedAlpha;
192         } else if (mAmOrPmPressed == PM) {
193             pmColor = mSelectedColor;
194             pmAlpha = mSelectedAlpha;
195         }
196 
197         // Draw the two circles.
198         mPaint.setColor(amColor);
199         mPaint.setAlpha(amAlpha);
200         canvas.drawCircle(mAmXCenter, mAmPmYCenter, mAmPmCircleRadius, mPaint);
201         mPaint.setColor(pmColor);
202         mPaint.setAlpha(pmAlpha);
203         canvas.drawCircle(mPmXCenter, mAmPmYCenter, mAmPmCircleRadius, mPaint);
204 
205         // Draw the AM/PM texts on top.
206         mPaint.setColor(mAmPmTextColor);
207         int textYCenter = mAmPmYCenter - (int) (mPaint.descent() + mPaint.ascent()) / 2;
208         canvas.drawText(mAmText, mAmXCenter, textYCenter, mPaint);
209         canvas.drawText(mPmText, mPmXCenter, textYCenter, mPaint);
210     }
211 }
212