summaryrefslogtreecommitdiffstats
path: root/core/java/android/text
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/text')
-rw-r--r--core/java/android/text/BoringLayout.java9
-rw-r--r--core/java/android/text/InputType.java9
-rw-r--r--core/java/android/text/StaticLayout.java8
-rw-r--r--core/java/android/text/TextPaint.java2
-rw-r--r--core/java/android/text/format/DateUtils.java58
-rw-r--r--core/java/android/text/format/Formatter.java35
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java81
-rw-r--r--core/java/android/text/method/CharacterPickerDialog.java30
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java44
-rw-r--r--core/java/android/text/method/Touch.java18
-rw-r--r--core/java/android/text/style/AbsoluteSizeSpan.java32
-rw-r--r--core/java/android/text/style/ImageSpan.java2
-rw-r--r--core/java/android/text/style/LineHeightSpan.java7
-rw-r--r--core/java/android/text/util/Linkify.java2
-rw-r--r--core/java/android/text/util/Regex.java2
-rw-r--r--core/java/android/text/util/Rfc822InputFilter.java58
-rw-r--r--core/java/android/text/util/Rfc822Token.java26
-rw-r--r--core/java/android/text/util/Rfc822Tokenizer.java25
18 files changed, 398 insertions, 50 deletions
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 843754b..944f735 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -19,6 +19,7 @@ package android.text;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.text.style.ParagraphStyle;
import android.util.FloatMath;
/**
@@ -262,6 +263,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
TextUtils.recycle(temp);
+ if (boring && text instanceof Spanned) {
+ Spanned sp = (Spanned) text;
+ Object[] styles = sp.getSpans(0, text.length(), ParagraphStyle.class);
+ if (styles.length > 0) {
+ boring = false;
+ }
+ }
+
if (boring) {
Metrics fm = metrics;
if (fm == null) {
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index 41d5b84..8592221 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -146,6 +146,15 @@ public interface InputType {
*/
public static final int TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000;
+ /**
+ * Flag for {@link #TYPE_CLASS_TEXT}: the input method does not need to
+ * display any dictionary-based candidates. This is useful for text views that
+ * do not contain words from the language and do not benefit from any
+ * dictionary-based completions or corrections. It overrides the
+ * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set.
+ */
+ public static final int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000;
+
// ----------------------------------------------------------------------
/**
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index c133cf2..f0a5ffd 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -968,7 +968,13 @@ extends Layout
fm.bottom = bottom;
for (int i = 0; i < chooseht.length; i++) {
- chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm);
+ if (chooseht[i] instanceof LineHeightSpan.WithDensity) {
+ ((LineHeightSpan.WithDensity) chooseht[i]).
+ chooseHeight(text, start, end, choosehtv[i], v, fm, paint);
+
+ } else {
+ chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm);
+ }
}
above = fm.ascent;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index f13820d..f9e7cac 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -27,6 +27,7 @@ public class TextPaint extends Paint {
public int baselineShift;
public int linkColor;
public int[] drawableState;
+ public float density = 1.0f;
public TextPaint() {
super();
@@ -51,5 +52,6 @@ public class TextPaint extends Paint {
baselineShift = tp.baselineShift;
linkColor = tp.linkColor;
drawableState = tp.drawableState;
+ density = tp.density;
}
}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 1a4eb69..9dd8ceb 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -25,7 +25,9 @@ import android.pim.DateException;
import java.util.Calendar;
import java.util.Date;
+import java.util.Formatter;
import java.util.GregorianCalendar;
+import java.util.Locale;
import java.util.TimeZone;
/**
@@ -1040,6 +1042,31 @@ public class DateUtils
/**
* Formats a date or a time range according to the local conventions.
+ * <p>
+ * Note that this is a convenience method. Using it involves creating an
+ * internal {@link java.util.Formatter} instance on-the-fly, which is
+ * somewhat costly in terms of memory and time. This is probably acceptable
+ * if you use the method only rarely, but if you rely on it for formatting a
+ * large number of dates, consider creating and reusing your own
+ * {@link java.util.Formatter} instance and use the version of
+ * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+ * that takes a {@link java.util.Formatter}.
+ *
+ * @param context the context is required only if the time is shown
+ * @param startMillis the start time in UTC milliseconds
+ * @param endMillis the end time in UTC milliseconds
+ * @param flags a bit mask of options See
+ * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+ * @return a string containing the formatted date/time range.
+ */
+ public static String formatDateRange(Context context, long startMillis,
+ long endMillis, int flags) {
+ Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
+ return formatDateRange(context, f, startMillis, endMillis, flags).toString();
+ }
+
+ /**
+ * Formats a date or a time range according to the local conventions.
*
* <p>
* Example output strings (date formats in these examples are shown using
@@ -1181,14 +1208,17 @@ public class DateUtils
* instead of "December 31, 2008".
*
* @param context the context is required only if the time is shown
+ * @param formatter the Formatter used for formatting the date range.
+ * Note: be sure to call setLength(0) on StringBuilder passed to
+ * the Formatter constructor unless you want the results to accumulate.
* @param startMillis the start time in UTC milliseconds
* @param endMillis the end time in UTC milliseconds
* @param flags a bit mask of options
*
- * @return a string containing the formatted date/time range.
+ * @return the formatter with the formatted date/time range appended to the string buffer.
*/
- public static String formatDateRange(Context context, long startMillis,
- long endMillis, int flags) {
+ public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
+ long endMillis, int flags) {
Resources res = Resources.getSystem();
boolean showTime = (flags & FORMAT_SHOW_TIME) != 0;
boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0;
@@ -1423,8 +1453,7 @@ public class DateUtils
if (noMonthDay && startMonthNum == endMonthNum) {
// Example: "January, 2008"
- String startDateString = startDate.format(defaultDateFormat);
- return startDateString;
+ return formatter.format("%s", startDate.format(defaultDateFormat));
}
if (startYear != endYear || noMonthDay) {
@@ -1436,10 +1465,9 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startDateString, startTimeString,
endWeekDayString, endDateString, endTimeString);
- return dateRange;
}
// Get the month, day, and year strings for the start and end dates
@@ -1476,12 +1504,11 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startMonthString, startMonthDayString,
startYearString, startTimeString,
endWeekDayString, endMonthString, endMonthDayString,
endYearString, endTimeString);
- return dateRange;
}
if (startDay != endDay) {
@@ -1496,12 +1523,11 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startMonthString, startMonthDayString,
startYearString, startTimeString,
endWeekDayString, endMonthString, endMonthDayString,
endYearString, endTimeString);
- return dateRange;
}
// Same start and end day
@@ -1522,6 +1548,7 @@ public class DateUtils
} else {
// Example: "10:00 - 11:00 am"
String timeFormat = res.getString(com.android.internal.R.string.time1_time2);
+ // Don't use the user supplied Formatter because the result will pollute the buffer.
timeString = String.format(timeFormat, startTimeString, endTimeString);
}
}
@@ -1545,7 +1572,7 @@ public class DateUtils
fullFormat = res.getString(com.android.internal.R.string.time_date);
} else {
// Example: "Oct 9"
- return dateString;
+ return formatter.format("%s", dateString);
}
}
} else if (showWeekDay) {
@@ -1554,16 +1581,15 @@ public class DateUtils
fullFormat = res.getString(com.android.internal.R.string.time_wday);
} else {
// Example: "Tue"
- return startWeekDayString;
+ return formatter.format("%s", startWeekDayString);
}
} else if (showTime) {
- return timeString;
+ return formatter.format("%s", timeString);
}
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat, timeString, startWeekDayString, dateString);
- return dateRange;
+ return formatter.format(fullFormat, timeString, startWeekDayString, dateString);
}
/**
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 367b26c..baaa3ce 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -32,6 +32,18 @@ public final class Formatter {
* @return formated string with the number
*/
public static String formatFileSize(Context context, long number) {
+ return formatFileSize(context, number, false);
+ }
+
+ /**
+ * Like {@link #formatFileSize}, but trying to generate shorter numbers
+ * (showing fewer digits of precisin).
+ */
+ public static String formatShortFileSize(Context context, long number) {
+ return formatFileSize(context, number, true);
+ }
+
+ private static String formatFileSize(Context context, long number, boolean shorter) {
if (context == null) {
return "";
}
@@ -58,13 +70,24 @@ public final class Formatter {
suffix = com.android.internal.R.string.petabyteShort;
result = result / 1024;
}
- if (result < 100) {
- String value = String.format("%.2f", result);
- return context.getResources().
- getString(com.android.internal.R.string.fileSizeSuffix,
- value, context.getString(suffix));
+ String value;
+ if (result < 1) {
+ value = String.format("%.2f", result);
+ } else if (result < 10) {
+ if (shorter) {
+ value = String.format("%.1f", result);
+ } else {
+ value = String.format("%.2f", result);
+ }
+ } else if (result < 100) {
+ if (shorter) {
+ value = String.format("%.0f", result);
+ } else {
+ value = String.format("%.2f", result);
+ }
+ } else {
+ value = String.format("%.0f", result);
}
- String value = String.format("%.0f", result);
return context.getResources().
getString(com.android.internal.R.string.fileSizeSuffix,
value, context.getString(suffix));
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 92f6289..ab33cb3 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -22,6 +22,7 @@ import android.graphics.Rect;
import android.text.*;
import android.widget.TextView;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.MotionEvent;
// XXX this doesn't extend MetaKeyKeyListener because the signatures
@@ -256,8 +257,32 @@ implements MovementMethod
(MetaKeyKeyListener.getMetaState(buffer,
MetaKeyKeyListener.META_SELECTING) != 0);
+ DoubleTapState[] tap = buffer.getSpans(0, buffer.length(),
+ DoubleTapState.class);
+ boolean doubletap = false;
+
+ if (tap.length > 0) {
+ if (event.getEventTime() - tap[0].mWhen <=
+ ViewConfiguration.getDoubleTapTimeout()) {
+ if (sameWord(buffer, off, Selection.getSelectionEnd(buffer))) {
+ doubletap = true;
+ }
+ }
+
+ tap[0].mWhen = event.getEventTime();
+ } else {
+ DoubleTapState newtap = new DoubleTapState();
+ newtap.mWhen = event.getEventTime();
+ buffer.setSpan(newtap, 0, buffer.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
if (cap) {
Selection.extendSelection(buffer, off);
+ } else if (doubletap) {
+ Selection.setSelection(buffer,
+ findWordStart(buffer, off),
+ findWordEnd(buffer, off));
} else {
Selection.setSelection(buffer, off);
}
@@ -272,6 +297,62 @@ implements MovementMethod
return handled;
}
+ private static class DoubleTapState implements NoCopySpan {
+ long mWhen;
+ }
+
+ private static boolean sameWord(CharSequence text, int one, int two) {
+ int start = findWordStart(text, one);
+ int end = findWordEnd(text, one);
+
+ if (end == start) {
+ return false;
+ }
+
+ return start == findWordStart(text, two) &&
+ end == findWordEnd(text, two);
+ }
+
+ // TODO: Unify with TextView.getWordForDictionary()
+ private static int findWordStart(CharSequence text, int start) {
+ for (; start > 0; start--) {
+ char c = text.charAt(start - 1);
+ int type = Character.getType(c);
+
+ if (c != '\'' &&
+ type != Character.UPPERCASE_LETTER &&
+ type != Character.LOWERCASE_LETTER &&
+ type != Character.TITLECASE_LETTER &&
+ type != Character.MODIFIER_LETTER &&
+ type != Character.DECIMAL_DIGIT_NUMBER) {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ // TODO: Unify with TextView.getWordForDictionary()
+ private static int findWordEnd(CharSequence text, int end) {
+ int len = text.length();
+
+ for (; end < len; end++) {
+ char c = text.charAt(end);
+ int type = Character.getType(c);
+
+ if (c != '\'' &&
+ type != Character.UPPERCASE_LETTER &&
+ type != Character.LOWERCASE_LETTER &&
+ type != Character.TITLECASE_LETTER &&
+ type != Character.MODIFIER_LETTER &&
+ type != Character.DECIMAL_DIGIT_NUMBER) {
+ break;
+ }
+ }
+
+ return end;
+ }
+
public boolean canSelectArbitrarily() {
return true;
}
diff --git a/core/java/android/text/method/CharacterPickerDialog.java b/core/java/android/text/method/CharacterPickerDialog.java
index 3c406751..880e46d 100644
--- a/core/java/android/text/method/CharacterPickerDialog.java
+++ b/core/java/android/text/method/CharacterPickerDialog.java
@@ -25,15 +25,14 @@ import android.text.*;
import android.view.LayoutInflater;
import android.view.View.OnClickListener;
import android.view.View;
-import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
+import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
-import android.widget.TextView;
/**
* Dialog for choosing accented characters related to a base character.
@@ -45,6 +44,7 @@ public class CharacterPickerDialog extends Dialog
private String mOptions;
private boolean mInsert;
private LayoutInflater mInflater;
+ private Button mCancelButton;
/**
* Creates a new CharacterPickerDialog that presents the specified
@@ -54,7 +54,7 @@ public class CharacterPickerDialog extends Dialog
public CharacterPickerDialog(Context context, View view,
Editable text, String options,
boolean insert) {
- super(context);
+ super(context, com.android.internal.R.style.Theme_Panel);
mView = view;
mText = text;
@@ -70,28 +70,32 @@ public class CharacterPickerDialog extends Dialog
WindowManager.LayoutParams params = getWindow().getAttributes();
params.token = mView.getApplicationWindowToken();
params.type = params.TYPE_APPLICATION_ATTACHED_DIALOG;
+ params.flags = params.flags | Window.FEATURE_NO_TITLE;
- setTitle(R.string.select_character);
setContentView(R.layout.character_picker);
GridView grid = (GridView) findViewById(R.id.characterPicker);
grid.setAdapter(new OptionsAdapter(getContext()));
grid.setOnItemClickListener(this);
- findViewById(R.id.cancel).setOnClickListener(this);
+ mCancelButton = (Button) findViewById(R.id.cancel);
+ mCancelButton.setOnClickListener(this);
}
/**
* Handles clicks on the character buttons.
*/
public void onItemClick(AdapterView parent, View view, int position, long id) {
- int selEnd = Selection.getSelectionEnd(mText);
String result = String.valueOf(mOptions.charAt(position));
+ replaceCharacterAndClose(result);
+ }
+ private void replaceCharacterAndClose(CharSequence replace) {
+ int selEnd = Selection.getSelectionEnd(mText);
if (mInsert || selEnd == 0) {
- mText.insert(selEnd, result);
+ mText.insert(selEnd, replace);
} else {
- mText.replace(selEnd - 1, selEnd, result);
+ mText.replace(selEnd - 1, selEnd, replace);
}
dismiss();
@@ -101,21 +105,25 @@ public class CharacterPickerDialog extends Dialog
* Handles clicks on the Cancel button.
*/
public void onClick(View v) {
- dismiss();
+ if (v == mCancelButton) {
+ dismiss();
+ } else if (v instanceof Button) {
+ CharSequence result = ((Button) v).getText();
+ replaceCharacterAndClose(result);
+ }
}
private class OptionsAdapter extends BaseAdapter {
- private Context mContext;
public OptionsAdapter(Context context) {
super();
- mContext = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
Button b = (Button)
mInflater.inflate(R.layout.character_picker_button, null);
b.setText(String.valueOf(mOptions.charAt(position)));
+ b.setOnClickListener(CharacterPickerDialog.this);
return b;
}
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index e420c27..2e76470 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -401,20 +401,17 @@ public class QwertyKeyListener extends BaseKeyListener {
private static SparseArray<String> PICKER_SETS =
new SparseArray<String>();
static {
- PICKER_SETS.put('!', "\u00A1");
- PICKER_SETS.put('<', "\u00AB");
- PICKER_SETS.put('>', "\u00BB");
- PICKER_SETS.put('?', "\u00BF");
PICKER_SETS.put('A', "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100");
PICKER_SETS.put('C', "\u00C7\u0106\u010C");
PICKER_SETS.put('D', "\u010E");
PICKER_SETS.put('E', "\u00C8\u00C9\u00CA\u00CB\u0118\u011A\u0112");
+ PICKER_SETS.put('G', "\u011E");
PICKER_SETS.put('L', "\u0141");
- PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF\u012A");
+ PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF\u012A\u0130");
PICKER_SETS.put('N', "\u00D1\u0143\u0147");
PICKER_SETS.put('O', "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6\u014C");
PICKER_SETS.put('R', "\u0158");
- PICKER_SETS.put('S', "\u015A\u0160");
+ PICKER_SETS.put('S', "\u015A\u0160\u015E");
PICKER_SETS.put('T', "\u0164");
PICKER_SETS.put('U', "\u00D9\u00DA\u00DB\u00DC\u016E\u016A");
PICKER_SETS.put('Y', "\u00DD\u0178");
@@ -423,18 +420,47 @@ public class QwertyKeyListener extends BaseKeyListener {
PICKER_SETS.put('c', "\u00E7\u0107\u010D");
PICKER_SETS.put('d', "\u010F");
PICKER_SETS.put('e', "\u00E8\u00E9\u00EA\u00EB\u0119\u011B\u0113");
- PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF\u012B");
+ PICKER_SETS.put('g', "\u011F");
+ PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF\u012B\u0131");
PICKER_SETS.put('l', "\u0142");
PICKER_SETS.put('n', "\u00F1\u0144\u0148");
PICKER_SETS.put('o', "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6\u014D");
PICKER_SETS.put('r', "\u0159");
- PICKER_SETS.put('s', "\u00A7\u00DF\u015B\u0161");
+ PICKER_SETS.put('s', "\u00A7\u00DF\u015B\u0161\u015F");
PICKER_SETS.put('t', "\u0165");
PICKER_SETS.put('u', "\u00F9\u00FA\u00FB\u00FC\u016F\u016B");
PICKER_SETS.put('y', "\u00FD\u00FF");
PICKER_SETS.put('z', "\u017A\u017C\u017E");
PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT,
- "\u2026\u00A5\u2022\u00AE\u00A9\u00B1");
+ "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\");
+ PICKER_SETS.put('/', "\\");
+
+ // From packages/inputmethods/LatinIME/res/xml/kbd_symbols.xml
+
+ PICKER_SETS.put('1', "\u00b9\u00bd\u2153\u00bc\u215b");
+ PICKER_SETS.put('2', "\u00b2\u2154");
+ PICKER_SETS.put('3', "\u00b3\u00be\u215c");
+ PICKER_SETS.put('4', "\u2074");
+ PICKER_SETS.put('5', "\u215d");
+ PICKER_SETS.put('7', "\u215e");
+ PICKER_SETS.put('0', "\u207f\u2205");
+ PICKER_SETS.put('$', "\u00a2\u00a3\u20ac\u00a5\u20a3\u20a4\u20b1");
+ PICKER_SETS.put('%', "\u2030");
+ PICKER_SETS.put('*', "\u2020\u2021");
+ PICKER_SETS.put('-', "\u2013\u2014");
+ PICKER_SETS.put('+', "\u00b1");
+ PICKER_SETS.put('(', "[{<");
+ PICKER_SETS.put(')', "]}>");
+ PICKER_SETS.put('!', "\u00a1");
+ PICKER_SETS.put('"', "\u201c\u201d\u00ab\u00bb\u02dd");
+ PICKER_SETS.put('?', "\u00bf");
+ PICKER_SETS.put(',', "\u201a\u201e");
+
+ // From packages/inputmethods/LatinIME/res/xml/kbd_symbols_shift.xml
+
+ PICKER_SETS.put('=', "\u2260\u2248\u221e");
+ PICKER_SETS.put('<', "\u2264\u00ab\u2039");
+ PICKER_SETS.put('>', "\u2265\u00bb\u203a");
};
private boolean showCharacterPicker(View view, Editable content, char c,
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index dfc16f5..6995107 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -72,6 +72,24 @@ public class Touch {
}
/**
+ * @hide
+ * Returns the maximum scroll value in x.
+ */
+ public static int getMaxScrollX(TextView widget, Layout layout, int y) {
+ int top = layout.getLineForVertical(y);
+ int bottom = layout.getLineForVertical(y + widget.getHeight()
+ - widget.getTotalPaddingTop() -widget.getTotalPaddingBottom());
+ int left = Integer.MAX_VALUE;
+ int right = 0;
+ for (int i = top; i <= bottom; i++) {
+ left = (int) Math.min(left, layout.getLineLeft(i));
+ right = (int) Math.max(right, layout.getLineRight(i));
+ }
+ return right - left - widget.getWidth() - widget.getTotalPaddingLeft()
+ - widget.getTotalPaddingRight();
+ }
+
+ /**
* Handles touch events for dragging. You may want to do other actions
* like moving the cursor on touch as well.
*/
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 484f8ce..1214040 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -24,13 +24,28 @@ import android.text.TextUtils;
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mSize;
+ private boolean mDip;
+ /**
+ * Set the text size to <code>size</code> physical pixels.
+ */
public AbsoluteSizeSpan(int size) {
mSize = size;
}
+ /**
+ * Set the text size to <code>size</code> physical pixels,
+ * or to <code>size</code> device-independent pixels if
+ * <code>dip</code> is true.
+ */
+ public AbsoluteSizeSpan(int size, boolean dip) {
+ mSize = size;
+ mDip = dip;
+ }
+
public AbsoluteSizeSpan(Parcel src) {
mSize = src.readInt();
+ mDip = src.readInt() != 0;
}
public int getSpanTypeId() {
@@ -43,19 +58,32 @@ public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableS
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mSize);
+ dest.writeInt(mDip ? 1 : 0);
}
public int getSize() {
return mSize;
}
+ public boolean getDip() {
+ return mDip;
+ }
+
@Override
public void updateDrawState(TextPaint ds) {
- ds.setTextSize(mSize);
+ if (mDip) {
+ ds.setTextSize(mSize * ds.density);
+ } else {
+ ds.setTextSize(mSize);
+ }
}
@Override
public void updateMeasureState(TextPaint ds) {
- ds.setTextSize(mSize);
+ if (mDip) {
+ ds.setTextSize(mSize * ds.density);
+ } else {
+ ds.setTextSize(mSize);
+ }
}
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 86ef5f6..74b9463 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -36,6 +36,7 @@ public class ImageSpan extends DynamicDrawableSpan {
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
+ @Deprecated
public ImageSpan(Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -43,6 +44,7 @@ public class ImageSpan extends DynamicDrawableSpan {
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int) instead.
*/
+ @Deprecated
public ImageSpan(Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index c0ef97c..44a1706 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,6 +19,7 @@ package android.text.style;
import android.graphics.Paint;
import android.graphics.Canvas;
import android.text.Layout;
+import android.text.TextPaint;
public interface LineHeightSpan
extends ParagraphStyle, WrapTogetherSpan
@@ -26,4 +27,10 @@ extends ParagraphStyle, WrapTogetherSpan
public void chooseHeight(CharSequence text, int start, int end,
int spanstartv, int v,
Paint.FontMetricsInt fm);
+
+ public interface WithDensity extends LineHeightSpan {
+ public void chooseHeight(CharSequence text, int start, int end,
+ int spanstartv, int v,
+ Paint.FontMetricsInt fm, TextPaint paint);
+ }
}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index d61e888..ce25c47 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -208,7 +208,7 @@ public class Linkify {
if ((mask & WEB_URLS) != 0) {
gatherLinks(links, text, Regex.WEB_URL_PATTERN,
- new String[] { "http://", "https://" },
+ new String[] { "http://", "https://", "rtsp://" },
sUrlMatchFilter, null);
}
diff --git a/core/java/android/text/util/Regex.java b/core/java/android/text/util/Regex.java
index a349b82..a6844a4 100644
--- a/core/java/android/text/util/Regex.java
+++ b/core/java/android/text/util/Regex.java
@@ -65,7 +65,7 @@ public class Regex {
*/
public static final Pattern WEB_URL_PATTERN
= Pattern.compile(
- "((?:(http|https|Http|Https):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+ "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+ "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+" // named host
diff --git a/core/java/android/text/util/Rfc822InputFilter.java b/core/java/android/text/util/Rfc822InputFilter.java
new file mode 100644
index 0000000..8c8b7fc
--- /dev/null
+++ b/core/java/android/text/util/Rfc822InputFilter.java
@@ -0,0 +1,58 @@
+package android.text.util;
+
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+
+/**
+ * Implements special address cleanup rules:
+ * The first space key entry following an "@" symbol that is followed by any combination
+ * of letters and symbols, including one+ dots and zero commas, should insert an extra
+ * comma (followed by the space).
+ *
+ * @hide
+ */
+public class Rfc822InputFilter implements InputFilter {
+
+ public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
+ int dstart, int dend) {
+
+ // quick check - did they enter a single space?
+ if (end-start != 1 || source.charAt(start) != ' ') {
+ return null;
+ }
+
+ // determine if the characters before the new space fit the pattern
+ // follow backwards and see if we find a comma, dot, or @
+ int scanBack = dstart;
+ boolean dotFound = false;
+ while (scanBack > 0) {
+ char c = dest.charAt(--scanBack);
+ switch (c) {
+ case '.':
+ dotFound = true; // one or more dots are req'd
+ break;
+ case ',':
+ return null;
+ case '@':
+ if (!dotFound) {
+ return null;
+ }
+ // we have found a comma-insert case. now just do it
+ // in the least expensive way we can.
+ if (source instanceof Spanned) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(",");
+ sb.append(source);
+ return sb;
+ } else {
+ return ", ";
+ }
+ default:
+ // just keep going
+ }
+ }
+
+ // no termination cases were found, so don't edit the input
+ return null;
+ }
+}
diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java
index 7fe11bc..0edeeb5 100644
--- a/core/java/android/text/util/Rfc822Token.java
+++ b/core/java/android/text/util/Rfc822Token.java
@@ -168,5 +168,31 @@ public class Rfc822Token {
return sb.toString();
}
+
+ public int hashCode() {
+ int result = 17;
+ if (mName != null) result = 31 * result + mName.hashCode();
+ if (mAddress != null) result = 31 * result + mAddress.hashCode();
+ if (mComment != null) result = 31 * result + mComment.hashCode();
+ return result;
+ }
+
+ private static boolean stringEquals(String a, String b) {
+ if (a == null) {
+ return (b == null);
+ } else {
+ return (a.equals(b));
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Rfc822Token)) {
+ return false;
+ }
+ Rfc822Token other = (Rfc822Token) o;
+ return (stringEquals(mName, other.mName) &&
+ stringEquals(mAddress, other.mAddress) &&
+ stringEquals(mComment, other.mComment));
+ }
}
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index d4e78b0..cb39f7d 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -19,6 +19,7 @@ package android.text.util;
import android.widget.MultiAutoCompleteTextView;
import java.util.ArrayList;
+import java.util.Collection;
/**
* This class works as a Tokenizer for MultiAutoCompleteTextView for
@@ -27,18 +28,22 @@ import java.util.ArrayList;
* into a series of Rfc822Tokens.
*/
public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
+
/**
* This constructor will try to take a string like
* "Foo Bar (something) &lt;foo\@google.com&gt;,
* blah\@google.com (something)"
- * and convert it into one or more Rfc822Tokens.
+ * and convert it into one or more Rfc822Tokens, output into the supplied
+ * collection.
+ *
* It does *not* decode MIME encoded-words; charset conversion
* must already have taken place if necessary.
* It will try to be tolerant of broken syntax instead of
* returning an error.
+ *
+ * @hide
*/
- public static Rfc822Token[] tokenize(CharSequence text) {
- ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
+ public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
StringBuilder name = new StringBuilder();
StringBuilder address = new StringBuilder();
StringBuilder comment = new StringBuilder();
@@ -148,7 +153,21 @@ public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
name.toString(),
comment.toString()));
}
+ }
+ /**
+ * This method will try to take a string like
+ * "Foo Bar (something) &lt;foo\@google.com&gt;,
+ * blah\@google.com (something)"
+ * and convert it into one or more Rfc822Tokens.
+ * It does *not* decode MIME encoded-words; charset conversion
+ * must already have taken place if necessary.
+ * It will try to be tolerant of broken syntax instead of
+ * returning an error.
+ */
+ public static Rfc822Token[] tokenize(CharSequence text) {
+ ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
+ tokenize(text, out);
return out.toArray(new Rfc822Token[out.size()]);
}