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
17package com.android.datetimepicker.time;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.graphics.Canvas;
22import android.graphics.Paint;
23import android.graphics.Typeface;
24import android.graphics.Paint.Align;
25import android.util.Log;
26import android.view.View;
27
28import com.android.datetimepicker.R;
29import com.android.datetimepicker.Utils;
30
31import java.text.DateFormatSymbols;
32
33/**
34 * Draw the two smaller AM and PM circles next to where the larger circle will be.
35 */
36public 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
65    public AmPmCirclesView(Context context) {
66        super(context);
67        mIsInitialized = false;
68    }
69
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
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
116    public void setAmOrPm(int amOrPm) {
117        mAmOrPm = amOrPm;
118    }
119
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     */
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
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