blob: b5ab026933eca72b98ad29d9eaf2e5e27d5d32fb [file] [log] [blame]
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -08001/*
Justin Klaassen4b3af052014-05-27 17:53:10 -07002 * Copyright (C) 2014 The Android Open Source Project
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -08003 *
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 *
Justin Klaassen4b3af052014-05-27 17:53:10 -07008 * http://www.apache.org/licenses/LICENSE-2.0
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -08009 *
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.calculator2;
18
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080019import android.content.Context;
Justin Klaassen4b3af052014-05-27 17:53:10 -070020import android.content.res.TypedArray;
Hongwei Wang245925e2014-05-11 14:38:47 -070021import android.graphics.Paint;
Justin Klaassen4b3af052014-05-27 17:53:10 -070022import android.graphics.Paint.FontMetricsInt;
23import android.graphics.Rect;
24import android.text.method.ScrollingMovementMethod;
25import android.text.TextPaint;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080026import android.util.AttributeSet;
Hongwei Wang245925e2014-05-11 14:38:47 -070027import android.util.TypedValue;
Gilles Debunnef57b8b42011-01-27 10:54:07 -080028import android.view.ActionMode;
29import android.view.Menu;
30import android.view.MenuItem;
Gilles Debunne79525c32011-01-25 08:25:41 -080031import android.view.MotionEvent;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080032import android.widget.EditText;
Justin Klaassenfed941a2014-06-09 18:42:40 +010033import android.widget.TextView;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080034
Alan Viverette461992d2014-03-07 13:29:56 -080035public class CalculatorEditText extends EditText {
Alan Viverette461992d2014-03-07 13:29:56 -080036
Justin Klaassen4b3af052014-05-27 17:53:10 -070037 private final ActionMode.Callback mNoSelectionActionModeCallback = new ActionMode.Callback() {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080038 @Override
39 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
40 return false;
41 }
42
43 @Override
44 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080045 // Prevents the selection action mode on double tap.
46 return false;
47 }
48
49 @Override
Alan Viverette461992d2014-03-07 13:29:56 -080050 public void onDestroyActionMode(ActionMode mode) {
51 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080052
53 @Override
54 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
55 return false;
56 }
Justin Klaassen4b3af052014-05-27 17:53:10 -070057 };
58
59 private final float mMaximumTextSize;
60 private final float mMinimumTextSize;
61 private final float mStepTextSize;
62
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070063 // Temporary objects for use in layout methods.
64 private final Paint mTempPaint = new TextPaint();
65 private final Rect mTempRect = new Rect();
66
Justin Klaassen4b3af052014-05-27 17:53:10 -070067 private int mWidthConstraint = -1;
Justin Klaassenfed941a2014-06-09 18:42:40 +010068 private OnTextSizeChangeListener mOnTextSizeChangeListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070069
70 public CalculatorEditText(Context context) {
71 this(context, null);
72 }
73
74 public CalculatorEditText(Context context, AttributeSet attrs) {
75 this(context, attrs, 0);
76 }
77
78 public CalculatorEditText(Context context, AttributeSet attrs, int defStyle) {
79 super(context, attrs, defStyle);
80
81 final TypedArray a = context.obtainStyledAttributes(
82 attrs, R.styleable.CalculatorEditText, defStyle, 0);
83 mMaximumTextSize = a.getDimension(
84 R.styleable.CalculatorEditText_maxTextSize, getTextSize());
85 mMinimumTextSize = a.getDimension(
86 R.styleable.CalculatorEditText_minTextSize, getTextSize());
87 mStepTextSize = a.getDimension(R.styleable.CalculatorEditText_stepTextSize,
88 (mMaximumTextSize - mMinimumTextSize) / 3);
89
90 a.recycle();
91
92 setCustomSelectionActionModeCallback(mNoSelectionActionModeCallback);
93 setMovementMethod(ScrollingMovementMethod.getInstance());
94 setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
95 setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
96 }
97
98 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -070099 public boolean onTouchEvent(MotionEvent event) {
100 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
101 // Hack to prevent keyboard and insertion handle from showing.
102 cancelLongPress();
103 }
104 return super.onTouchEvent(event);
105 }
106
107 @Override
108 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
109 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
110
111 mWidthConstraint =
112 MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
113 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText().toString()));
114 }
115
116 @Override
117 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
118 super.onTextChanged(text, start, lengthBefore, lengthAfter);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700119 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
120 }
121
Justin Klaassenfed941a2014-06-09 18:42:40 +0100122 @Override
123 public void setTextSize(int unit, float size) {
124 final float oldTextSize = getTextSize();
125 super.setTextSize(unit, size);
126
127 if (mOnTextSizeChangeListener != null && getTextSize() != oldTextSize) {
128 mOnTextSizeChangeListener.onTextSizeChanged(this, oldTextSize);
129 }
130 }
131
132 public void setOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
133 mOnTextSizeChangeListener = listener;
134 }
135
Justin Klaassen4b3af052014-05-27 17:53:10 -0700136 public float getVariableTextSize(String text) {
137 if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) {
138 // Not measured, bail early.
139 return getTextSize();
140 }
141
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700142 // Capture current paint state.
143 mTempPaint.set(getPaint());
144
145 // Step through increasing text sizes until the text would no longer fit.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700146 float lastFitTextSize = mMinimumTextSize;
147 while (lastFitTextSize < mMaximumTextSize) {
148 final float nextSize = Math.min(lastFitTextSize + mStepTextSize, mMaximumTextSize);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700149 mTempPaint.setTextSize(nextSize);
150 if (mTempPaint.measureText(text) > mWidthConstraint) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700151 break;
152 } else {
153 lastFitTextSize = nextSize;
154 }
155 }
156
157 return lastFitTextSize;
158 }
159
160 @Override
161 public int getCompoundPaddingTop() {
162 // Measure the top padding from the capital letter height of the text instead of the top,
163 // but don't remove more than the available top padding otherwise clipping may occur.
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700164 getPaint().getTextBounds("H", 0, 1, mTempRect);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700165
166 final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700167 final int paddingOffset = -(fontMetrics.ascent + mTempRect.height());
Justin Klaassen4b3af052014-05-27 17:53:10 -0700168 return super.getCompoundPaddingTop() - Math.min(getPaddingTop(), paddingOffset);
169 }
170
171 @Override
172 public int getCompoundPaddingBottom() {
173 // Measure the bottom padding from the baseline of the text instead of the bottom, but don't
174 // remove more than the available bottom padding otherwise clipping may occur.
175 final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
176 return super.getCompoundPaddingBottom() - Math.min(getPaddingBottom(), fontMetrics.descent);
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800177 }
Justin Klaassenfed941a2014-06-09 18:42:40 +0100178
179 public interface OnTextSizeChangeListener {
180 void onTextSizeChanged(TextView textView, float oldSize);
181 }
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800182}