summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Debunne <debunne@google.com>2011-08-04 21:22:30 -0700
committerGilles Debunne <debunne@google.com>2011-08-23 13:13:54 -0700
commit6435a56a8c02de98befcc8cd743b2b638cffb327 (patch)
tree62a5678b5531f53c7b25e920f8f9c6b65f8b510c
parentdefa12e95b8d25db5f3e9a044e83d6fe680b67a3 (diff)
downloadframeworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.zip
frameworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.tar.gz
frameworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.tar.bz2
Spell checking in TextViews
New UX interactions (the Paste action is no longer displayed after a delay) suggestionEnabled flag replaced by existing input type flag. removeSpans fixed in SpannableStringBuilder to always send notifications SuggestionSpan handled by TextView instead of SpannableStringBuilder New span update algorithm to correctly handle edition around word boundaries. Change-Id: I52c01172f19e595fa512e285a565a3fd97c3c50e
-rw-r--r--api/current.txt117
-rw-r--r--core/java/android/text/SpannableStringBuilder.java125
-rw-r--r--core/java/android/text/method/WordIterator.java4
-rw-r--r--core/java/android/text/style/SpellCheckSpan.java41
-rw-r--r--core/java/android/text/style/SuggestionSpan.java17
-rw-r--r--core/java/android/view/textservice/SuggestionsInfo.java4
-rw-r--r--core/java/android/widget/SpellChecker.java226
-rw-r--r--core/java/android/widget/TextView.java376
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.pngbin709 -> 626 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.pngbin945 -> 435 bytes
-rw-r--r--core/res/res/layout/keyguard_screen_password_landscape.xml1
-rw-r--r--core/res/res/layout/keyguard_screen_password_portrait.xml2
-rwxr-xr-xcore/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/public.xml2
-rwxr-xr-xcore/res/res/values/strings.xml2
15 files changed, 657 insertions, 266 deletions
diff --git a/api/current.txt b/api/current.txt
index fb16830..cb49ce2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -184,21 +184,21 @@ package android {
public static final class R.attr {
ctor public R.attr();
field public static final int absListViewStyle = 16842858; // 0x101006a
- field public static final int accessibilityEventTypes = 16843644; // 0x101037c
- field public static final int accessibilityFeedbackType = 16843646; // 0x101037e
- field public static final int accessibilityFlags = 16843648; // 0x1010380
+ field public static final int accessibilityEventTypes = 16843643; // 0x101037b
+ field public static final int accessibilityFeedbackType = 16843645; // 0x101037d
+ field public static final int accessibilityFlags = 16843647; // 0x101037f
field public static final int accountPreferences = 16843423; // 0x101029f
field public static final int accountType = 16843407; // 0x101028f
field public static final int action = 16842797; // 0x101002d
- field public static final int actionBarDivider = 16843685; // 0x10103a5
- field public static final int actionBarItemBackground = 16843686; // 0x10103a6
+ field public static final int actionBarDivider = 16843684; // 0x10103a4
+ field public static final int actionBarItemBackground = 16843685; // 0x10103a5
field public static final int actionBarSize = 16843499; // 0x10102eb
- field public static final int actionBarSplitStyle = 16843666; // 0x1010392
+ field public static final int actionBarSplitStyle = 16843665; // 0x1010391
field public static final int actionBarStyle = 16843470; // 0x10102ce
field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4
field public static final int actionBarTabStyle = 16843507; // 0x10102f3
field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5
- field public static final int actionBarWidgetTheme = 16843681; // 0x10103a1
+ field public static final int actionBarWidgetTheme = 16843680; // 0x10103a0
field public static final int actionButtonStyle = 16843480; // 0x10102d8
field public static final int actionDropDownStyle = 16843479; // 0x10102d7
field public static final int actionLayout = 16843515; // 0x10102fb
@@ -210,11 +210,11 @@ package android {
field public static final int actionModeCopyDrawable = 16843538; // 0x1010312
field public static final int actionModeCutDrawable = 16843537; // 0x1010311
field public static final int actionModePasteDrawable = 16843539; // 0x1010313
- field public static final int actionModeSelectAllDrawable = 16843642; // 0x101037a
- field public static final int actionModeSplitBackground = 16843687; // 0x10103a7
- field public static final int actionModeStyle = 16843678; // 0x101039e
+ field public static final int actionModeSelectAllDrawable = 16843641; // 0x1010379
+ field public static final int actionModeSplitBackground = 16843686; // 0x10103a6
+ field public static final int actionModeStyle = 16843677; // 0x101039d
field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
- field public static final int actionProviderClass = 16843667; // 0x1010393
+ field public static final int actionProviderClass = 16843666; // 0x1010392
field public static final int actionViewClass = 16843516; // 0x10102fc
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
@@ -226,7 +226,7 @@ package android {
field public static final int alertDialogIcon = 16843605; // 0x1010355
field public static final int alertDialogStyle = 16842845; // 0x101005d
field public static final int alertDialogTheme = 16843529; // 0x1010309
- field public static final int alignmentMode = 16843638; // 0x1010376
+ field public static final int alignmentMode = 16843637; // 0x1010375
field public static final int allContactsName = 16843468; // 0x10102cc
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
@@ -260,8 +260,8 @@ package android {
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
- field public static final int backgroundSplit = 16843669; // 0x1010395
- field public static final int backgroundStacked = 16843668; // 0x1010394
+ field public static final int backgroundSplit = 16843668; // 0x1010394
+ field public static final int backgroundStacked = 16843667; // 0x1010393
field public static final int backupAgent = 16843391; // 0x101027f
field public static final int baseline = 16843548; // 0x101031c
field public static final int baselineAlignBottom = 16843042; // 0x1010122
@@ -270,7 +270,7 @@ package android {
field public static final int borderlessButtonStyle = 16843563; // 0x101032b
field public static final int bottom = 16843184; // 0x10101b0
field public static final int bottomBright = 16842957; // 0x10100cd
- field public static final int bottomChevronDrawable = 16843655; // 0x1010387
+ field public static final int bottomChevronDrawable = 16843654; // 0x1010386
field public static final int bottomDark = 16842953; // 0x10100c9
field public static final int bottomLeftRadius = 16843179; // 0x10101ab
field public static final int bottomMedium = 16842958; // 0x10100ce
@@ -289,7 +289,7 @@ package android {
field public static final int cacheColorHint = 16843009; // 0x1010101
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
- field public static final int canRetrieveWindowContent = 16843649; // 0x1010381
+ field public static final int canRetrieveWindowContent = 16843648; // 0x1010380
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
field public static final deprecated int capitalize = 16843113; // 0x1010169
field public static final int centerBright = 16842956; // 0x10100cc
@@ -318,18 +318,18 @@ package android {
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int color = 16843173; // 0x10101a5
- field public static final int colorActivatedHighlight = 16843674; // 0x101039a
+ field public static final int colorActivatedHighlight = 16843673; // 0x1010399
field public static final int colorBackground = 16842801; // 0x1010031
field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab
- field public static final int colorFocusedHighlight = 16843673; // 0x1010399
+ field public static final int colorFocusedHighlight = 16843672; // 0x1010398
field public static final int colorForeground = 16842800; // 0x1010030
field public static final int colorForegroundInverse = 16843270; // 0x1010206
- field public static final int colorLongPressedHighlight = 16843672; // 0x1010398
- field public static final int colorMultiSelectHighlight = 16843675; // 0x101039b
- field public static final int colorPressedHighlight = 16843671; // 0x1010397
- field public static final int columnCount = 16843635; // 0x1010373
+ field public static final int colorLongPressedHighlight = 16843671; // 0x1010397
+ field public static final int colorMultiSelectHighlight = 16843674; // 0x101039a
+ field public static final int colorPressedHighlight = 16843670; // 0x1010396
+ field public static final int columnCount = 16843634; // 0x1010372
field public static final int columnDelay = 16843215; // 0x10101cf
- field public static final int columnOrderPreserved = 16843636; // 0x1010374
+ field public static final int columnOrderPreserved = 16843635; // 0x1010373
field public static final int columnWidth = 16843031; // 0x1010117
field public static final int compatibleWidthLimitDp = 16843621; // 0x1010365
field public static final int completionHint = 16843122; // 0x1010172
@@ -383,11 +383,11 @@ package android {
field public static final int drawSelectorOnTop = 16843004; // 0x10100fc
field public static final int drawable = 16843161; // 0x1010199
field public static final int drawableBottom = 16843118; // 0x101016e
- field public static final int drawableEnd = 16843677; // 0x101039d
+ field public static final int drawableEnd = 16843676; // 0x101039c
field public static final int drawableLeft = 16843119; // 0x101016f
field public static final int drawablePadding = 16843121; // 0x1010171
field public static final int drawableRight = 16843120; // 0x1010170
- field public static final int drawableStart = 16843676; // 0x101039c
+ field public static final int drawableStart = 16843675; // 0x101039b
field public static final int drawableTop = 16843117; // 0x101016d
field public static final int drawingCacheQuality = 16842984; // 0x10100e8
field public static final int dropDownAnchor = 16843363; // 0x1010263
@@ -444,7 +444,7 @@ package android {
field public static final int fastScrollTextColor = 16843609; // 0x1010359
field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
- field public static final int feedbackCount = 16843661; // 0x101038d
+ field public static final int feedbackCount = 16843660; // 0x101038c
field public static final int fillAfter = 16843197; // 0x10101bd
field public static final int fillBefore = 16843196; // 0x10101bc
field public static final int fillEnabled = 16843343; // 0x101024f
@@ -497,7 +497,7 @@ package android {
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
field public static final int handle = 16843354; // 0x101025a
- field public static final int handleDrawable = 16843651; // 0x1010383
+ field public static final int handleDrawable = 16843650; // 0x1010382
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
@@ -506,12 +506,12 @@ package android {
field public static final int headerDividersEnabled = 16843310; // 0x101022e
field public static final int height = 16843093; // 0x1010155
field public static final int hint = 16843088; // 0x1010150
- field public static final int hitRadius = 16843658; // 0x101038a
+ field public static final int hitRadius = 16843657; // 0x1010389
field public static final int homeAsUpIndicator = 16843531; // 0x101030b
field public static final int homeLayout = 16843549; // 0x101031d
field public static final int horizontalDivider = 16843053; // 0x101012d
field public static final int horizontalGap = 16843327; // 0x101023f
- field public static final int horizontalOffset = 16843663; // 0x101038f
+ field public static final int horizontalOffset = 16843662; // 0x101038e
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
@@ -557,7 +557,7 @@ package android {
field public static final int installLocation = 16843447; // 0x10102b7
field public static final int interpolator = 16843073; // 0x1010141
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
- field public static final int isAuxiliary = 16843643; // 0x101037b
+ field public static final int isAuxiliary = 16843642; // 0x101037a
field public static final int isDefault = 16843297; // 0x1010221
field public static final int isIndicator = 16843079; // 0x1010147
field public static final int isModifier = 16843334; // 0x1010246
@@ -610,7 +610,7 @@ package android {
field public static final int layout_centerInParent = 16843151; // 0x101018f
field public static final int layout_centerVertical = 16843153; // 0x1010191
field public static final int layout_column = 16843084; // 0x101014c
- field public static final int layout_columnSpan = 16843641; // 0x1010379
+ field public static final int layout_columnSpan = 16843640; // 0x1010378
field public static final int layout_gravity = 16842931; // 0x10100b3
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
@@ -618,8 +618,8 @@ package android {
field public static final int layout_marginLeft = 16842999; // 0x10100f7
field public static final int layout_marginRight = 16843001; // 0x10100f9
field public static final int layout_marginTop = 16843000; // 0x10100f8
- field public static final int layout_row = 16843639; // 0x1010377
- field public static final int layout_rowSpan = 16843640; // 0x1010378
+ field public static final int layout_row = 16843638; // 0x1010376
+ field public static final int layout_rowSpan = 16843639; // 0x1010377
field public static final int layout_scale = 16843155; // 0x1010193
field public static final int layout_span = 16843085; // 0x101014d
field public static final int layout_toLeftOf = 16843138; // 0x1010182
@@ -629,7 +629,7 @@ package android {
field public static final int layout_x = 16843135; // 0x101017f
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
- field public static final int leftChevronDrawable = 16843652; // 0x1010384
+ field public static final int leftChevronDrawable = 16843651; // 0x1010383
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -641,8 +641,8 @@ package android {
field public static final int listDividerAlertDialog = 16843525; // 0x1010305
field public static final int listPopupWindowStyle = 16843519; // 0x10102ff
field public static final int listPreferredItemHeight = 16842829; // 0x101004d
- field public static final int listPreferredItemHeightLarge = 16843664; // 0x1010390
- field public static final int listPreferredItemHeightSmall = 16843665; // 0x1010391
+ field public static final int listPreferredItemHeightLarge = 16843663; // 0x101038f
+ field public static final int listPreferredItemHeightSmall = 16843664; // 0x1010390
field public static final int listSelector = 16843003; // 0x10100fb
field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
field public static final int listViewStyle = 16842868; // 0x1010074
@@ -673,8 +673,8 @@ package android {
field public static final int minHeight = 16843072; // 0x1010140
field public static final int minLevel = 16843185; // 0x10101b1
field public static final int minLines = 16843094; // 0x1010156
- field public static final int minResizeHeight = 16843680; // 0x10103a0
- field public static final int minResizeWidth = 16843679; // 0x101039f
+ field public static final int minResizeHeight = 16843679; // 0x101039f
+ field public static final int minResizeWidth = 16843678; // 0x101039e
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
field public static final int mode = 16843134; // 0x101017e
@@ -690,7 +690,7 @@ package android {
field public static final int nextFocusUp = 16842979; // 0x10100e3
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
- field public static final int notificationTimeout = 16843647; // 0x101037f
+ field public static final int notificationTimeout = 16843646; // 0x101037e
field public static final int numColumns = 16843032; // 0x1010118
field public static final int numStars = 16843076; // 0x1010144
field public static final deprecated int numeric = 16843109; // 0x1010165
@@ -704,11 +704,11 @@ package android {
field public static final int orderingFromXml = 16843239; // 0x10101e7
field public static final int orientation = 16842948; // 0x10100c4
field public static final int outAnimation = 16843128; // 0x1010178
- field public static final int outerRadius = 16843657; // 0x1010389
+ field public static final int outerRadius = 16843656; // 0x1010388
field public static final int overScrollFooter = 16843459; // 0x10102c3
field public static final int overScrollHeader = 16843458; // 0x10102c2
field public static final int overScrollMode = 16843457; // 0x10102c1
- field public static final int packageNames = 16843645; // 0x101037d
+ field public static final int packageNames = 16843644; // 0x101037c
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingLeft = 16842966; // 0x10100d6
@@ -793,17 +793,17 @@ package android {
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int right = 16843183; // 0x10101af
- field public static final int rightChevronDrawable = 16843653; // 0x1010385
+ field public static final int rightChevronDrawable = 16843652; // 0x1010384
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
field public static final int ringtoneType = 16843257; // 0x10101f9
field public static final int rotation = 16843558; // 0x1010326
field public static final int rotationX = 16843559; // 0x1010327
field public static final int rotationY = 16843560; // 0x1010328
- field public static final int rowCount = 16843633; // 0x1010371
+ field public static final int rowCount = 16843632; // 0x1010370
field public static final int rowDelay = 16843216; // 0x10101d0
field public static final int rowEdgeFlags = 16843329; // 0x1010241
field public static final int rowHeight = 16843058; // 0x1010132
- field public static final int rowOrderPreserved = 16843634; // 0x1010372
+ field public static final int rowOrderPreserved = 16843633; // 0x1010371
field public static final int saveEnabled = 16842983; // 0x10100e7
field public static final int scaleGravity = 16843262; // 0x10101fe
field public static final int scaleHeight = 16843261; // 0x10101fd
@@ -869,7 +869,7 @@ package android {
field public static final int smallIcon = 16843422; // 0x101029e
field public static final int smallScreens = 16843396; // 0x1010284
field public static final int smoothScrollbar = 16843313; // 0x1010231
- field public static final int snapMargin = 16843660; // 0x101038c
+ field public static final int snapMargin = 16843659; // 0x101038b
field public static final int soundEffectsEnabled = 16843285; // 0x1010215
field public static final int spacing = 16843027; // 0x1010113
field public static final int spinnerDropDownItemStyle = 16842887; // 0x1010087
@@ -915,11 +915,10 @@ package android {
field public static final int stretchMode = 16843030; // 0x1010116
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextStyle = 16843513; // 0x10102f9
- field public static final int subtypeExtraValue = 16843684; // 0x10103a4
- field public static final int subtypeLocale = 16843683; // 0x10103a3
+ field public static final int subtypeExtraValue = 16843683; // 0x10103a3
+ field public static final int subtypeLocale = 16843682; // 0x10103a2
field public static final int suggestActionMsg = 16843228; // 0x10101dc
field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
- field public static final int suggestionsEnabled = 16843632; // 0x1010370
field public static final int summary = 16843241; // 0x10101e9
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
@@ -936,7 +935,7 @@ package android {
field public static final int tag = 16842961; // 0x10100d1
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
- field public static final int targetDrawables = 16843650; // 0x1010382
+ field public static final int targetDrawables = 16843649; // 0x1010381
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
field public static final int taskAffinity = 16842770; // 0x1010012
@@ -951,15 +950,15 @@ package android {
field public static final int tension = 16843370; // 0x101026a
field public static final int testOnly = 16843378; // 0x1010272
field public static final int text = 16843087; // 0x101014f
- field public static final int textAllCaps = 16843670; // 0x1010396
+ field public static final int textAllCaps = 16843669; // 0x1010395
field public static final int textAppearance = 16842804; // 0x1010034
field public static final int textAppearanceButton = 16843271; // 0x1010207
field public static final int textAppearanceInverse = 16842805; // 0x1010035
field public static final int textAppearanceLarge = 16842816; // 0x1010040
field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043
field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301
- field public static final int textAppearanceListItem = 16843688; // 0x10103a8
- field public static final int textAppearanceListItemSmall = 16843689; // 0x10103a9
+ field public static final int textAppearanceListItem = 16843687; // 0x10103a7
+ field public static final int textAppearanceListItemSmall = 16843688; // 0x10103a8
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
@@ -1027,7 +1026,7 @@ package android {
field public static final int toYScale = 16843205; // 0x10101c5
field public static final int top = 16843182; // 0x10101ae
field public static final int topBright = 16842955; // 0x10100cb
- field public static final int topChevronDrawable = 16843654; // 0x1010386
+ field public static final int topChevronDrawable = 16843653; // 0x1010385
field public static final int topDark = 16842951; // 0x10100c7
field public static final int topLeftRadius = 16843177; // 0x10101a9
field public static final int topOffset = 16843352; // 0x1010258
@@ -1039,12 +1038,12 @@ package android {
field public static final int translationY = 16843555; // 0x1010323
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
- field public static final int uiOptions = 16843682; // 0x10103a2
+ field public static final int uiOptions = 16843681; // 0x10103a1
field public static final int uncertainGestureColor = 16843382; // 0x1010276
field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int useDefaultMargins = 16843637; // 0x1010375
+ field public static final int useDefaultMargins = 16843636; // 0x1010374
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
field public static final int userVisible = 16843409; // 0x1010291
@@ -1058,10 +1057,10 @@ package android {
field public static final int verticalCorrection = 16843322; // 0x101023a
field public static final int verticalDivider = 16843054; // 0x101012e
field public static final int verticalGap = 16843328; // 0x1010240
- field public static final int verticalOffset = 16843662; // 0x101038e
+ field public static final int verticalOffset = 16843661; // 0x101038d
field public static final int verticalScrollbarPosition = 16843572; // 0x1010334
field public static final int verticalSpacing = 16843029; // 0x1010115
- field public static final int vibrationDuration = 16843659; // 0x101038b
+ field public static final int vibrationDuration = 16843658; // 0x101038a
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
field public static final int vmSafeMode = 16843448; // 0x10102b8
@@ -1078,7 +1077,7 @@ package android {
field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298
field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293
field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294
- field public static final int waveDrawable = 16843656; // 0x1010388
+ field public static final int waveDrawable = 16843655; // 0x1010387
field public static final int webTextViewStyle = 16843449; // 0x10102b9
field public static final int webViewStyle = 16842885; // 0x1010085
field public static final int weekDayTextAppearance = 16843592; // 0x1010348
@@ -20786,6 +20785,7 @@ package android.text.style {
method public java.lang.String getLocale();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
+ method public void setFlags(int);
method public void updateDrawState(android.text.TextPaint);
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
@@ -27106,7 +27106,6 @@ package android.widget {
method public void setSingleLine();
method public void setSingleLine(boolean);
method public final void setSpannableFactory(android.text.Spannable.Factory);
- method public void setSuggestionsEnabled(boolean);
method public final void setText(java.lang.CharSequence);
method public void setText(java.lang.CharSequence, android.widget.TextView.BufferType);
method public final void setText(char[], int, int);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 6bde802..5fed775 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -16,21 +16,18 @@
package android.text;
-import com.android.internal.util.ArrayUtils;
-
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.text.style.SuggestionSpan;
+
+import com.android.internal.util.ArrayUtils;
import java.lang.reflect.Array;
/**
* This is the class for text whose content and markup can both be changed.
*/
-public class SpannableStringBuilder
-implements CharSequence, GetChars, Spannable, Editable, Appendable,
- GraphicsOperations
-{
+public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
+ Appendable, GraphicsOperations {
/**
* Create a new SpannableStringBuilder with empty contents
*/
@@ -111,8 +108,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
if (where < 0) {
throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
} else if (where >= len) {
- throw new IndexOutOfBoundsException("charAt: " + where +
- " >= length " + len);
+ throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len);
}
if (where >= mGapStart)
@@ -266,8 +262,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return append(String.valueOf(text));
}
- private int change(int start, int end,
- CharSequence tb, int tbstart, int tbend) {
+ private int change(int start, int end, CharSequence tb, int tbstart, int tbend) {
return change(true, start, end, tb, tbstart, tbend);
}
@@ -277,8 +272,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
int ret = tbend - tbstart;
TextWatcher[] recipients = null;
- if (notify)
+ if (notify) {
recipients = sendTextWillChange(start, end - start, tbend - tbstart);
+ }
for (int i = mSpanCount - 1; i >= 0; i--) {
if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -353,7 +349,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
// no need for span fixup on pure insertion
if (tbend > tbstart && end - start == 0) {
if (notify) {
- removeSuggestionSpans(start, end);
sendTextChange(recipients, start, end - start, tbend - tbstart);
sendTextHasChanged(recipients);
}
@@ -388,7 +383,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
if (mSpanEnds[i] < mSpanStarts[i]) {
removeSpan(i);
}
- removeSuggestionSpans(start, end);
}
if (notify) {
@@ -399,30 +393,26 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return ret;
}
- /**
- * Removes the SuggestionSpan that overlap the [start, end] range, and that would
- * not make sense anymore after the change.
- */
- private void removeSuggestionSpans(int start, int end) {
- for (int i = mSpanCount - 1; i >= 0; i--) {
- final int spanEnd = mSpanEnds[i];
- final int spanSpart = mSpanStarts[i];
- if ((mSpans[i] instanceof SuggestionSpan) && (
- (spanSpart < start && spanEnd > start) ||
- (spanSpart < end && spanEnd > end))) {
- removeSpan(i);
- }
- }
- }
-
private void removeSpan(int i) {
- // XXX send notification on removal
- System.arraycopy(mSpans, i + 1, mSpans, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, mSpanCount - (i + 1));
+ Object object = mSpans[i];
+
+ int start = mSpanStarts[i];
+ int end = mSpanEnds[i];
+
+ if (start > mGapStart) start -= mGapLength;
+ if (end > mGapStart) end -= mGapLength;
+
+ int count = mSpanCount - (i + 1);
+ System.arraycopy(mSpans, i + 1, mSpans, i, count);
+ System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
+ System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
+ System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
mSpanCount--;
+
+ mSpans[mSpanCount] = null;
+
+ sendSpanRemoved(object, start, end);
}
// Documentation from interface
@@ -462,11 +452,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
moveGapTo(end);
TextWatcher[] recipients;
- recipients = sendTextWillChange(start, end - start,
- tbend - tbstart);
-
int origlen = end - start;
+ recipients = sendTextWillChange(start, origlen, tbend - tbstart);
+
if (mGapLength < 2)
resizeFor(length() + 1);
@@ -486,11 +475,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
new Exception("mGapLength < 1").printStackTrace();
}
- int oldlen = (end + 1) - start;
-
int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
change(false, start, start + 1, "", 0, 0);
- change(false, start + inserted, start + inserted + oldlen - 1, "", 0, 0);
+ change(false, start + inserted, start + inserted + origlen, "", 0, 0);
/*
* Special case to keep the cursor in the same position
@@ -515,13 +502,12 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
off = off * inserted / (end - start);
selend = (int) off + start;
- setSpan(false, Selection.SELECTION_END, selend, selend,
- Spanned.SPAN_POINT_POINT);
+ setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
}
-
sendTextChange(recipients, start, origlen, inserted);
sendTextHasChanged(recipients);
}
+
return this;
}
@@ -534,8 +520,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
setSpan(true, what, start, end, flags);
}
- private void setSpan(boolean send,
- Object what, int start, int end, int flags) {
+ private void setSpan(boolean send, Object what, int start, int end, int flags) {
int nstart = start;
int nend = end;
@@ -546,8 +531,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
char c = charAt(start - 1);
if (c != '\n')
- throw new RuntimeException(
- "PARAGRAPH span must start at paragraph boundary");
+ throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
}
}
@@ -556,23 +540,22 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
char c = charAt(end - 1);
if (c != '\n')
- throw new RuntimeException(
- "PARAGRAPH span must end at paragraph boundary");
+ throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
}
}
- if (start > mGapStart)
+ if (start > mGapStart) {
start += mGapLength;
- else if (start == mGapStart) {
+ } else if (start == mGapStart) {
int flag = (flags & START_MASK) >> START_SHIFT;
if (flag == POINT || (flag == PARAGRAPH && start == length()))
start += mGapLength;
}
- if (end > mGapStart)
+ if (end > mGapStart) {
end += mGapLength;
- else if (end == mGapStart) {
+ } else if (end == mGapStart) {
int flag = (flags & END_MASK);
if (flag == POINT || (flag == PARAGRAPH && end == length()))
@@ -637,25 +620,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
public void removeSpan(Object what) {
for (int i = mSpanCount - 1; i >= 0; i--) {
if (mSpans[i] == what) {
- int ostart = mSpanStarts[i];
- int oend = mSpanEnds[i];
-
- if (ostart > mGapStart)
- ostart -= mGapLength;
- if (oend > mGapStart)
- oend -= mGapLength;
-
- int count = mSpanCount - (i + 1);
-
- System.arraycopy(mSpans, i + 1, mSpans, i, count);
- System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
- System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
- System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
-
- mSpanCount--;
- mSpans[mSpanCount] = null;
-
- sendSpanRemoved(what, ostart, oend);
+ removeSpan(i);
return;
}
}
@@ -729,6 +694,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
*/
@SuppressWarnings("unchecked")
public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
+ if (kind == null) return ArrayUtils.emptyArray(kind);
+
int spanCount = mSpanCount;
Object[] spans = mSpans;
int[] starts = mSpanStarts;
@@ -742,6 +709,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
T ret1 = null;
for (int i = 0; i < spanCount; i++) {
+ if (!kind.isInstance(spans[i])) continue;
+
int spanStart = starts[i];
int spanEnd = ends[i];
@@ -766,10 +735,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
continue;
}
- if (kind != null && !kind.isInstance(spans[i])) {
- continue;
- }
-
if (count == 0) {
// Safe conversion thanks to the isInstance test above
ret1 = (T) spans[i];
@@ -909,8 +874,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return recip;
}
- private void sendTextChange(TextWatcher[] recip, int start, int before,
- int after) {
+ private void sendTextChange(TextWatcher[] recip, int start, int before, int after) {
int n = recip.length;
for (int i = 0; i < n; i++) {
@@ -945,8 +909,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
}
private void sendSpanChanged(Object what, int s, int e, int st, int en) {
- SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
- SpanWatcher.class);
+ SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), SpanWatcher.class);
int n = recip.length;
for (int i = 0; i < n; i++) {
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index af524ee..0433ec4 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -73,6 +73,10 @@ public class WordIterator implements Selection.PositionIterator {
}
};
+ public void forceUpdate() {
+ mCurrentDirty = true;
+ }
+
public void setCharSequence(CharSequence incoming) {
// When incoming is different object, move listeners to new sequence
// and mark as dirty so we reload contents.
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
new file mode 100644
index 0000000..9b23177
--- /dev/null
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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.style;
+
+/**
+ * A SpellCheckSpan is an internal data structure created by the TextView's SpellChecker to
+ * annotate portions of the text that are about to or currently being spell checked. They are
+ * automatically removed once the spell check is completed.
+ *
+ * @hide
+ */
+public class SpellCheckSpan {
+
+ private boolean mSpellCheckInProgress;
+
+ public SpellCheckSpan() {
+ mSpellCheckInProgress = false;
+ }
+
+ public void setSpellCheckInProgress() {
+ mSpellCheckInProgress = true;
+ }
+
+ public boolean isSpellCheckInProgress() {
+ return mSpellCheckInProgress;
+ }
+}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index fb94bc7..ea57f917 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -40,7 +40,7 @@ import java.util.Locale;
* These spans should typically be created by the input method to provide correction and alternates
* for the text.
*
- * @see TextView#setSuggestionsEnabled(boolean)
+ * @see TextView#isSuggestionsEnabled()
*/
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
@@ -76,7 +76,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
* And the current IME might want to specify any IME as the target IME including other IMEs.
*/
- private final int mFlags;
+ private int mFlags;
private final String[] mSuggestions;
private final String mLocaleString;
private final String mNotificationTargetClassName;
@@ -134,8 +134,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
} else {
mNotificationTargetClassName = "";
}
- mHashCode = hashCodeInternal(
- mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);
+ mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
initStyle(context);
}
@@ -211,6 +210,10 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
return mFlags;
}
+ public void setFlags(int flags) {
+ mFlags = flags;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -247,10 +250,10 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
return mHashCode;
}
- private static int hashCodeInternal(int flags, String[] suggestions,String locale,
+ private static int hashCodeInternal(String[] suggestions, String locale,
String notificationTargetClassName) {
- return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
- notificationTargetClassName});
+ return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
+ locale, notificationTargetClassName});
}
public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index ed0f89d..62a06b9 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -16,6 +16,8 @@
package android.view.textservice;
+import com.android.internal.util.ArrayUtils;
+
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,7 +25,7 @@ import android.os.Parcelable;
* This class contains a metadata of suggestions from the text service
*/
public final class SuggestionsInfo implements Parcelable {
- private static final String[] EMPTY = new String[0];
+ private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
/**
* Flag of the attributes of the suggestions that can be obtained by
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
new file mode 100644
index 0000000..5e3b956
--- /dev/null
+++ b/core/java/android/widget/SpellChecker.java
@@ -0,0 +1,226 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.widget;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.style.SpellCheckSpan;
+import android.text.style.SuggestionSpan;
+import android.util.Log;
+import android.view.textservice.SpellCheckerSession;
+import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+import android.view.textservice.TextServicesManager;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Locale;
+
+
+/**
+ * Helper class for TextView. Bridge between the TextView and the Dictionnary service.
+ *
+ * @hide
+ */
+public class SpellChecker implements SpellCheckerSessionListener {
+ private static final String LOG_TAG = "SpellChecker";
+ private static final boolean DEBUG_SPELL_CHECK = false;
+ private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds
+
+ private final TextView mTextView;
+
+ final SpellCheckerSession spellCheckerSession;
+ final int mCookie;
+
+ // Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position
+ private int[] mIds;
+ private SpellCheckSpan[] mSpellCheckSpans;
+ // The actual current number of used slots in the above arrays
+ private int mLength;
+
+ private int mSpanSequenceCounter = 0;
+ private Runnable mChecker;
+
+ public SpellChecker(TextView textView) {
+ mTextView = textView;
+
+ final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
+ getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
+ spellCheckerSession = textServicesManager.newSpellCheckerSession(
+ null /* not currently used by the textServicesManager */, Locale.getDefault(),
+ this, true /* means use the languages defined in Settings */);
+ mCookie = hashCode();
+
+ // Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand
+ final int size = ArrayUtils.idealObjectArraySize(4);
+ mIds = new int[size];
+ mSpellCheckSpans = new SpellCheckSpan[size];
+ mLength = 0;
+ }
+
+ public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
+ int length = mIds.length;
+ if (mLength >= length) {
+ final int newSize = length * 2;
+ int[] newIds = new int[newSize];
+ SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize];
+ System.arraycopy(mIds, 0, newIds, 0, length);
+ System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length);
+ mIds = newIds;
+ mSpellCheckSpans = newSpellCheckSpans;
+ }
+
+ mIds[mLength] = mSpanSequenceCounter++;
+ mSpellCheckSpans[mLength] = spellCheckSpan;
+ mLength++;
+
+ if (DEBUG_SPELL_CHECK) {
+ final Editable mText = (Editable) mTextView.getText();
+ int start = mText.getSpanStart(spellCheckSpan);
+ int end = mText.getSpanEnd(spellCheckSpan);
+ if (start >= 0 && end >= 0) {
+ Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end));
+ } else {
+ Log.d(LOG_TAG, "Schedule check EMPTY!");
+ }
+ }
+
+ scheduleSpellCheck();
+ }
+
+ public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
+ for (int i = 0; i < mLength; i++) {
+ if (mSpellCheckSpans[i] == spellCheckSpan) {
+ removeAtIndex(i);
+ return;
+ }
+ }
+ }
+
+ private void removeAtIndex(int i) {
+ System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1);
+ System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1);
+ mLength--;
+ }
+
+ public void onSelectionChanged() {
+ scheduleSpellCheck();
+ }
+
+ private void scheduleSpellCheck() {
+ if (mLength == 0) return;
+ if (mChecker != null) {
+ mTextView.removeCallbacks(mChecker);
+ }
+ if (mChecker == null) {
+ mChecker = new Runnable() {
+ public void run() {
+ spellCheck();
+ }
+ };
+ }
+ mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK);
+ }
+
+ private void spellCheck() {
+ final Editable editable = (Editable) mTextView.getText();
+ final int selectionStart = Selection.getSelectionStart(editable);
+ final int selectionEnd = Selection.getSelectionEnd(editable);
+
+ TextInfo[] textInfos = new TextInfo[mLength];
+ int textInfosCount = 0;
+
+ for (int i = 0; i < mLength; i++) {
+ SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
+
+ if (spellCheckSpan.isSpellCheckInProgress()) continue;
+
+ final int start = editable.getSpanStart(spellCheckSpan);
+ final int end = editable.getSpanEnd(spellCheckSpan);
+
+ // Do not check this word if the user is currently editing it
+ if (start >= 0 && end >= 0 && (selectionEnd < start || selectionStart > end)) {
+ final String word = editable.subSequence(start, end).toString();
+ spellCheckSpan.setSpellCheckInProgress();
+ textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
+ }
+ }
+
+ if (textInfosCount > 0) {
+ if (textInfosCount < mLength) {
+ TextInfo[] textInfosCopy = new TextInfo[textInfosCount];
+ System.arraycopy(textInfos, 0, textInfosCopy, 0, textInfosCount);
+ textInfos = textInfosCopy;
+ }
+ spellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
+ false /* TODO Set sequentialWords to true for initial spell check */);
+ }
+ }
+
+ @Override
+ public void onGetSuggestions(SuggestionsInfo[] results) {
+ final Editable editable = (Editable) mTextView.getText();
+ for (int i = 0; i < results.length; i++) {
+ SuggestionsInfo suggestionsInfo = results[i];
+ if (suggestionsInfo.getCookie() != mCookie) continue;
+
+ final int sequenceNumber = suggestionsInfo.getSequence();
+ // Starting from the end, to limit the number of array copy while removing
+ for (int j = mLength - 1; j >= 0; j--) {
+ if (sequenceNumber == mIds[j]) {
+ SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
+ final int attributes = suggestionsInfo.getSuggestionsAttributes();
+ boolean isInDictionary =
+ ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
+ boolean looksLikeTypo =
+ ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
+
+ if (DEBUG_SPELL_CHECK) {
+ final int start = editable.getSpanStart(spellCheckSpan);
+ final int end = editable.getSpanEnd(spellCheckSpan);
+ Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " +
+ editable.subSequence(start, end) +
+ "\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") +
+ "\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO"));
+ }
+
+ if (!isInDictionary && looksLikeTypo) {
+ String[] suggestions = getSuggestions(suggestionsInfo);
+ if (suggestions.length > 0) {
+ SuggestionSpan suggestionSpan = new SuggestionSpan(
+ mTextView.getContext(), suggestions,
+ SuggestionSpan.FLAG_EASY_CORRECT |
+ SuggestionSpan.FLAG_MISSPELLED);
+ final int start = editable.getSpanStart(spellCheckSpan);
+ final int end = editable.getSpanEnd(spellCheckSpan);
+ editable.setSpan(suggestionSpan, start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ // TODO limit to the word rectangle region
+ mTextView.invalidate();
+
+ if (DEBUG_SPELL_CHECK) {
+ String suggestionsString = "";
+ for (String s : suggestions) { suggestionsString += s + "|"; }
+ Log.d(LOG_TAG, " Suggestions for " + sequenceNumber + " " +
+ editable.subSequence(start, end)+ " " + suggestionsString);
+ }
+ }
+ }
+ editable.removeSpan(spellCheckSpan);
+ }
+ }
+ }
+ }
+
+ private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) {
+ final int len = Math.max(0, suggestionsInfo.getSuggestionsCount());
+ String[] suggestions = new String[len];
+ for (int j = 0; j < len; ++j) {
+ suggestions[j] = suggestionsInfo.getSuggestionAt(j);
+ }
+ return suggestions;
+ }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 662b964..683a984 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -61,11 +61,6 @@ import android.text.SpannedString;
import android.text.StaticLayout;
import android.text.TextDirectionHeuristic;
import android.text.TextDirectionHeuristics;
-import android.text.TextDirectionHeuristics.AnyStrong;
-import android.text.TextDirectionHeuristics.CharCount;
-import android.text.TextDirectionHeuristics.FirstStrong;
-import android.text.TextDirectionHeuristics.TextDirectionAlgorithm;
-import android.text.TextDirectionHeuristics.TextDirectionHeuristicImpl;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -88,6 +83,7 @@ import android.text.method.TransformationMethod2;
import android.text.method.WordIterator;
import android.text.style.ClickableSpan;
import android.text.style.ParagraphStyle;
+import android.text.style.SpellCheckSpan;
import android.text.style.SuggestionSpan;
import android.text.style.TextAppearanceSpan;
import android.text.style.URLSpan;
@@ -220,7 +216,6 @@ import java.util.HashMap;
* @attr ref android.R.styleable#TextView_imeActionLabel
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
- * @attr ref android.R.styleable#TextView_suggestionsEnabled
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -334,7 +329,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mTextEditSuggestionItemLayout;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
private SuggestionRangeSpan mSuggestionRangeSpan;
- private boolean mSuggestionsEnabled = true;
private int mCursorDrawableRes;
private final Drawable[] mCursorDrawable = new Drawable[2];
@@ -356,6 +350,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private WordIterator mWordIterator;
+ private SpellChecker mSpellChecker;
+
// The alignment to pass to Layout, or null if not resolved.
private Layout.Alignment mLayoutAlignment;
@@ -826,10 +822,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mTextIsSelectable = a.getBoolean(attr, false);
break;
- case com.android.internal.R.styleable.TextView_suggestionsEnabled:
- mSuggestionsEnabled = a.getBoolean(attr, true);
- break;
-
case com.android.internal.R.styleable.TextView_textAllCaps:
allCaps = a.getBoolean(attr, false);
break;
@@ -3100,18 +3092,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
boolean needEditableForNotification = false;
+ boolean startSpellCheck = false;
if (mListeners != null && mListeners.size() != 0) {
needEditableForNotification = true;
}
- if (type == BufferType.EDITABLE || mInput != null ||
- needEditableForNotification) {
+ if (type == BufferType.EDITABLE || mInput != null || needEditableForNotification) {
Editable t = mEditableFactory.newEditable(text);
text = t;
setFilters(t, mFilters);
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
+ startSpellCheck = true;
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
} else if (!(text instanceof CharWrapper)) {
@@ -3200,6 +3193,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
+ if (startSpellCheck) {
+ updateSpellCheckSpans(0, textLength);
+ }
+
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
}
@@ -7113,8 +7110,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* to turn off ellipsizing.
*
* If {@link #setMaxLines} has been used to set two or more lines,
- * {@link TextUtils.TruncateAt#END} and {@link TextUtils.TruncateAt#MARQUEE}
- * are only supported (other ellipsizing types will not do anything).
+ * {@link android.text.TextUtils.TruncateAt#END} and
+ * {@link android.text.TextUtils.TruncateAt#MARQUEE}* are only supported
+ * (other ellipsizing types will not do anything).
*
* @attr ref android.R.styleable#TextView_ellipsize
*/
@@ -7376,7 +7374,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param lengthAfter The length of the replacement modified text
*/
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
- // intentionally empty
+ // intentionally empty, template pattern method can be overridden by subclasses
}
/**
@@ -7388,6 +7386,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
protected void onSelectionChanged(int selStart, int selEnd) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
+ if (mSpellChecker != null) {
+ mSpellChecker.onSelectionChanged();
+ }
}
/**
@@ -7422,8 +7423,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void sendBeforeTextChanged(CharSequence text, int start, int before,
- int after) {
+ private void sendBeforeTextChanged(CharSequence text, int start, int before, int after) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
@@ -7431,14 +7431,32 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
list.get(i).beforeTextChanged(text, start, before, after);
}
}
+
+ // The spans that are inside or intersect the modified region no longer make sense
+ removeIntersectingSpans(start, start + before, SpellCheckSpan.class);
+ removeIntersectingSpans(start, start + before, SuggestionSpan.class);
+ }
+
+ // Removes all spans that are inside or actually overlap the start..end range
+ private <T> void removeIntersectingSpans(int start, int end, Class<T> type) {
+ if (!(mText instanceof Editable)) return;
+ Editable text = (Editable) mText;
+
+ T[] spans = text.getSpans(start, end, type);
+ final int length = spans.length;
+ for (int i = 0; i < length; i++) {
+ final int s = text.getSpanStart(spans[i]);
+ final int e = text.getSpanEnd(spans[i]);
+ if (e == start || s == end) break;
+ text.removeSpan(spans[i]);
+ }
}
/**
* Not private so it can be called from an inner class without going
* through a thunk.
*/
- void sendOnTextChanged(CharSequence text, int start, int before,
- int after) {
+ void sendOnTextChanged(CharSequence text, int start, int before, int after) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
@@ -7486,6 +7504,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
+ // The WordIterator text change listener may be called after this one.
+ // Make sure this changed text is rescanned before the iterator is used on it.
+ getWordIterator().forceUpdate();
+ updateSpellCheckSpans(start, start + after);
+
// Hide the controllers if the amount of content changed
if (before != after) {
hideControllers();
@@ -7573,7 +7596,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
-
+
if (what instanceof ParcelableSpan) {
// If this is a span that can be sent to a remote process,
// the current extract editor would be interested in it.
@@ -7603,10 +7626,102 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
+
+ if (what instanceof SpellCheckSpan) {
+ if (newStart < 0) {
+ getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what);
+ } else if (oldStart < 0) {
+ getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what);
+ }
+ }
+
+ if (what instanceof SuggestionSpan) {
+ if (newStart < 0) {
+ Log.d("spellcheck", "REMOVE suggspan " + mText.subSequence(oldStart, oldEnd));
+ }
+ }
+ }
+
+ /**
+ * Create new SpellCheckSpans on the modified region.
+ */
+ private void updateSpellCheckSpans(int start, int end) {
+ if (!(mText instanceof Editable) || !isSuggestionsEnabled()) return;
+ Editable text = (Editable) mText;
+
+ WordIterator wordIterator = getWordIterator();
+ wordIterator.setCharSequence(text);
+
+ // Move back to the beginning of the current word, if any
+ int wordStart = wordIterator.preceding(start);
+ int wordEnd;
+ if (wordStart == BreakIterator.DONE) {
+ wordEnd = wordIterator.following(start);
+ if (wordEnd != BreakIterator.DONE) {
+ wordStart = wordIterator.getBeginning(wordEnd);
+ }
+ } else {
+ wordEnd = wordIterator.getEnd(wordStart);
+ }
+ if (wordEnd == BreakIterator.DONE) {
+ return;
+ }
+
+ // Iterate over the newly added text and schedule new SpellCheckSpans
+ while (wordStart <= end) {
+ if (wordEnd >= start) {
+ // A word across the interval boundaries must remove boundary edition spans
+ if (wordStart < start && wordEnd > start) {
+ removeEditionSpansAt(start, text);
+ }
+
+ if (wordStart < end && wordEnd > end) {
+ removeEditionSpansAt(end, text);
+ }
+
+ // Do not create new boundary spans if they already exist
+ boolean createSpellCheckSpan = true;
+ if (wordEnd == start) {
+ SpellCheckSpan[] spellCheckSpans = text.getSpans(start, start,
+ SpellCheckSpan.class);
+ if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
+ }
+
+ if (wordStart == end) {
+ SpellCheckSpan[] spellCheckSpans = text.getSpans(end, end,
+ SpellCheckSpan.class);
+ if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
+ }
+
+ if (createSpellCheckSpan) {
+ text.setSpan(new SpellCheckSpan(), wordStart, wordEnd,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ // iterate word by word
+ wordEnd = wordIterator.following(wordEnd);
+ if (wordEnd == BreakIterator.DONE) return;
+ wordStart = wordIterator.getBeginning(wordEnd);
+ if (wordStart == BreakIterator.DONE) {
+ Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText);
+ return;
+ }
+ }
+ }
+
+ private static void removeEditionSpansAt(int offset, Editable text) {
+ SuggestionSpan[] suggestionSpans = text.getSpans(offset, offset, SuggestionSpan.class);
+ for (int i = 0; i < suggestionSpans.length; i++) {
+ text.removeSpan(suggestionSpans[i]);
+ }
+ SpellCheckSpan[] spellCheckSpans = text.getSpans(offset, offset, SpellCheckSpan.class);
+ for (int i = 0; i < spellCheckSpans.length; i++) {
+ text.removeSpan(spellCheckSpans[i]);
+ }
}
- private class ChangeWatcher
- implements TextWatcher, SpanWatcher {
+ private class ChangeWatcher implements TextWatcher, SpanWatcher {
private CharSequence mBeforeText;
@@ -7631,8 +7746,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
TextView.this.handleTextChanged(buffer, start, before, after);
if (AccessibilityManager.getInstance(mContext).isEnabled() &&
- (isFocused() || isSelected() &&
- isShown())) {
+ (isFocused() || isSelected() && isShown())) {
sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
mBeforeText = null;
}
@@ -7642,8 +7756,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
TextView.this.sendAfterTextChanged(buffer);
- if (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0) {
+ if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
}
}
@@ -7841,17 +7954,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mInputContentType != null) {
mInputContentType.enterDown = false;
}
+
hideControllers();
- removeAllSuggestionSpans();
+
+ removeSpans(0, mText.length(), SuggestionSpan.class);
+ removeSpans(0, mText.length(), SpellCheckSpan.class);
}
startStopMarquee(hasWindowFocus);
}
- private void removeAllSuggestionSpans() {
+ private void removeSpans(int start, int end, Class<?> type) {
if (mText instanceof Editable) {
Editable editable = ((Editable) mText);
- SuggestionSpan[] spans = editable.getSpans(0, mText.length(), SuggestionSpan.class);
+ Object[] spans = editable.getSpans(start, end, type);
final int length = spans.length;
for (int i = 0; i < length; i++) {
editable.removeSpan(spans[i]);
@@ -7969,6 +8085,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
+
+ handled = true;
}
if (handled) {
@@ -7980,11 +8098,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * @return <code>true</code> if the cursor/current selection overlaps a {@link SuggestionSpan}.
+ */
+ private boolean isCursorInsideSuggestionSpan() {
+ if (!(mText instanceof Spannable)) return false;
+
+ SuggestionSpan[] suggestionSpans = ((Spannable) mText).getSpans(getSelectionStart(),
+ getSelectionEnd(), SuggestionSpan.class);
+ return (suggestionSpans.length > 0);
+ }
+
+ /**
* @return <code>true</code> if the cursor is inside an {@link SuggestionSpan} with
* {@link SuggestionSpan#FLAG_EASY_CORRECT} set.
*/
private boolean isCursorInsideEasyCorrectionSpan() {
- Spannable spannable = (Spannable) TextView.this.mText;
+ Spannable spannable = (Spannable) mText;
SuggestionSpan[] suggestionSpans = spannable.getSpans(getSelectionStart(),
getSelectionEnd(), SuggestionSpan.class);
for (int i = 0; i < suggestionSpans.length; i++) {
@@ -8445,16 +8574,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selectionStart = ((Spanned) mText).getSpanStart(url);
selectionEnd = ((Spanned) mText).getSpanEnd(url);
} else {
- if (mWordIterator == null) {
- mWordIterator = new WordIterator();
- }
- // WordIerator handles text changes, this is a no-op if text in unchanged.
- mWordIterator.setCharSequence(mText);
+ WordIterator wordIterator = getWordIterator();
+ // WordIterator handles text changes, this is a no-op if text in unchanged.
+ wordIterator.setCharSequence(mText);
- selectionStart = mWordIterator.getBeginning(minOffset);
+ selectionStart = wordIterator.getBeginning(minOffset);
if (selectionStart == BreakIterator.DONE) return false;
- selectionEnd = mWordIterator.getEnd(maxOffset);
+ selectionEnd = wordIterator.getEnd(maxOffset);
if (selectionEnd == BreakIterator.DONE) return false;
}
@@ -8462,6 +8589,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
}
+ WordIterator getWordIterator() {
+ if (mWordIterator == null) {
+ mWordIterator = new WordIterator();
+ }
+ return mWordIterator;
+ }
+
+ private SpellChecker getSpellChecker() {
+ if (mSpellChecker == null) {
+ mSpellChecker = new SpellChecker(this);
+ }
+ return mSpellChecker;
+ }
+
private long getLastTouchOffsets() {
int minOffset, maxOffset;
@@ -8790,7 +8931,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
stopSelectionActionMode();
Selection.setSelection((Spannable) mText, offset);
- getInsertionController().showImmediately();
+ getInsertionController().showWithActionPopup();
handled = true;
}
@@ -9067,10 +9208,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnClickListener {
- private static final int MAX_NUMBER_SUGGESTIONS = 5;
+ private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
private static final int NO_SUGGESTIONS = -1;
+ private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
private WordIterator mSuggestionWordIterator;
- private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0];
+ private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
+ [(int) (AVERAGE_HIGHLIGHTS_PER_SUGGESTION * MAX_NUMBER_SUGGESTIONS)];
@Override
protected void createPopupWindow() {
@@ -9149,9 +9292,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void show() {
if (!(mText instanceof Editable)) return;
- updateSuggestions();
- super.show();
+ if (updateSuggestions()) {
+ super.show();
+ }
}
@Override
@@ -9179,7 +9323,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void updateSuggestions() {
+ private boolean updateSuggestions() {
Spannable spannable = (Spannable)TextView.this.mText;
SuggestionSpan[] suggestionSpans = getSuggestionSpans();
@@ -9217,22 +9361,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- if (totalNbSuggestions == 0) {
- // TODO Replace by final text, use a dedicated layout, add a fade out timer...
- TextView textView = (TextView) mContentView.getChildAt(0);
- textView.setText("No suggestions available");
- SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
- suggestionInfo.spanStart = NO_SUGGESTIONS;
- totalNbSuggestions++;
- } else {
- if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
- ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (totalNbSuggestions == 0) return false;
- for (int i = 0; i < totalNbSuggestions; i++) {
- final TextView textView = (TextView) mContentView.getChildAt(i);
- highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
- }
+ if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+ ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ for (int i = 0; i < totalNbSuggestions; i++) {
+ final TextView textView = (TextView) mContentView.getChildAt(i);
+ highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
}
for (int i = 0; i < totalNbSuggestions; i++) {
@@ -9241,6 +9378,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
for (int i = totalNbSuggestions; i < MAX_NUMBER_SUGGESTIONS; i++) {
mContentView.getChildAt(i).setVisibility(GONE);
}
+
+ return true;
+ }
+
+ private void onDictionarySuggestionsReceived(String[] suggestions) {
+ if (suggestions.length == 0) {
+ // TODO Actual implementation of this feature
+ suggestions = new String[] {"Add to dictionary"};
+ }
+
+ WordIterator wordIterator = getWordIterator();
+ wordIterator.setCharSequence(mText);
+
+ final int pos = getSelectionStart();
+ int wordStart = wordIterator.getBeginning(pos);
+ int wordEnd = wordIterator.getEnd(pos);
+
+ SuggestionSpan suggestionSpan = new SuggestionSpan(getContext(), suggestions, 0);
+ ((Editable) mText).setSpan(suggestionSpan, wordStart, wordEnd,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ show();
}
private long[] getWordLimits(CharSequence text) {
@@ -9422,6 +9580,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final String originalText = mText.subSequence(spanStart, spanEnd).toString();
((Editable) mText).replace(spanStart, spanEnd, suggestion);
+ // A replacement on a misspelled text removes the misspelled flag.
+ // TODO restore the flag if the misspelled word is selected back?
+ int suggestionSpanFlags = suggestionInfo.suggestionSpan.getFlags();
+ if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
+ suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
+ suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
+ suggestionInfo.suggestionSpan.setFlags(suggestionSpanFlags);
+ }
+
// Notify source IME of the suggestion pick. Do this before swaping texts.
if (!TextUtils.isEmpty(
suggestionInfo.suggestionSpan.getNotificationTargetClassName())) {
@@ -9471,53 +9638,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean areSuggestionsShown() {
return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
- }
+ }
+
+ void onDictionarySuggestionsReceived(String[] suggestions) {
+ if (mSuggestionsPopupWindow != null) {
+ mSuggestionsPopupWindow.onDictionarySuggestionsReceived(suggestions);
+ }
+ }
/**
- * Some parts of the text can have alternate suggestion text attached. This is typically done by
- * the IME by adding {@link SuggestionSpan}s to the text.
+ * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
+ * by the IME or by the spell checker as the user types. This is done by adding
+ * {@link SuggestionSpan}s to the text.
*
* When suggestions are enabled (default), this list of suggestions will be displayed when the
- * user double taps on these parts of the text. No suggestions are displayed when this value is
- * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value.
- *
- * Note that suggestions are only enabled for a subset of input types. In addition to setting
- * this flag to <code>true</code> using {@link #setSuggestionsEnabled(boolean)} or the
- * <code>android:suggestionsEnabled</code> xml attribute, this method will return
- * <code>true</code> only if the class of your input type is {@link InputType#TYPE_CLASS_TEXT}.
- * In addition, the type variation must also be one of
+ * user asks for them on these parts of the text. This value depends on the inputType of this
+ * TextView.
+ *
+ * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}.
+ *
+ * In addition, the type variation must be one of
* {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
* {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
* {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
* {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
* {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
*
- * @return true if the suggestions popup window is enabled.
+ * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set.
*
- * @attr ref android.R.styleable#TextView_suggestionsEnabled
+ * @return true if the suggestions popup window is enabled, based on the inputType.
*/
public boolean isSuggestionsEnabled() {
- if (!mSuggestionsEnabled) return false;
if ((mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false;
+ if ((mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false;
+
final int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
+ return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT ||
variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE ||
variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ||
- variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) return true;
-
- return false;
- }
-
- /**
- * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}.
- *
- * @param enabled Whether or not suggestions are enabled.
- *
- * @attr ref android.R.styleable#TextView_suggestionsEnabled
- */
- public void setSuggestionsEnabled(boolean enabled) {
- mSuggestionsEnabled = enabled;
+ variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
}
/**
@@ -9787,11 +9947,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void show() {
boolean canPaste = canPaste();
- boolean suggestionsEnabled = isSuggestionsEnabled();
+ boolean canSuggest = isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
- mReplaceTextView.setVisibility(suggestionsEnabled ? View.VISIBLE : View.GONE);
+ mReplaceTextView.setVisibility(canSuggest ? View.VISIBLE : View.GONE);
- if (!canPaste && !suggestionsEnabled) return;
+ if (!canPaste && !canSuggest) return;
super.show();
}
@@ -9802,6 +9962,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
onTextContextMenuItem(ID_PASTE);
hide();
} else if (view == mReplaceTextView) {
+ final int middle = (getSelectionStart() + getSelectionEnd()) / 2;
+ stopSelectionActionMode();
+ Selection.setSelection((Spannable) mText, middle);
showSuggestions();
}
}
@@ -10133,17 +10296,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void show() {
super.show();
- hideAfterDelay();
- }
-
- public void show(int delayBeforeShowActionPopup) {
- show();
final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
- delayBeforeShowActionPopup = 0;
+ showActionPopupWindow(0);
}
- showActionPopupWindow(delayBeforeShowActionPopup);
+
+ hideAfterDelay();
+ }
+
+ public void showWithActionPopup() {
+ show();
+ showActionPopupWindow(0);
}
private void hideAfterDelay() {
@@ -10194,7 +10358,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Tapping on the handle dismisses the displayed action popup
mActionPopupWindow.hide();
} else {
- show(0);
+ showWithActionPopup();
}
}
}
@@ -10349,16 +10513,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private class InsertionPointCursorController implements CursorController {
- private static final int DELAY_BEFORE_PASTE_ACTION = 1600;
-
private InsertionHandleView mHandle;
public void show() {
- getHandle().show(DELAY_BEFORE_PASTE_ACTION);
+ getHandle().show();
}
- public void showImmediately() {
- getHandle().show(0);
+ public void showWithActionPopup() {
+ getHandle().showWithActionPopup();
}
public void hide() {
@@ -10390,7 +10552,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private class SelectionModifierCursorController implements CursorController {
- private static final int DELAY_BEFORE_REPLACE_ACTION = 1200;
+ private static final int DELAY_BEFORE_REPLACE_ACTION = 200; // milliseconds
// The cursor controller handles, lazily created when shown.
private SelectionStartHandleView mStartHandle;
private SelectionEndHandleView mEndHandle;
@@ -10879,8 +11041,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mAutoLinkMask;
private boolean mLinksClickable = true;
- private float mSpacingMult = 1;
- private float mSpacingAdd = 0;
+ private float mSpacingMult = 1.0f;
+ private float mSpacingAdd = 0.0f;
private boolean mTextIsSelectable = false;
private static final int LINES = 1;
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
index 5579443..b161361 100644
--- a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
index 6674914..0a7b364 100644
--- a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index bc86ab7..12df99e 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -151,7 +151,6 @@
android:background="@drawable/lockscreen_password_field_dark"
android:textColor="?android:attr/textColorPrimary"
android:imeOptions="flagNoFullscreen|actionDone"
- android:suggestionsEnabled="false"
/>
<ImageView android:id="@+id/switch_ime_button"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 994c439..6145e47 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -114,7 +114,7 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#ffffffff"
android:imeOptions="actionDone"
- android:suggestionsEnabled="false"/>
+ />
<ImageView android:id="@+id/switch_ime_button"
android:layout_width="wrap_content"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8db6b4f..7bb5e06 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -881,10 +881,6 @@
Default value is false. EditText content is always selectable. -->
<attr name="textIsSelectable" format="boolean" />
- <!-- When true, IME suggestions will be displayed when the user double taps on editable text.
- The default value is true. -->
- <attr name="suggestionsEnabled" format="boolean" />
-
<!-- Where to ellipsize text. -->
<attr name="ellipsize">
<enum name="none" value="0" />
@@ -3148,8 +3144,6 @@
<!-- Indicates that the content of a non-editable text can be selected. -->
<attr name="textIsSelectable" />
- <!-- Suggestions will be displayed when the user double taps on editable text. -->
- <attr name="suggestionsEnabled" />
<!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
<attr name="textAllCaps" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a6bf1e0..b9d05fd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1717,8 +1717,6 @@
<public type="attr" name="textSuggestionsWindowStyle" />
<public type="attr" name="textEditSuggestionItemLayout" />
- <public type="attr" name="suggestionsEnabled" />
-
<public type="attr" name="rowCount" />
<public type="attr" name="rowOrderPreserved" />
<public type="attr" name="columnCount" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c80923d..e31a215 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2470,7 +2470,7 @@
<string name="paste">Paste</string>
<!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
- <string name="replace">Replace</string>
+ <string name="replace">Replace\u2026</string>
<!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
<string name="copyUrl">Copy URL</string>