blob: b41b6d3974885d518a758adac32929db555cbe46 [file] [log] [blame]
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -08001/*
Justin Klaassen44595162015-05-28 17:55:20 -07002 * Copyright (C) 2015 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
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070019import android.content.ClipData;
Justin Klaassen44595162015-05-28 17:55:20 -070020import android.content.ClipboardManager;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080021import android.content.Context;
Justin Klaassen4b3af052014-05-27 17:53:10 -070022import android.content.res.TypedArray;
Justin Klaassenfc5ac822015-06-18 13:15:17 -070023import android.text.Layout;
Justin Klaassen4b3af052014-05-27 17:53:10 -070024import android.text.TextPaint;
Justin Klaassen44595162015-05-28 17:55:20 -070025import android.text.method.ScrollingMovementMethod;
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;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070030import android.view.MenuInflater;
Gilles Debunnef57b8b42011-01-27 10:54:07 -080031import android.view.MenuItem;
Hans Boehm76b78152015-04-17 10:50:35 -070032import android.view.View;
Justin Klaassenfed941a2014-06-09 18:42:40 +010033import android.widget.TextView;
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -080034
Hans Boehm84614952014-11-25 18:46:17 -080035/**
Hans Boehm08e8f322015-04-21 13:18:38 -070036 * TextView adapted for Calculator display.
Hans Boehm84614952014-11-25 18:46:17 -080037 */
Justin Klaassen44595162015-05-28 17:55:20 -070038public class CalculatorText extends AlignedTextView implements View.OnLongClickListener {
Alan Viverette461992d2014-03-07 13:29:56 -080039
Justin Klaassen44595162015-05-28 17:55:20 -070040 private final ActionMode.Callback mPasteActionModeCallback = new ActionMode.Callback() {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080041 @Override
42 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -070043 if (item.getItemId() == R.id.menu_paste) {
44 paste();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070045 mode.finish();
46 return true;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070047 }
Justin Klaassenfc5ac822015-06-18 13:15:17 -070048 return false;
Gilles Debunnef57b8b42011-01-27 10:54:07 -080049 }
50
51 @Override
52 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -070053 final ClipboardManager clipboard = (ClipboardManager) getContext()
54 .getSystemService(Context.CLIPBOARD_SERVICE);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070055 if (clipboard.hasPrimaryClip()) {
56 MenuInflater inflater = mode.getMenuInflater();
57 inflater.inflate(R.menu.paste, menu);
58 return true;
59 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080060 // Prevents the selection action mode on double tap.
61 return false;
62 }
63
64 @Override
Gilles Debunnef57b8b42011-01-27 10:54:07 -080065 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
66 return false;
67 }
Justin Klaassenfc5ac822015-06-18 13:15:17 -070068
69 @Override
70 public void onDestroyActionMode(ActionMode mode) {
71 mActionMode = null;
72 }
Justin Klaassen4b3af052014-05-27 17:53:10 -070073 };
74
Justin Klaassenfc5ac822015-06-18 13:15:17 -070075 // Temporary paint for use in layout methods.
76 private final TextPaint mTempPaint = new TextPaint();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070077
Justin Klaassen4b3af052014-05-27 17:53:10 -070078 private final float mMaximumTextSize;
79 private final float mMinimumTextSize;
80 private final float mStepTextSize;
81
82 private int mWidthConstraint = -1;
Justin Klaassenfc5ac822015-06-18 13:15:17 -070083
84 private ActionMode mActionMode;
85
86 private OnPasteListener mOnPasteListener;
Justin Klaassenfed941a2014-06-09 18:42:40 +010087 private OnTextSizeChangeListener mOnTextSizeChangeListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070088
Hans Boehm08e8f322015-04-21 13:18:38 -070089 public CalculatorText(Context context) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -070090 this(context, null /* attrs */);
Justin Klaassen4b3af052014-05-27 17:53:10 -070091 }
92
Hans Boehm08e8f322015-04-21 13:18:38 -070093 public CalculatorText(Context context, AttributeSet attrs) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -070094 this(context, attrs, 0 /* defStyleAttr */);
Justin Klaassen4b3af052014-05-27 17:53:10 -070095 }
96
Justin Klaassenfc5ac822015-06-18 13:15:17 -070097 public CalculatorText(Context context, AttributeSet attrs, int defStyleAttr) {
98 super(context, attrs, defStyleAttr);
Justin Klaassen4b3af052014-05-27 17:53:10 -070099
100 final TypedArray a = context.obtainStyledAttributes(
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700101 attrs, R.styleable.CalculatorText, defStyleAttr, 0);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700102 mMaximumTextSize = a.getDimension(
Hans Boehm08e8f322015-04-21 13:18:38 -0700103 R.styleable.CalculatorText_maxTextSize, getTextSize());
Justin Klaassen4b3af052014-05-27 17:53:10 -0700104 mMinimumTextSize = a.getDimension(
Hans Boehm08e8f322015-04-21 13:18:38 -0700105 R.styleable.CalculatorText_minTextSize, getTextSize());
106 mStepTextSize = a.getDimension(R.styleable.CalculatorText_stepTextSize,
Justin Klaassen4b3af052014-05-27 17:53:10 -0700107 (mMaximumTextSize - mMinimumTextSize) / 3);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700108 a.recycle();
109
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700110 // Allow scrolling by default.
Hans Boehm08e8f322015-04-21 13:18:38 -0700111 setMovementMethod(ScrollingMovementMethod.getInstance());
112
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700113 // Reset the clickable flag, which is added when specifying a movement method.
114 setClickable(false);
115
116 // Add a long click to start the ActionMode manually.
117 setOnLongClickListener(this);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700118 }
119
120 @Override
Hans Boehm76b78152015-04-17 10:50:35 -0700121 public boolean onLongClick(View v) {
Hans Boehm1176f232015-05-11 16:26:03 -0700122 mActionMode = startActionMode(mPasteActionModeCallback, ActionMode.TYPE_FLOATING);
Hans Boehm76b78152015-04-17 10:50:35 -0700123 return true;
124 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700125
126 @Override
127 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Justin Klaassen44595162015-05-28 17:55:20 -0700128 // Prevent shrinking/resizing with our variable textSize.
129 if (!isLaidOut()) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700130 setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
Justin Klaassen44595162015-05-28 17:55:20 -0700131 setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
132 }
133
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700134 // Re-calculate our textSize based on new width.
135 final int width = MeasureSpec.getSize(widthMeasureSpec)
Justin Klaassen44595162015-05-28 17:55:20 -0700136 - getPaddingLeft() - getPaddingRight();
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700137 if (mWidthConstraint != width) {
138 mWidthConstraint = width;
139 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText()));
140 }
141
142 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700143 }
144
Hans Boehm84614952014-11-25 18:46:17 -0800145 public int getWidthConstraint() { return mWidthConstraint; }
146
Justin Klaassen4b3af052014-05-27 17:53:10 -0700147 @Override
148 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
149 super.onTextChanged(text, start, lengthBefore, lengthAfter);
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -0700150
Justin Klaassen4b3af052014-05-27 17:53:10 -0700151 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
152 }
153
Justin Klaassenfed941a2014-06-09 18:42:40 +0100154 @Override
155 public void setTextSize(int unit, float size) {
156 final float oldTextSize = getTextSize();
157 super.setTextSize(unit, size);
158
159 if (mOnTextSizeChangeListener != null && getTextSize() != oldTextSize) {
160 mOnTextSizeChangeListener.onTextSizeChanged(this, oldTextSize);
161 }
162 }
163
Justin Klaassen44595162015-05-28 17:55:20 -0700164 public float getMinimumTextSize() {
165 return mMinimumTextSize;
166 }
167
168 public float getMaximumTextSize() {
169 return mMaximumTextSize;
170 }
171
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700172 public float getVariableTextSize(CharSequence text) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700173 if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) {
174 // Not measured, bail early.
175 return getTextSize();
176 }
177
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700178 // Capture current paint state.
179 mTempPaint.set(getPaint());
180
181 // Step through increasing text sizes until the text would no longer fit.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700182 float lastFitTextSize = mMinimumTextSize;
183 while (lastFitTextSize < mMaximumTextSize) {
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700184 mTempPaint.setTextSize(Math.min(lastFitTextSize + mStepTextSize, mMaximumTextSize));
185 if (Layout.getDesiredWidth(text, mTempPaint) > mWidthConstraint) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700186 break;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700187 }
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700188 lastFitTextSize = mTempPaint.getTextSize();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700189 }
190
191 return lastFitTextSize;
192 }
193
Hans Boehm1176f232015-05-11 16:26:03 -0700194 public boolean stopActionMode() {
195 if (mActionMode != null) {
196 mActionMode.finish();
197 return true;
198 }
199 return false;
200 }
201
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700202 public void setOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
203 mOnTextSizeChangeListener = listener;
204 }
205
206 public void setOnPasteListener(OnPasteListener listener) {
207 mOnPasteListener = listener;
208 }
209
210 private void paste() {
211 final ClipboardManager clipboard = (ClipboardManager) getContext()
212 .getSystemService(Context.CLIPBOARD_SERVICE);
213 final ClipData primaryClip = clipboard.getPrimaryClip();
214 if (primaryClip != null && mOnPasteListener != null) {
215 mOnPasteListener.onPaste(primaryClip);
216 }
217 }
218
Justin Klaassenfed941a2014-06-09 18:42:40 +0100219 public interface OnTextSizeChangeListener {
220 void onTextSizeChanged(TextView textView, float oldSize);
221 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700222
Justin Klaassenfc5ac822015-06-18 13:15:17 -0700223 public interface OnPasteListener {
224 boolean onPaste(ClipData clip);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700225 }
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800226}