diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 | 
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 | 
| commit | b798689749c64baba81f02e10cf2157c747d6b46 (patch) | |
| tree | da394a395ddb1a6cf69193314846b03fe47a397e /core/java/android/text | |
| parent | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (diff) | |
| download | frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.zip frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.gz frameworks_base-b798689749c64baba81f02e10cf2157c747d6b46.tar.bz2 | |
auto import from //branches/cupcake/...@125939
Diffstat (limited to 'core/java/android/text')
17 files changed, 557 insertions, 89 deletions
| diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index 0ffe4ac..bd86834 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +  package android.text;  import android.text.TextUtils; @@ -104,6 +120,11 @@ public interface InputType {       */      public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000; +    /** +     * Flag for {@link #TYPE_CLASS_TEXT}: flags any text being used as a search string +     */ +    public static final int TYPE_TEXT_FLAG_SEARCH = 0x00040000; +          // ----------------------------------------------------------------------      /** @@ -139,8 +160,7 @@ public interface InputType {      public static final int TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000050;      /** -     * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing -     * address. +     * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address.       */      public static final int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000060; @@ -150,14 +170,12 @@ public interface InputType {      public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000070;      /** -     * Variation of {@link #TYPE_CLASS_TEXT}: entering a search string -     * for a web search. +     * Variation of {@link #TYPE_CLASS_TEXT}: entering a simple text search (e.g. web search)       */ -    public static final int TYPE_TEXT_VARIATION_WEB_SEARCH = 0x00000080; +    public static final int TYPE_TEXT_VARIATION_SEARCH_STRING = 0x00000080;      /** -     * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of -     * a web form. +     * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form.       */      public static final int TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x00000090; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 346db49..95acf9d 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1074,7 +1074,9 @@ public abstract class Layout {          float h2 = getSecondaryHorizontal(point) - 0.5f;          int caps = TextKeyListener.getMetaState(editingBuffer, -                                                KeyEvent.META_SHIFT_ON); +                                                KeyEvent.META_SHIFT_ON) | +                   TextKeyListener.getMetaState(editingBuffer, +                                                TextKeyListener.META_SELECTING);          int fn = TextKeyListener.getMetaState(editingBuffer,                                                KeyEvent.META_ALT_ON);          int dist = 0; diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 48f65c6..feae6cf 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -146,26 +146,26 @@ public class DateUtils      // The following FORMAT_* symbols are used for specifying the format of      // dates and times in the formatDateRange method. -    public static final int FORMAT_SHOW_TIME      = 0x00001; -    public static final int FORMAT_SHOW_WEEKDAY   = 0x00002; -    public static final int FORMAT_SHOW_YEAR      = 0x00004; -    public static final int FORMAT_NO_YEAR        = 0x00008; -    public static final int FORMAT_SHOW_DATE      = 0x00010; -    public static final int FORMAT_NO_MONTH_DAY   = 0x00020; -    public static final int FORMAT_12HOUR         = 0x00040; -    public static final int FORMAT_24HOUR         = 0x00080; -    public static final int FORMAT_CAP_AMPM       = 0x00100; -    public static final int FORMAT_NO_NOON        = 0x00200; -    public static final int FORMAT_CAP_NOON       = 0x00400; -    public static final int FORMAT_NO_MIDNIGHT    = 0x00800; -    public static final int FORMAT_CAP_MIDNIGHT   = 0x01000; -    public static final int FORMAT_UTC            = 0x02000; -    public static final int FORMAT_ABBREV_TIME    = 0x04000; +    public static final int FORMAT_SHOW_TIME = 0x00001; +    public static final int FORMAT_SHOW_WEEKDAY = 0x00002; +    public static final int FORMAT_SHOW_YEAR = 0x00004; +    public static final int FORMAT_NO_YEAR = 0x00008; +    public static final int FORMAT_SHOW_DATE = 0x00010; +    public static final int FORMAT_NO_MONTH_DAY = 0x00020; +    public static final int FORMAT_12HOUR = 0x00040; +    public static final int FORMAT_24HOUR = 0x00080; +    public static final int FORMAT_CAP_AMPM = 0x00100; +    public static final int FORMAT_NO_NOON = 0x00200; +    public static final int FORMAT_CAP_NOON = 0x00400; +    public static final int FORMAT_NO_MIDNIGHT = 0x00800; +    public static final int FORMAT_CAP_MIDNIGHT = 0x01000; +    public static final int FORMAT_UTC = 0x02000; +    public static final int FORMAT_ABBREV_TIME = 0x04000;      public static final int FORMAT_ABBREV_WEEKDAY = 0x08000; -    public static final int FORMAT_ABBREV_MONTH   = 0x10000; -    public static final int FORMAT_NUMERIC_DATE   = 0x20000; -    public static final int FORMAT_ABBREV_ALL     = (FORMAT_ABBREV_TIME -            | FORMAT_ABBREV_WEEKDAY | FORMAT_ABBREV_MONTH); +    public static final int FORMAT_ABBREV_MONTH = 0x10000; +    public static final int FORMAT_NUMERIC_DATE = 0x20000; +    public static final int FORMAT_ABBREV_RELATIVE = 0x40000; +    public static final int FORMAT_ABBREV_ALL = 0x80000;      public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);      public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT); @@ -233,18 +233,20 @@ public class DateUtils      };      /** -     * Request the full spelled-out name. -     * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. -     * @more -     * <p>e.g. "Sunday" or "January" +     * Request the full spelled-out name. For use with the 'abbrev' parameter of +     * {@link #getDayOfWeekString} and {@link #getMonthString}. +     *  +     * @more <p> +     *       e.g. "Sunday" or "January"       */      public static final int LENGTH_LONG = 10;      /** -     * Request an abbreviated version of the name. -     * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. -     * @more -     * <p>e.g. "Sun" or "Jan" +     * Request an abbreviated version of the name. For use with the 'abbrev' +     * parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. +     *  +     * @more <p> +     *       e.g. "Sun" or "Jan"       */      public static final int LENGTH_MEDIUM = 20; @@ -364,53 +366,162 @@ public class DateUtils       *     0, MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, WEEK_IN_MILLIS       */      public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution) { -        Resources r = Resources.getSystem(); +        int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH; +        return getRelativeTimeSpanString(time, now, minResolution, flags); +    } +    /** +     * Returns a string describing 'time' as a time relative to 'now'. +     * <p> +     * Time spans in the past are formatted like "42 minutes ago". Time spans in +     * the future are formatted like "in 42 minutes". +     * <p> +     * Can use {@link #FORMAT_ABBREV_RELATIVE} flag to use abbreviated relative +     * times, like "42 mins ago". +     *  +     * @param time the time to describe, in milliseconds +     * @param now the current time in milliseconds +     * @param minResolution the minimum timespan to report. For example, a time +     *            3 seconds in the past will be reported as "0 minutes ago" if +     *            this is set to MINUTE_IN_MILLIS. Pass one of 0, +     *            MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, +     *            WEEK_IN_MILLIS +     * @param flags a bit mask of formatting options, such as +     *            {@link #FORMAT_NUMERIC_DATE} or +     *            {@link #FORMAT_ABBREV_RELATIVE} +     */ +    public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution, +            int flags) { +        Resources r = Resources.getSystem(); +        boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0; +                  boolean past = (now >= time);          long duration = Math.abs(now - time); -         +          int resId;          long count;          if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {              count = duration / SECOND_IN_MILLIS;              if (past) { -                resId = com.android.internal.R.plurals.num_seconds_ago; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_num_seconds_ago; +                } else { +                    resId = com.android.internal.R.plurals.num_seconds_ago; +                }              } else { -                resId = com.android.internal.R.plurals.in_num_seconds; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_in_num_seconds; +                } else { +                    resId = com.android.internal.R.plurals.in_num_seconds; +                }              }          } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {              count = duration / MINUTE_IN_MILLIS;              if (past) { -                resId = com.android.internal.R.plurals.num_minutes_ago; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_num_minutes_ago; +                } else { +                    resId = com.android.internal.R.plurals.num_minutes_ago; +                }              } else { -                resId = com.android.internal.R.plurals.in_num_minutes; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_in_num_minutes; +                } else { +                    resId = com.android.internal.R.plurals.in_num_minutes; +                }              }          } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {              count = duration / HOUR_IN_MILLIS;              if (past) { -                resId = com.android.internal.R.plurals.num_hours_ago; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_num_hours_ago; +                } else { +                    resId = com.android.internal.R.plurals.num_hours_ago; +                }              } else { -                resId = com.android.internal.R.plurals.in_num_hours; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_in_num_hours; +                } else { +                    resId = com.android.internal.R.plurals.in_num_hours; +                }              }          } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {              count = duration / DAY_IN_MILLIS;              if (past) { -                resId = com.android.internal.R.plurals.num_days_ago; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_num_days_ago; +                } else { +                    resId = com.android.internal.R.plurals.num_days_ago; +                }              } else { -                resId = com.android.internal.R.plurals.in_num_days; +                if (abbrevRelative) { +                    resId = com.android.internal.R.plurals.abbrev_in_num_days; +                } else { +                    resId = com.android.internal.R.plurals.in_num_days; +                }              }          } else { -            // Longer than a week ago, so just show the date. -            int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH; -                          // We know that we won't be showing the time, so it is safe to pass              // in a null context.              return formatDateRange(null, time, time, flags);          } -         +          String format = r.getQuantityString(resId, (int) count);          return String.format(format, count);      } +     +    /** +     * Return string describing the elapsed time since startTime formatted like +     * "[relative time/date], [time]". +     * <p> +     * Example output strings for the US date format. +     * <ul> +     * <li>3 mins ago, 10:15 AM</li> +     * <li>yesterday, 12:20 PM</li> +     * <li>Dec 12, 4:12 AM</li> +     * <li>11/14/2007, 8:20 AM</li> +     * </ul> +     *  +     * @param time some time in the past. +     * @param minResolution the minimum elapsed time (in milliseconds) to report +     *            when showing relative times. For example, a time 3 seconds in +     *            the past will be reported as "0 minutes ago" if this is set to +     *            {@link #MINUTE_IN_MILLIS}. +     * @param transitionResolution the elapsed time (in milliseconds) at which +     *            to stop reporting relative measurements. Elapsed times greater +     *            than this resolution will default to normal date formatting. +     *            For example, will transition from "6 days ago" to "Dec 12" +     *            when using {@link #WEEK_IN_MILLIS}. +     */ +    public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution, +            long transitionResolution, int flags) { +        Resources r = Resources.getSystem(); + +        long now = System.currentTimeMillis(); +        long duration = Math.abs(now - time); + +        // getRelativeTimeSpanString() doesn't correctly format relative dates +        // above a week or exact dates below a day, so clamp +        // transitionResolution as needed. +        if (transitionResolution > WEEK_IN_MILLIS) { +            transitionResolution = WEEK_IN_MILLIS; +        } else if (transitionResolution < DAY_IN_MILLIS) { +            transitionResolution = DAY_IN_MILLIS; +        } + +        CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME); +         +        String result; +        if (duration < transitionResolution) { +            CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags); +            result = r.getString(com.android.internal.R.string.relative_time, relativeClause, timeClause); +        } else { +            CharSequence dateClause = getRelativeTimeSpanString(c, time, false); +            result = r.getString(com.android.internal.R.string.date_time, dateClause, timeClause); +        } + +        return result; +    }      /**       * Returns a string describing a day relative to the current day. For example if the day is @@ -1005,8 +1116,8 @@ public class DateUtils          boolean showYear = (flags & FORMAT_SHOW_YEAR) != 0;          boolean noYear = (flags & FORMAT_NO_YEAR) != 0;          boolean useUTC = (flags & FORMAT_UTC) != 0; -        boolean abbrevWeekDay = (flags & FORMAT_ABBREV_WEEKDAY) != 0; -        boolean abbrevMonth = (flags & FORMAT_ABBREV_MONTH) != 0; +        boolean abbrevWeekDay = (flags & (FORMAT_ABBREV_WEEKDAY | FORMAT_ABBREV_ALL)) != 0; +        boolean abbrevMonth = (flags & (FORMAT_ABBREV_MONTH | FORMAT_ABBREV_ALL)) != 0;          boolean noMonthDay = (flags & FORMAT_NO_MONTH_DAY) != 0;          boolean numericDate = (flags & FORMAT_NUMERIC_DATE) != 0; @@ -1087,7 +1198,7 @@ public class DateUtils                  startTimeFormat = HOUR_MINUTE_24;                  endTimeFormat = HOUR_MINUTE_24;              } else { -                boolean abbrevTime = (flags & FORMAT_ABBREV_TIME) != 0; +                boolean abbrevTime = (flags & (FORMAT_ABBREV_TIME | FORMAT_ABBREV_ALL)) != 0;                  boolean capAMPM = (flags & FORMAT_CAP_AMPM) != 0;                  boolean noNoon = (flags & FORMAT_NO_NOON) != 0;                  boolean capNoon = (flags & FORMAT_CAP_NOON) != 0; @@ -1419,7 +1530,6 @@ public class DateUtils          long now = System.currentTimeMillis();          long span = now - millis; -        Resources res = c.getResources();          if (sNowTime == null) {              sNowTime = new Time();              sThenTime = new Time(); @@ -1449,6 +1559,7 @@ public class DateUtils              prepositionId = R.string.preposition_for_date;          }          if (withPreposition) { +            Resources res = c.getResources();              result = res.getString(prepositionId, result);          }          return result; diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 652413e..7457439 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -31,8 +31,10 @@ ArrowKeyMovementMethod  implements MovementMethod  {      private boolean up(TextView widget, Spannable buffer) { -        boolean cap = MetaKeyKeyListener.getMetaState(buffer, -                        KeyEvent.META_SHIFT_ON) == 1; +        boolean cap = (MetaKeyKeyListener.getMetaState(buffer, +                        KeyEvent.META_SHIFT_ON) == 1) || +                      (MetaKeyKeyListener.getMetaState(buffer, +                        MetaKeyKeyListener.META_SELECTING) != 0);          boolean alt = MetaKeyKeyListener.getMetaState(buffer,                          KeyEvent.META_ALT_ON) == 1;          Layout layout = widget.getLayout(); @@ -55,8 +57,10 @@ implements MovementMethod      }      private boolean down(TextView widget, Spannable buffer) { -        boolean cap = MetaKeyKeyListener.getMetaState(buffer, -                        KeyEvent.META_SHIFT_ON) == 1; +        boolean cap = (MetaKeyKeyListener.getMetaState(buffer, +                        KeyEvent.META_SHIFT_ON) == 1) || +                      (MetaKeyKeyListener.getMetaState(buffer, +                        MetaKeyKeyListener.META_SELECTING) != 0);          boolean alt = MetaKeyKeyListener.getMetaState(buffer,                          KeyEvent.META_ALT_ON) == 1;          Layout layout = widget.getLayout(); @@ -79,8 +83,10 @@ implements MovementMethod      }      private boolean left(TextView widget, Spannable buffer) { -        boolean cap = MetaKeyKeyListener.getMetaState(buffer, -                        KeyEvent.META_SHIFT_ON) == 1; +        boolean cap = (MetaKeyKeyListener.getMetaState(buffer, +                        KeyEvent.META_SHIFT_ON) == 1) || +                      (MetaKeyKeyListener.getMetaState(buffer, +                        MetaKeyKeyListener.META_SELECTING) != 0);          boolean alt = MetaKeyKeyListener.getMetaState(buffer,                          KeyEvent.META_ALT_ON) == 1;          Layout layout = widget.getLayout(); @@ -101,8 +107,10 @@ implements MovementMethod      }      private boolean right(TextView widget, Spannable buffer) { -        boolean cap = MetaKeyKeyListener.getMetaState(buffer, -                        KeyEvent.META_SHIFT_ON) == 1; +        boolean cap = (MetaKeyKeyListener.getMetaState(buffer, +                        KeyEvent.META_SHIFT_ON) == 1) || +                      (MetaKeyKeyListener.getMetaState(buffer, +                        MetaKeyKeyListener.META_SELECTING) != 0);          boolean alt = MetaKeyKeyListener.getMetaState(buffer,                          KeyEvent.META_ALT_ON) == 1;          Layout layout = widget.getLayout(); @@ -141,6 +149,13 @@ implements MovementMethod          case KeyEvent.KEYCODE_DPAD_RIGHT:              handled |= right(widget, buffer);              break; + +        case KeyEvent.KEYCODE_DPAD_CENTER: +            if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { +                if (widget.showContextMenu()) { +                    handled = true; +                } +            }          }          if (handled) { @@ -179,7 +194,10 @@ implements MovementMethod                  int line = layout.getLineForVertical(y);                  int off = layout.getOffsetForHorizontal(line, x); -                boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0; +                boolean cap = (MetaKeyKeyListener.getMetaState(buffer, +                                KeyEvent.META_SHIFT_ON) == 1) || +                              (MetaKeyKeyListener.getMetaState(buffer, +                                MetaKeyKeyListener.META_SELECTING) != 0);                  if (cap) {                      Selection.extendSelection(buffer, off); diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index f0305d9..d5a473b 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -22,7 +22,8 @@ import android.text.*;  /**   * This base class encapsulates the behavior for handling the meta keys - * (caps, fn, sym).  Key listener that care about meta state should + * (shift and alt) and the pseudo-meta state of selecting text. + * Key listeners that care about meta state should   * inherit from it; you should not instantiate this class directly in a client.   */ @@ -31,13 +32,49 @@ public abstract class MetaKeyKeyListener {      public static final int META_ALT_ON = KeyEvent.META_ALT_ON;      public static final int META_SYM_ON = KeyEvent.META_SYM_ON; -    public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << 8; -    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << 8; -    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << 8; +    private static final int LOCKED_SHIFT = 8; +     +    public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << LOCKED_SHIFT; +    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << LOCKED_SHIFT; +    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << LOCKED_SHIFT; + +    /** +     * @hide pending API review +     */ +    public static final int META_SELECTING = 1 << 16; + +    private static final int USED_SHIFT = 24; +     +    private static final long META_CAP_USED = ((long)KeyEvent.META_SHIFT_ON) << USED_SHIFT; +    private static final long META_ALT_USED = ((long)KeyEvent.META_ALT_ON) << USED_SHIFT; +    private static final long META_SYM_USED = ((long)KeyEvent.META_SYM_ON) << USED_SHIFT; + +    private static final int PRESSED_SHIFT = 32; +     +    private static final long META_CAP_PRESSED = ((long)KeyEvent.META_SHIFT_ON) << PRESSED_SHIFT; +    private static final long META_ALT_PRESSED = ((long)KeyEvent.META_ALT_ON) << PRESSED_SHIFT; +    private static final long META_SYM_PRESSED = ((long)KeyEvent.META_SYM_ON) << PRESSED_SHIFT; +    private static final int RELEASED_SHIFT = 40; +     +    private static final long META_CAP_RELEASED = ((long)KeyEvent.META_SHIFT_ON) << RELEASED_SHIFT; +    private static final long META_ALT_RELEASED = ((long)KeyEvent.META_ALT_ON) << RELEASED_SHIFT; +    private static final long META_SYM_RELEASED = ((long)KeyEvent.META_SYM_ON) << RELEASED_SHIFT; + +    private static final long META_SHIFT_MASK = META_SHIFT_ON +            | META_CAP_LOCKED | META_CAP_USED +            | META_CAP_PRESSED | META_CAP_RELEASED; +    private static final long META_ALT_MASK = META_ALT_ON +            | META_ALT_LOCKED | META_ALT_USED +            | META_ALT_PRESSED | META_ALT_RELEASED; +    private static final long META_SYM_MASK = META_SYM_ON +            | META_SYM_LOCKED | META_SYM_USED +            | META_SYM_PRESSED | META_SYM_RELEASED; +          private static final Object CAP = new Object();      private static final Object ALT = new Object();      private static final Object SYM = new Object(); +    private static final Object SELECTING = new Object();      /**       * Resets all meta state to inactive. @@ -46,6 +83,7 @@ public abstract class MetaKeyKeyListener {          text.removeSpan(CAP);          text.removeSpan(ALT);          text.removeSpan(SYM); +        text.removeSpan(SELECTING);      }      /** @@ -59,13 +97,14 @@ public abstract class MetaKeyKeyListener {      public static final int getMetaState(CharSequence text) {          return getActive(text, CAP, META_SHIFT_ON, META_CAP_LOCKED) |                 getActive(text, ALT, META_ALT_ON, META_ALT_LOCKED) | -               getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED); +               getActive(text, SYM, META_SYM_ON, META_SYM_LOCKED) | +               getActive(text, SELECTING, META_SELECTING, META_SELECTING);      }      /**       * Gets the state of a particular meta key.       * -     * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON +     * @param meta META_SHIFT_ON, META_ALT_ON, META_SYM_ON, or META_SELECTING       * @param text the buffer in which the meta key would have been pressed.       *       * @return 0 if inactive, 1 if active, 2 if locked. @@ -81,6 +120,9 @@ public abstract class MetaKeyKeyListener {              case META_SYM_ON:                  return getActive(text, SYM, 1, 2); +            case META_SELECTING: +                return getActive(text, SELECTING, 1, 2); +              default:                  return 0;          } @@ -120,7 +162,8 @@ public abstract class MetaKeyKeyListener {       * keep track of meta state in the specified text.       */      public static boolean isMetaTracker(CharSequence text, Object what) { -        return what == CAP || what == ALT || what == SYM; +        return what == CAP || what == ALT || what == SYM || +               what == SELECTING;      }      private static void adjust(Spannable content, Object what) { @@ -140,6 +183,7 @@ public abstract class MetaKeyKeyListener {          resetLock(content, CAP);          resetLock(content, ALT);          resetLock(content, SYM); +        resetLock(content, SELECTING);      }      private static void resetLock(Spannable content, Object what) { @@ -189,6 +233,23 @@ public abstract class MetaKeyKeyListener {      }      /** +     * Start selecting text. +     * @hide pending API review +     */ +    public static void startSelecting(View view, Spannable content) { +        content.setSpan(SELECTING, 0, 0, PRESSED); +    } + +    /** +     * Stop selecting text.  This does not actually collapse the selection; +     * call {@link android.text.Selection#setSelection} too. +     * @hide pending API review +     */ +    public static void stopSelecting(View view, Spannable content) { +        content.removeSpan(SELECTING); +    } + +    /**       * Handles release of the meta keys.       */      public boolean onKeyUp(View view, Editable content, int keyCode, @@ -225,6 +286,170 @@ public abstract class MetaKeyKeyListener {          if ((states&META_SHIFT_ON) != 0) resetLock(content, CAP);          if ((states&META_ALT_ON) != 0) resetLock(content, ALT);          if ((states&META_SYM_ON) != 0) resetLock(content, SYM); +        if ((states&META_SELECTING) != 0) resetLock(content, SELECTING); +    } + +    /** +     * Call this if you are a method that ignores the locked meta state +     * (arrow keys, for example) and you handle a key. +     */ +    public static long resetLockedMeta(long state) { +        state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK); +        state = resetLock(state, META_ALT_ON, META_ALT_MASK); +        state = resetLock(state, META_SYM_ON, META_SYM_MASK); +        return state; +    } + +    private static long resetLock(long state, int what, long mask) { +        if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) { +            state &= ~mask; +        } +        return state; +    } + +    // --------------------------------------------------------------------- +    // Version of API that operates on a state bit mask +    // --------------------------------------------------------------------- + +    /** +     * Gets the state of the meta keys. +     *  +     * @param state the current meta state bits. +     * +     * @return an integer in which each bit set to one represents a pressed +     *         or locked meta key. +     */ +    public static final int getMetaState(long state) { +        return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) | +               getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) | +               getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED); +    } + +    /** +     * Gets the state of a particular meta key. +     * +     * @param state the current state bits. +     * @param meta META_SHIFT_ON, META_ALT_ON, or META_SYM_ON +     * +     * @return 0 if inactive, 1 if active, 2 if locked. +     */ +    public static final int getMetaState(long state, int meta) { +        switch (meta) { +            case META_SHIFT_ON: +                return getActive(state, meta, 1, 2); + +            case META_ALT_ON: +                return getActive(state, meta, 1, 2); + +            case META_SYM_ON: +                return getActive(state, meta, 1, 2); + +            default: +                return 0; +        } +    } + +    private static int getActive(long state, int meta, int on, int lock) { +        if ((state&(meta<<LOCKED_SHIFT)) != 0) { +            return lock; +        } else if ((state&meta) != 0) { +            return on; +        } else { +            return 0; +        } +    } + +    /** +     * Call this method after you handle a keypress so that the meta +     * state will be reset to unshifted (if it is not still down) +     * or primed to be reset to unshifted (once it is released).  Takes +     * the current state, returns the new state. +     */ +    public static long adjustMetaAfterKeypress(long state) { +        state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK); +        state = adjust(state, META_ALT_ON, META_ALT_MASK); +        state = adjust(state, META_SYM_ON, META_SYM_MASK); +        return state; +    } + +    private static long adjust(long state, int what, long mask) { +        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) +            return (state&~mask) | what | ((long)what)<<USED_SHIFT; +        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0) +            return state & ~mask; +        return state; +    } + +    /** +     * Handles presses of the meta keys. +     */ +    public static long handleKeyDown(long state, int keyCode, KeyEvent event) { +        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { +            return press(state, META_SHIFT_ON, META_SHIFT_MASK); +        } + +        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT +                || keyCode == KeyEvent.KEYCODE_NUM) { +            return press(state, META_ALT_ON, META_ALT_MASK); +        } + +        if (keyCode == KeyEvent.KEYCODE_SYM) { +            return press(state, META_SYM_ON, META_SYM_MASK); +        } + +        return state; +    } + +    private static long press(long state, int what, long mask) { +        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) +            ; // repeat before use +        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0) +            state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT); +        else if ((state&(((long)what)<<USED_SHIFT)) != 0) +            ; // repeat after use +        else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) +            state = state&~mask; +        else +            state = state | what | (((long)what)<<PRESSED_SHIFT); +        return state; +    } + +    /** +     * Handles release of the meta keys. +     */ +    public static long handleKeyUp(long state, int keyCode, KeyEvent event) { +        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { +            return release(state, META_SHIFT_ON, META_SHIFT_MASK); +        } + +        if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT +                || keyCode == KeyEvent.KEYCODE_NUM) { +            return release(state, META_ALT_ON, META_ALT_MASK); +        } + +        if (keyCode == KeyEvent.KEYCODE_SYM) { +            return release(state, META_SYM_ON, META_SYM_MASK); +        } + +        return state; +    } + +    private static long release(long state, int what, long mask) { +        if ((state&(((long)what)<<USED_SHIFT)) != 0) +            state = state&~mask; +        else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0) +            state = state | what | (((long)what)<<RELEASED_SHIFT); +        return state; +    } + +    public long clearMetaKeyState(long state, int which) { +        if ((which&META_SHIFT_ON) != 0) +            state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK); +        if ((which&META_ALT_ON) != 0) +            state = resetLock(state, META_ALT_ON, META_ALT_MASK); +        if ((which&META_SYM_ON) != 0) +            state = resetLock(state, META_SYM_ON, META_SYM_MASK); +        return state;      }      /** diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index be6ef77..27eda69 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -18,7 +18,7 @@ package android.text.style;  import android.text.TextPaint; -public class BackgroundColorSpan extends CharacterStyle { +public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance {      private int mColor; diff --git a/core/java/android/text/style/CharacterStyle.java b/core/java/android/text/style/CharacterStyle.java index 7620d29..14dfddd 100644 --- a/core/java/android/text/style/CharacterStyle.java +++ b/core/java/android/text/style/CharacterStyle.java @@ -16,12 +16,12 @@  package android.text.style; -import android.graphics.Paint;  import android.text.TextPaint;  /**   * The classes that affect character-level text formatting extend this - * class.  Most also extend {@link MetricAffectingSpan}. + * class.  Most extend its subclass {@link MetricAffectingSpan}, but simple + * ones may just implement {@link UpdateAppearance}.   */  public abstract class CharacterStyle {  	public abstract void updateDrawState(TextPaint tp); diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java index a232ed7..989ef54 100644 --- a/core/java/android/text/style/ClickableSpan.java +++ b/core/java/android/text/style/ClickableSpan.java @@ -25,7 +25,7 @@ import android.view.View;   * text can be selected.  If clicked, the {@link #onClick} method will   * be called.   */ -public abstract class ClickableSpan extends CharacterStyle { +public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {      /**       * Performs the click action associated with this span. diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index dd89b68..89dc45b 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -17,18 +17,55 @@  package android.text.style;  import android.graphics.Canvas; +import android.graphics.Color;  import android.graphics.Paint;  import android.graphics.Rect; +import android.graphics.Paint.Style;  import android.graphics.drawable.Drawable; +import android.util.Log;  import java.lang.ref.WeakReference;  /**   *   */ -public abstract class DynamicDrawableSpan -extends ReplacementSpan -{ +public abstract class DynamicDrawableSpan extends ReplacementSpan { +    private static final String TAG = "DynamicDrawableSpan"; +     +    /** +     * A constant indicating that the bottom of this span should be aligned +     * with the bottom of the surrounding text, i.e., at the same level as the +     * lowest descender in the text. +     */ +    public static final int ALIGN_BOTTOM = 0; +     +    /** +     * A constant indicating that the bottom of this span should be aligned +     * with the baseline of the surrounding text. +     */ +    public static final int ALIGN_BASELINE = 1; +     +    protected final int mVerticalAlignment; +     +    public DynamicDrawableSpan() { +        mVerticalAlignment = ALIGN_BOTTOM; +    } + +    /** +     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}. +     */ +    protected DynamicDrawableSpan(int verticalAlignment) { +        mVerticalAlignment = verticalAlignment; +    } + +    /** +     * Returns the vertical alignment of this span, one of {@link #ALIGN_BOTTOM} or +     * {@link #ALIGN_BASELINE}. +     */ +    public int getVerticalAlignment() { +        return mVerticalAlignment; +    } +      /**       * Your subclass must implement this method to provide the bitmap          * to be drawn.  The dimensions of the bitmap must be the same @@ -61,7 +98,12 @@ extends ReplacementSpan          Drawable b = getCachedDrawable();          canvas.save(); -        canvas.translate(x, bottom - b.getBounds().bottom); +        int transY = bottom - b.getBounds().bottom; +        if (mVerticalAlignment == ALIGN_BASELINE) { +            transY -= paint.getFontMetricsInt().descent; +        } + +        canvas.translate(x, transY);          b.draw(canvas);          canvas.restore();      } diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index 5cccd9c..99b3381 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -16,10 +16,9 @@  package android.text.style; -import android.graphics.Paint;  import android.text.TextPaint; -public class ForegroundColorSpan extends CharacterStyle { +public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance {      private int mColor; diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java index de067b1..2eebc0d 100644 --- a/core/java/android/text/style/ImageSpan.java +++ b/core/java/android/text/style/ImageSpan.java @@ -32,29 +32,73 @@ public class ImageSpan extends DynamicDrawableSpan {      private int mResourceId;      private Context mContext;      private String mSource; -          public ImageSpan(Bitmap b) { +        this(b, ALIGN_BOTTOM); +    } + +    /** +     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or +     * {@link DynamicDrawableSpan#ALIGN_BASELINE}. +     */ +    public ImageSpan(Bitmap b, int verticalAlignment) { +        super(verticalAlignment);          mDrawable = new BitmapDrawable(b);          mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(),                  mDrawable.getIntrinsicHeight());      }      public ImageSpan(Drawable d) { +        this(d, ALIGN_BOTTOM); +    } + +    /** +     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or +     * {@link DynamicDrawableSpan#ALIGN_BASELINE}. +     */ +    public ImageSpan(Drawable d, int verticalAlignment) { +        super(verticalAlignment);          mDrawable = d;      }      public ImageSpan(Drawable d, String source) { +        this(d, source, ALIGN_BOTTOM); +    } + +    /** +     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or +     * {@link DynamicDrawableSpan#ALIGN_BASELINE}. +     */ +    public ImageSpan(Drawable d, String source, int verticalAlignment) { +        super(verticalAlignment);          mDrawable = d;          mSource = source;      }      public ImageSpan(Context context, Uri uri) { +        this(context, uri, ALIGN_BOTTOM); +    } + +    /** +     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or +     * {@link DynamicDrawableSpan#ALIGN_BASELINE}. +     */ +    public ImageSpan(Context context, Uri uri, int verticalAlignment) { +        super(verticalAlignment);          mContext = context;          mContentUri = uri;      }      public ImageSpan(Context context, int resourceId) { +        this(context, resourceId, ALIGN_BOTTOM); +    } + +    /** +     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or +     * {@link DynamicDrawableSpan#ALIGN_BASELINE}. +     */ +    public ImageSpan(Context context, int resourceId, int verticalAlignment) { +        super(verticalAlignment);          mContext = context;          mResourceId = resourceId;      } diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java index 781bcec..64ab0d8 100644 --- a/core/java/android/text/style/MaskFilterSpan.java +++ b/core/java/android/text/style/MaskFilterSpan.java @@ -16,11 +16,10 @@  package android.text.style; -import android.graphics.Paint;  import android.graphics.MaskFilter;  import android.text.TextPaint; -public class MaskFilterSpan extends CharacterStyle { +public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {  	private MaskFilter mFilter; diff --git a/core/java/android/text/style/RasterizerSpan.java b/core/java/android/text/style/RasterizerSpan.java index 193c700..75b5bcc 100644 --- a/core/java/android/text/style/RasterizerSpan.java +++ b/core/java/android/text/style/RasterizerSpan.java @@ -16,11 +16,10 @@  package android.text.style; -import android.graphics.Paint;  import android.graphics.Rasterizer;  import android.text.TextPaint; -public class RasterizerSpan extends CharacterStyle { +public class RasterizerSpan extends CharacterStyle implements UpdateAppearance {  	private Rasterizer mRasterizer; diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java index 01ae38c..dd430e5 100644 --- a/core/java/android/text/style/StrikethroughSpan.java +++ b/core/java/android/text/style/StrikethroughSpan.java @@ -18,7 +18,7 @@ package android.text.style;  import android.text.TextPaint; -public class StrikethroughSpan extends CharacterStyle { +public class StrikethroughSpan extends CharacterStyle implements UpdateAppearance {  	@Override  	public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java index 5dce0f2..ca6f10c 100644 --- a/core/java/android/text/style/UnderlineSpan.java +++ b/core/java/android/text/style/UnderlineSpan.java @@ -18,7 +18,7 @@ package android.text.style;  import android.text.TextPaint; -public class UnderlineSpan extends CharacterStyle { +public class UnderlineSpan extends CharacterStyle implements UpdateAppearance {  	@Override  	public void updateDrawState(TextPaint ds) { diff --git a/core/java/android/text/style/UpdateAppearance.java b/core/java/android/text/style/UpdateAppearance.java new file mode 100644 index 0000000..198e4fa --- /dev/null +++ b/core/java/android/text/style/UpdateAppearance.java @@ -0,0 +1,10 @@ +package android.text.style; + +/** + * The classes that affect character-level text in a way that modifies their + * appearance when one is added or removed must implement this interface.  Note + * that if the class also impacts size or other metrics, it should instead + * implement {@link UpdateLayout}. + */ +public interface UpdateAppearance { +} diff --git a/core/java/android/text/style/UpdateLayout.java b/core/java/android/text/style/UpdateLayout.java index 211685a..591075e 100644 --- a/core/java/android/text/style/UpdateLayout.java +++ b/core/java/android/text/style/UpdateLayout.java @@ -18,7 +18,8 @@ package android.text.style;  /**   * The classes that affect character-level text formatting in a way that - * triggers a text layout update when one is added or remove must implement - * this interface. + * triggers a text layout update when one is added or removed must implement + * this interface.  This interface also includes {@link UpdateAppearance} + * since such a change implicitly also impacts the appearance.   */ -public interface UpdateLayout { } +public interface UpdateLayout extends UpdateAppearance { } | 
