blob: c6f38aea361f3368481b7285a66ffedd244562a4 [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;
Hongwei Wang245925e2014-05-11 14:38:47 -070023import android.graphics.Paint;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070024import android.net.Uri;
Justin Klaassen4b3af052014-05-27 17:53:10 -070025import android.text.TextPaint;
Justin Klaassen44595162015-05-28 17:55:20 -070026import android.text.method.ScrollingMovementMethod;
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;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070031import android.view.MenuInflater;
Gilles Debunnef57b8b42011-01-27 10:54:07 -080032import android.view.MenuItem;
Hans Boehm76b78152015-04-17 10:50:35 -070033import android.view.View;
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/**
Hans Boehm08e8f322015-04-21 13:18:38 -070037 * TextView adapted for Calculator display.
Hans Boehm84614952014-11-25 18:46:17 -080038 */
Justin Klaassen44595162015-05-28 17:55:20 -070039public class CalculatorText extends AlignedTextView implements View.OnLongClickListener {
Alan Viverette461992d2014-03-07 13:29:56 -080040
Hans Boehm1176f232015-05-11 16:26:03 -070041 private ActionMode mActionMode;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070042
Justin Klaassen44595162015-05-28 17:55:20 -070043 private final ActionMode.Callback mPasteActionModeCallback = new ActionMode.Callback() {
Gilles Debunnef57b8b42011-01-27 10:54:07 -080044 @Override
45 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070046 switch (item.getItemId()) {
47 case R.id.menu_paste:
48 pasteContent();
49 mode.finish();
50 return true;
51 default:
52 return false;
53 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080054 }
55
56 @Override
57 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070058 ClipboardManager clipboard =
59 (ClipboardManager) getContext().getSystemService(
60 Context.CLIPBOARD_SERVICE);
61 if (clipboard.hasPrimaryClip()) {
62 MenuInflater inflater = mode.getMenuInflater();
63 inflater.inflate(R.menu.paste, menu);
64 return true;
65 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080066 // Prevents the selection action mode on double tap.
67 return false;
68 }
69
70 @Override
Alan Viverette461992d2014-03-07 13:29:56 -080071 public void onDestroyActionMode(ActionMode mode) {
Hans Boehm1176f232015-05-11 16:26:03 -070072 mActionMode = null;
Alan Viverette461992d2014-03-07 13:29:56 -080073 }
Gilles Debunnef57b8b42011-01-27 10:54:07 -080074
75 @Override
76 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
77 return false;
78 }
Justin Klaassen4b3af052014-05-27 17:53:10 -070079 };
80
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070081 private PasteListener mPasteListener;
82
83 public void setPasteListener(PasteListener pasteListener) {
84 mPasteListener = pasteListener;
85 }
86
87 private void pasteContent() {
88 ClipboardManager clipboard =
89 (ClipboardManager) getContext().getSystemService(
90 Context.CLIPBOARD_SERVICE);
91 ClipData cd = clipboard.getPrimaryClip();
92 ClipData.Item item = cd.getItemAt(0);
93 // TODO: Should we handle multiple selections?
94 Uri uri = item.getUri();
95 if (uri == null || !mPasteListener.paste(uri)) {
96 mPasteListener.paste(item.coerceToText(getContext()).toString());
97 }
98 }
99
Justin Klaassen4b3af052014-05-27 17:53:10 -0700100 private final float mMaximumTextSize;
101 private final float mMinimumTextSize;
102 private final float mStepTextSize;
103
Justin Klaassen44595162015-05-28 17:55:20 -0700104 // Temporary paint for use in layout methods.
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700105 private final Paint mTempPaint = new TextPaint();
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700106
Justin Klaassen4b3af052014-05-27 17:53:10 -0700107 private int mWidthConstraint = -1;
Justin Klaassenfed941a2014-06-09 18:42:40 +0100108 private OnTextSizeChangeListener mOnTextSizeChangeListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700109
Hans Boehm08e8f322015-04-21 13:18:38 -0700110 public CalculatorText(Context context) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700111 this(context, null);
112 }
113
Hans Boehm08e8f322015-04-21 13:18:38 -0700114 public CalculatorText(Context context, AttributeSet attrs) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700115 this(context, attrs, 0);
116 }
117
Hans Boehm08e8f322015-04-21 13:18:38 -0700118 public CalculatorText(Context context, AttributeSet attrs, int defStyle) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700119 super(context, attrs, defStyle);
120
121 final TypedArray a = context.obtainStyledAttributes(
Hans Boehm08e8f322015-04-21 13:18:38 -0700122 attrs, R.styleable.CalculatorText, defStyle, 0);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700123 mMaximumTextSize = a.getDimension(
Hans Boehm08e8f322015-04-21 13:18:38 -0700124 R.styleable.CalculatorText_maxTextSize, getTextSize());
Justin Klaassen4b3af052014-05-27 17:53:10 -0700125 mMinimumTextSize = a.getDimension(
Hans Boehm08e8f322015-04-21 13:18:38 -0700126 R.styleable.CalculatorText_minTextSize, getTextSize());
127 mStepTextSize = a.getDimension(R.styleable.CalculatorText_stepTextSize,
Justin Klaassen4b3af052014-05-27 17:53:10 -0700128 (mMaximumTextSize - mMinimumTextSize) / 3);
129
130 a.recycle();
131
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700132 // Paste ActionMode is triggered explicitly, not through
133 // setCustomSelectionActionModeCallback.
Hans Boehm76b78152015-04-17 10:50:35 -0700134 setOnLongClickListener(this);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700135
Hans Boehm08e8f322015-04-21 13:18:38 -0700136 // Enable scrolling
137 setMovementMethod(ScrollingMovementMethod.getInstance());
138
Justin Klaassen4b3af052014-05-27 17:53:10 -0700139 setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700140 }
141
142 @Override
Hans Boehm76b78152015-04-17 10:50:35 -0700143 public boolean onLongClick(View v) {
Hans Boehm1176f232015-05-11 16:26:03 -0700144 mActionMode = startActionMode(mPasteActionModeCallback, ActionMode.TYPE_FLOATING);
Hans Boehm76b78152015-04-17 10:50:35 -0700145 return true;
146 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700147
148 @Override
149 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
150 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
151
Justin Klaassen44595162015-05-28 17:55:20 -0700152 // Prevent shrinking/resizing with our variable textSize.
153 if (!isLaidOut()) {
154 setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
155 }
156
157 mWidthConstraint = MeasureSpec.getSize(widthMeasureSpec)
158 - getPaddingLeft() - getPaddingRight();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700159 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText().toString()));
160 }
161
Hans Boehm84614952014-11-25 18:46:17 -0800162 public int getWidthConstraint() { return mWidthConstraint; }
163
Justin Klaassen4b3af052014-05-27 17:53:10 -0700164 @Override
165 protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
166 super.onTextChanged(text, start, lengthBefore, lengthAfter);
Justin Klaassenbfc4e4d2014-08-27 10:56:49 -0700167
Justin Klaassen4b3af052014-05-27 17:53:10 -0700168 setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
169 }
170
Justin Klaassenfed941a2014-06-09 18:42:40 +0100171 @Override
172 public void setTextSize(int unit, float size) {
173 final float oldTextSize = getTextSize();
174 super.setTextSize(unit, size);
175
176 if (mOnTextSizeChangeListener != null && getTextSize() != oldTextSize) {
177 mOnTextSizeChangeListener.onTextSizeChanged(this, oldTextSize);
178 }
179 }
180
181 public void setOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
182 mOnTextSizeChangeListener = listener;
183 }
184
Justin Klaassen44595162015-05-28 17:55:20 -0700185 public float getMinimumTextSize() {
186 return mMinimumTextSize;
187 }
188
189 public float getMaximumTextSize() {
190 return mMaximumTextSize;
191 }
192
Justin Klaassen4b3af052014-05-27 17:53:10 -0700193 public float getVariableTextSize(String text) {
194 if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) {
195 // Not measured, bail early.
196 return getTextSize();
197 }
198
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700199 // Capture current paint state.
200 mTempPaint.set(getPaint());
201
202 // Step through increasing text sizes until the text would no longer fit.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700203 float lastFitTextSize = mMinimumTextSize;
204 while (lastFitTextSize < mMaximumTextSize) {
205 final float nextSize = Math.min(lastFitTextSize + mStepTextSize, mMaximumTextSize);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700206 mTempPaint.setTextSize(nextSize);
207 if (mTempPaint.measureText(text) > mWidthConstraint) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700208 break;
209 } else {
210 lastFitTextSize = nextSize;
211 }
212 }
213
214 return lastFitTextSize;
215 }
216
Hans Boehm1176f232015-05-11 16:26:03 -0700217 public boolean stopActionMode() {
218 if (mActionMode != null) {
219 mActionMode.finish();
220 return true;
221 }
222 return false;
223 }
224
Justin Klaassenfed941a2014-06-09 18:42:40 +0100225 public interface OnTextSizeChangeListener {
226 void onTextSizeChanged(TextView textView, float oldSize);
227 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700228
229 public interface PasteListener {
230 void paste(String s);
231 boolean paste(Uri u);
232 }
Dmitri Plotnikovde3eec22011-01-17 18:23:37 -0800233}