blob: 380aa814f6bc859054eafef68471ea94f4968ba5 [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;
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -070024import android.os.Parcelable;
Justin Klaassen4b3af052014-05-27 17:53:10 -070025import android.text.method.ScrollingMovementMethod;
26import android.text.TextPaint;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080027import android.util.AttributeSet;
Hongwei Wang245925e2014-05-11 14:38:47 -070028import android.util.TypedValue;
Gilles Debunnef57b8b42011-01-27 10:54:07 -080029import android.view.ActionMode;
30import android.view.Menu;
31import android.view.MenuItem;
Gilles Debunne79525c32011-01-25 08:25:41 -080032import android.view.MotionEvent;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080033import android.widget.EditText;
Justin Klaassenfed941a2014-06-09 18:42:40 +010034import android.widget.TextView;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080035
Hans Boehm84614952014-11-25 18:46:17 -080036/**
37 * EditText adapted for Calculator display.
38 */
39
Alan Viverette461992d2014-03-07 13:29:56 -080040public class CalculatorEditText extends EditText {
Alan Viverette461992d2014-03-07 13:29:56 -080041
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -070042 private final static ActionMode.Callback NO_SELECTION_ACTION_MODE_CALLBACK =
43 new ActionMode.Callback() {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080044 @Override
45 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
46 return false;
47 }
48
49 @Override
50 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080051 // Prevents the selection action mode on double tap.
52 return false;
53 }
54
55 @Override
Alan Viverette461992d2014-03-07 13:29:56 -080056 public void onDestroyActionMode(ActionMode mode) {
57 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080058
59 @Override
60 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
61 return false;
62 }
Justin Klaassen4b3af052014-05-27 17:53:10 -070063 };
64
65 private final float mMaximumTextSize;
66 private final float mMinimumTextSize;
67 private final float mStepTextSize;
68
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070069 // Temporary objects for use in layout methods.
70 private final Paint mTempPaint = new TextPaint();
71 private final Rect mTempRect = new Rect();
72
Justin Klaassen4b3af052014-05-27 17:53:10 -070073 private int mWidthConstraint = -1;
Justin Klaassenfed941a2014-06-09 18:42:40 +010074 private OnTextSizeChangeListener mOnTextSizeChangeListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070075
76 public CalculatorEditText(Context context) {
77 this(context, null);
78 }
79
80 public CalculatorEditText(Context context, AttributeSet attrs) {
81 this(context, attrs, 0);
82 }
83
84 public CalculatorEditText(Context context, AttributeSet attrs, int defStyle) {
85 super(context, attrs, defStyle);
86
87 final TypedArray a = context.obtainStyledAttributes(
88 attrs, R.styleable.CalculatorEditText, defStyle, 0);
89 mMaximumTextSize = a.getDimension(
90 R.styleable.CalculatorEditText_maxTextSize, getTextSize());
91 mMinimumTextSize = a.getDimension(
92 R.styleable.CalculatorEditText_minTextSize, getTextSize());
93 mStepTextSize = a.getDimension(R.styleable.CalculatorEditText_stepTextSize,
94 (mMaximumTextSize - mMinimumTextSize) / 3);
95
96 a.recycle();
97
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -070098 setCustomSelectionActionModeCallback(NO_SELECTION_ACTION_MODE_CALLBACK);
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070099 if (isFocusable()) {
100 setMovementMethod(ScrollingMovementMethod.getInstance());
101 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700102 setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
103 setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
104 }
105
106 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700107 public boolean onTouchEvent(MotionEvent event) {
108 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
109 // Hack to prevent keyboard and insertion handle from showing.
110 cancelLongPress();
111 }
112 return super.onTouchEvent(event);
113 }
114
115 @Override
116 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
117 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
118
119 mWidthConstraint =
120 MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
121 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText().toString()));
122 }
123
Hans Boehm84614952014-11-25 18:46:17 -0800124 public int getWidthConstraint() { return mWidthConstraint; }
125
Justin Klaassen4b3af052014-05-27 17:53:10 -0700126 @Override
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -0700127 public Parcelable onSaveInstanceState() {
128 super.onSaveInstanceState();
129
130 // EditText will freeze any text with a selection regardless of getFreezesText() ->
131 // return null to prevent any state from being preserved at the instance level.
132 return null;
133 }
134
135 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700136 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
137 super.onTextChanged(text, start, lengthBefore, lengthAfter);
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -0700138
139 final int textLength = text.length();
140 if (getSelectionStart() != textLength || getSelectionEnd() != textLength) {
141 // Pin the selection to the end of the current text.
142 setSelection(textLength);
143 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700144 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
145 }
146
Justin Klaassenfed941a2014-06-09 18:42:40 +0100147 @Override
148 public void setTextSize(int unit, float size) {
149 final float oldTextSize = getTextSize();
150 super.setTextSize(unit, size);
151
152 if (mOnTextSizeChangeListener != null && getTextSize() != oldTextSize) {
153 mOnTextSizeChangeListener.onTextSizeChanged(this, oldTextSize);
154 }
155 }
156
157 public void setOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
158 mOnTextSizeChangeListener = listener;
159 }
160
Justin Klaassen4b3af052014-05-27 17:53:10 -0700161 public float getVariableTextSize(String text) {
162 if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) {
163 // Not measured, bail early.
164 return getTextSize();
165 }
166
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700167 // Capture current paint state.
168 mTempPaint.set(getPaint());
169
170 // Step through increasing text sizes until the text would no longer fit.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700171 float lastFitTextSize = mMinimumTextSize;
172 while (lastFitTextSize < mMaximumTextSize) {
173 final float nextSize = Math.min(lastFitTextSize + mStepTextSize, mMaximumTextSize);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700174 mTempPaint.setTextSize(nextSize);
175 if (mTempPaint.measureText(text) > mWidthConstraint) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700176 break;
177 } else {
178 lastFitTextSize = nextSize;
179 }
180 }
181
182 return lastFitTextSize;
183 }
184
185 @Override
186 public int getCompoundPaddingTop() {
187 // Measure the top padding from the capital letter height of the text instead of the top,
188 // but don't remove more than the available top padding otherwise clipping may occur.
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700189 getPaint().getTextBounds("H", 0, 1, mTempRect);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700190
191 final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700192 final int paddingOffset = -(fontMetrics.ascent + mTempRect.height());
Justin Klaassen4b3af052014-05-27 17:53:10 -0700193 return super.getCompoundPaddingTop() - Math.min(getPaddingTop(), paddingOffset);
194 }
195
196 @Override
197 public int getCompoundPaddingBottom() {
198 // Measure the bottom padding from the baseline of the text instead of the bottom, but don't
199 // remove more than the available bottom padding otherwise clipping may occur.
200 final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
201 return super.getCompoundPaddingBottom() - Math.min(getPaddingBottom(), fontMetrics.descent);
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800202 }
Justin Klaassenfed941a2014-06-09 18:42:40 +0100203
204 public interface OnTextSizeChangeListener {
205 void onTextSizeChanged(TextView textView, float oldSize);
206 }
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800207}