summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chips/res/values/dimen.xml1
-rw-r--r--chips/src/com/android/ex/chips/BaseRecipientAdapter.java6
-rw-r--r--chips/src/com/android/ex/chips/InvisibleRecipientChip.java135
-rw-r--r--chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java6
-rw-r--r--chips/src/com/android/ex/chips/RecipientChip.java90
-rw-r--r--chips/src/com/android/ex/chips/RecipientEditTextView.java165
-rw-r--r--chips/src/com/android/ex/chips/RecipientEntry.java96
-rw-r--r--chips/src/com/android/ex/chips/VisibleRecipientChip.java124
8 files changed, 435 insertions, 188 deletions
diff --git a/chips/res/values/dimen.xml b/chips/res/values/dimen.xml
index b93555f..98354d2 100644
--- a/chips/res/values/dimen.xml
+++ b/chips/res/values/dimen.xml
@@ -19,4 +19,5 @@
<dimen name="chip_height">32dip</dimen>
<dimen name="chip_text_size">14sp</dimen>
<dimen name="line_spacing_extra">4dip</dimen>
+ <integer name="chips_max_lines">-1</integer>
</resources> \ No newline at end of file
diff --git a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java
index c0cfa19..53f3625 100644
--- a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java
+++ b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java
@@ -646,7 +646,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter
entry.displayName,
entry.displayNameSource,
entry.destination, entry.destinationType, entry.destinationLabel,
- entry.contactId, entry.dataId, entry.thumbnailUriString));
+ entry.contactId, entry.dataId, entry.thumbnailUriString, true));
} else if (entryMap.containsKey(entry.contactId)) {
// We already have a section for the person.
final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
@@ -654,14 +654,14 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter
entry.displayName,
entry.displayNameSource,
entry.destination, entry.destinationType, entry.destinationLabel,
- entry.contactId, entry.dataId, entry.thumbnailUriString));
+ entry.contactId, entry.dataId, entry.thumbnailUriString, true));
} else {
final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
entryList.add(RecipientEntry.constructTopLevelEntry(
entry.displayName,
entry.displayNameSource,
entry.destination, entry.destinationType, entry.destinationLabel,
- entry.contactId, entry.dataId, entry.thumbnailUriString));
+ entry.contactId, entry.dataId, entry.thumbnailUriString, true));
entryMap.put(entry.contactId, entryList);
}
}
diff --git a/chips/src/com/android/ex/chips/InvisibleRecipientChip.java b/chips/src/com/android/ex/chips/InvisibleRecipientChip.java
new file mode 100644
index 0000000..46fe19a
--- /dev/null
+++ b/chips/src/com/android/ex/chips/InvisibleRecipientChip.java
@@ -0,0 +1,135 @@
+/*
+ * 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 com.android.ex.chips;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Rect;
+import android.text.TextUtils;
+import android.text.style.ReplacementSpan;
+
+/**
+ * RecipientChip defines a span that contains information relevant to a
+ * particular recipient.
+ */
+/* package */class InvisibleRecipientChip extends ReplacementSpan implements RecipientChip {
+ private final CharSequence mDisplay;
+
+ private final CharSequence mValue;
+
+ private final long mContactId;
+
+ private final long mDataId;
+
+ private RecipientEntry mEntry;
+
+ private boolean mSelected = false;
+
+ private CharSequence mOriginalText;
+
+ public InvisibleRecipientChip(RecipientEntry entry, int offset) {
+ super();
+ mDisplay = entry.getDisplayName();
+ mValue = entry.getDestination().trim();
+ mContactId = entry.getContactId();
+ mDataId = entry.getDataId();
+ mEntry = entry;
+ }
+
+ /**
+ * Set the selected state of the chip.
+ * @param selected
+ */
+ public void setSelected(boolean selected) {
+ mSelected = selected;
+ }
+
+ /**
+ * Return true if the chip is selected.
+ */
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ /**
+ * Get the text displayed in the chip.
+ */
+ public CharSequence getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Get the text value this chip represents.
+ */
+ public CharSequence getValue() {
+ return mValue;
+ }
+
+ /**
+ * Get the id of the contact associated with this chip.
+ */
+ public long getContactId() {
+ return mContactId;
+ }
+
+ /**
+ * Get the id of the data associated with this chip.
+ */
+ public long getDataId() {
+ return mDataId;
+ }
+
+ /**
+ * Get associated RecipientEntry.
+ */
+ public RecipientEntry getEntry() {
+ return mEntry;
+ }
+
+ public void setOriginalText(String text) {
+ if (!TextUtils.isEmpty(text)) {
+ text = text.trim();
+ }
+ mOriginalText = text;
+ }
+
+ public CharSequence getOriginalText() {
+ return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination();
+ }
+
+ @Override
+ public void draw(Canvas arg0, CharSequence arg1, int arg2, int arg3, float arg4, int arg5,
+ int arg6, int arg7, Paint arg8) {
+ // Do nothing.
+ }
+
+ @Override
+ public int getSize(Paint arg0, CharSequence arg1, int arg2, int arg3, FontMetricsInt arg4) {
+ return 0;
+ }
+
+ @Override
+ public Rect getBounds() {
+ return new Rect(0, 0, 0, 0);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // do nothing.
+ }
+}
diff --git a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java
index 553890e..b54d83d 100644
--- a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java
+++ b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java
@@ -114,7 +114,8 @@ public class RecipientAlternatesAdapter extends CursorAdapter {
c.getString(Queries.Query.DESTINATION_LABEL),
c.getLong(Queries.Query.CONTACT_ID),
c.getLong(Queries.Query.DATA_ID),
- c.getString(Queries.Query.PHOTO_THUMBNAIL_URI)));
+ c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
+ true));
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Received reverse look up information for " + address
+ " RESULTS: "
@@ -233,7 +234,8 @@ public class RecipientAlternatesAdapter extends CursorAdapter {
c.getString(Queries.Query.DESTINATION_LABEL),
c.getLong(Queries.Query.CONTACT_ID),
c.getLong(Queries.Query.DATA_ID),
- c.getString(Queries.Query.PHOTO_THUMBNAIL_URI));
+ c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
+ true);
}
@Override
diff --git a/chips/src/com/android/ex/chips/RecipientChip.java b/chips/src/com/android/ex/chips/RecipientChip.java
index 0f93a0d..c7745c2 100644
--- a/chips/src/com/android/ex/chips/RecipientChip.java
+++ b/chips/src/com/android/ex/chips/RecipientChip.java
@@ -16,97 +16,69 @@
package com.android.ex.chips;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
+import android.graphics.Canvas;
+import android.graphics.Rect;
/**
- * RecipientChip defines an ImageSpan that contains information relevant to a
+ * RecipientChip defines a drawable object that contains information relevant to a
* particular recipient.
*/
-/* package */class RecipientChip extends ImageSpan {
- private final CharSequence mDisplay;
-
- private final CharSequence mValue;
-
- private final long mContactId;
-
- private final long mDataId;
-
- private RecipientEntry mEntry;
-
- private boolean mSelected = false;
-
- private CharSequence mOriginalText;
-
- public RecipientChip(Drawable drawable, RecipientEntry entry, int offset) {
- super(drawable, DynamicDrawableSpan.ALIGN_BOTTOM);
- mDisplay = entry.getDisplayName();
- mValue = entry.getDestination().trim();
- mContactId = entry.getContactId();
- mDataId = entry.getDataId();
- mEntry = entry;
- }
+/* package */interface RecipientChip {
/**
* Set the selected state of the chip.
* @param selected
*/
- public void setSelected(boolean selected) {
- mSelected = selected;
- }
-
+ public void setSelected(boolean selected);
/**
* Return true if the chip is selected.
*/
- public boolean isSelected() {
- return mSelected;
- }
+ public boolean isSelected();
/**
* Get the text displayed in the chip.
*/
- public CharSequence getDisplay() {
- return mDisplay;
- }
+ public CharSequence getDisplay();
/**
* Get the text value this chip represents.
*/
- public CharSequence getValue() {
- return mValue;
- }
+ public CharSequence getValue();
/**
* Get the id of the contact associated with this chip.
*/
- public long getContactId() {
- return mContactId;
- }
+ public long getContactId();
/**
* Get the id of the data associated with this chip.
*/
- public long getDataId() {
- return mDataId;
- }
+ public long getDataId();
/**
* Get associated RecipientEntry.
*/
- public RecipientEntry getEntry() {
- return mEntry;
- }
+ public RecipientEntry getEntry();
+
+ /**
+ * Set the text in the edittextview originally associated with this chip
+ * before any reverse lookups.
+ */
+ public void setOriginalText(String text);
- public void setOriginalText(String text) {
- if (!TextUtils.isEmpty(text)) {
- text = text.trim();
- }
- mOriginalText = text;
- }
+ /**
+ * Set the text in the edittextview originally associated with this chip
+ * before any reverse lookups.
+ */
+ public CharSequence getOriginalText();
- public CharSequence getOriginalText() {
- return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination();
- }
+ /**
+ * Get the bounds of the chip; may be 0,0 if it is not visibly rendered.
+ */
+ public Rect getBounds();
+
+ /**
+ * Draw the chip.
+ */
+ public void draw(Canvas canvas);
}
diff --git a/chips/src/com/android/ex/chips/RecipientEditTextView.java b/chips/src/com/android/ex/chips/RecipientEditTextView.java
index 564bfe1..29787ab 100644
--- a/chips/src/com/android/ex/chips/RecipientEditTextView.java
+++ b/chips/src/com/android/ex/chips/RecipientEditTextView.java
@@ -43,7 +43,6 @@ import android.text.InputType;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
@@ -203,7 +202,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
+ "(1?[ ]*\\([0-9]+\\)[\\- \\.]*)?" // 1(<digits>)<sdd>*
+ "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
-
private final Runnable mAddTextWatcher = new Runnable() {
@Override
public void run() {
@@ -275,7 +273,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
addTextChangedListener(mTextWatcher);
mGestureDetector = new GestureDetector(context, this);
setOnEditorActionListener(this);
- mMaxLines = getLineCount();
}
@Override
@@ -601,9 +598,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
* Get the background drawable for a RecipientChip.
*/
// Visible for testing.
- /*package*/ Drawable getChipBackground(RecipientEntry contact) {
- return (mValidator != null && mValidator.isValid(contact.getDestination())) ?
- mChipBackground : mInvalidChipBackground;
+ /* package */Drawable getChipBackground(RecipientEntry contact) {
+ return contact.isValid() ? mChipBackground : mInvalidChipBackground;
}
private float getTextYOffset(String text, TextPaint paint, int height) {
@@ -636,7 +632,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
// Pass the full text, un-ellipsized, to the chip.
Drawable result = new BitmapDrawable(getResources(), tmpBitmap);
result.setBounds(0, 0, tmpBitmap.getWidth(), tmpBitmap.getHeight());
- RecipientChip recipientChip = new RecipientChip(result, contact, offset);
+ RecipientChip recipientChip = new VisibleRecipientChip(result, contact, offset);
// Return text to the original size.
paint.setTextSize(defaultSize);
paint.setColor(defaultColor);
@@ -710,7 +706,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
if (mInvalidChipBackground == null) {
mInvalidChipBackground = r.getDrawable(R.drawable.chip_background_invalid);
}
- mLineSpacingExtra = context.getResources().getDimension(R.dimen.line_spacing_extra);
+ mLineSpacingExtra = r.getDimension(R.dimen.line_spacing_extra);
+ mMaxLines = r.getInteger(R.integer.chips_max_lines);
a.recycle();
}
@@ -775,7 +772,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
if (chips != null) {
Rect bounds;
for (RecipientChip chip : chips) {
- bounds = chip.getDrawable().getBounds();
+ bounds = chip.getBounds();
if (getWidth() > 0 && bounds.right - bounds.left > getWidth()) {
// Need to redraw that chip.
replaceChip(chip, chip.getEntry());
@@ -811,7 +808,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
&& editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) {
tokenEnd++;
}
- createReplacementChip(tokenStart, tokenEnd, editable);
+ createReplacementChip(tokenStart, tokenEnd, editable, i < CHIP_LIMIT
+ || !mShouldShrink);
}
mPendingChipsCount--;
}
@@ -866,16 +864,15 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
}
// Find the last chip; eliminate any commit characters after it.
RecipientChip[] chips = getSortedRecipients();
+ Spannable spannable = getSpannable();
if (chips != null && chips.length > 0) {
int end;
- ImageSpan lastSpan;
mMoreChip = getMoreChip();
if (mMoreChip != null) {
- lastSpan = mMoreChip;
+ end = spannable.getSpanEnd(mMoreChip);
} else {
- lastSpan = getLastChip();
+ end = getSpannable().getSpanEnd(getLastChip());
}
- end = getSpannable().getSpanEnd(lastSpan);
Editable editable = getText();
int length = editable.length();
if (length > end) {
@@ -893,7 +890,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
* Create a chip that represents just the email address of a recipient. At some later
* point, this chip will be attached to a real contact entry, if one exists.
*/
- private void createReplacementChip(int tokenStart, int tokenEnd, Editable editable) {
+ private void createReplacementChip(int tokenStart, int tokenEnd, Editable editable,
+ boolean visible) {
if (alreadyHasChip(tokenStart, tokenEnd)) {
// There is already a chip present at this location.
// Don't recreate it.
@@ -908,19 +906,19 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
if (entry != null) {
String destText = createAddressText(entry);
SpannableString chipText = new SpannableString(destText);
- int end = getSelectionEnd();
- int start = mTokenizer != null ? mTokenizer.findTokenStart(getText(), end) : 0;
RecipientChip chip = null;
try {
if (!mNoChips) {
- /* leave space for the contact icon if this is not just an email address */
- chip = constructChipSpan(
- entry,
- start,
- false,
- TextUtils.isEmpty(entry.getDisplayName())
- || TextUtils.equals(entry.getDisplayName(),
- entry.getDestination()));
+ /*
+ * leave space for the contact icon if this is not just an
+ * email address
+ */
+ boolean leaveSpace = TextUtils.isEmpty(entry.getDisplayName())
+ || TextUtils.equals(entry.getDisplayName(),
+ entry.getDestination());
+ chip = visible ?
+ constructChipSpan(entry, tokenStart, false, leaveSpace)
+ : new InvisibleRecipientChip(entry, commitCharIndex);
}
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage(), e);
@@ -954,58 +952,55 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
return null;
}
if (isPhoneQuery() && isPhoneNumber(token)) {
- return RecipientEntry
- .constructFakeEntry(token);
+ return RecipientEntry.constructFakeEntry(token, true);
}
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(token);
String display = null;
- if (isValid(token) && tokens != null && tokens.length > 0) {
+ boolean isValid = isValid(token);
+ if (isValid && tokens != null && tokens.length > 0) {
// If we can get a name from tokenizing, then generate an entry from
// this.
display = tokens[0].getName();
if (!TextUtils.isEmpty(display)) {
- if (!isPhoneQuery()) {
- if (!TextUtils.isEmpty(token)) {
- token = token.trim();
- }
- char charAt = token.charAt(token.length() - 1);
- if (charAt == COMMIT_CHAR_COMMA || charAt == COMMIT_CHAR_SEMICOLON) {
- token = token.substring(0, token.length() - 1);
- }
- }
- return RecipientEntry.constructGeneratedEntry(display, token);
+ return RecipientEntry.constructGeneratedEntry(display, tokens[0].getAddress(),
+ isValid);
} else {
display = tokens[0].getAddress();
if (!TextUtils.isEmpty(display)) {
- return RecipientEntry.constructFakeEntry(display);
+ return RecipientEntry.constructFakeEntry(display, isValid);
}
}
}
// Unable to validate the token or to create a valid token from it.
// Just create a chip the user can edit.
String validatedToken = null;
- if (mValidator != null && !mValidator.isValid(token)) {
+ if (mValidator != null && !isValid) {
// Try fixing up the entry using the validator.
validatedToken = mValidator.fixText(token).toString();
if (!TextUtils.isEmpty(validatedToken)) {
if (validatedToken.contains(token)) {
- // protect against the case of a validator with a null domain,
+ // protect against the case of a validator with a null
+ // domain,
// which doesn't add a domain to the token
Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(validatedToken);
if (tokenized.length > 0) {
validatedToken = tokenized[0].getAddress();
+ isValid = true;
}
} else {
- // We ran into a case where the token was invalid and removed
- // by the validator. In this case, just use the original token
+ // We ran into a case where the token was invalid and
+ // removed
+ // by the validator. In this case, just use the original
+ // token
// and let the user sort out the error chip.
validatedToken = null;
+ isValid = false;
}
}
}
// Otherwise, fallback to just creating an editable email address chip.
- return RecipientEntry
- .constructFakeEntry(!TextUtils.isEmpty(validatedToken) ? validatedToken : token);
+ return RecipientEntry.constructFakeEntry(
+ !TextUtils.isEmpty(validatedToken) ? validatedToken : token, isValid);
}
private boolean isValid(String text) {
@@ -1249,7 +1244,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
setSelection(end);
String text = getText().toString().substring(start, end);
if (!TextUtils.isEmpty(text)) {
- RecipientEntry entry = RecipientEntry.constructFakeEntry(text);
+ RecipientEntry entry = RecipientEntry.constructFakeEntry(text, isValid(text));
QwertyKeyListener.markAsReplaced(editable, start, end, "");
CharSequence chipText = createChip(entry, false);
int selEnd = getSelectionEnd();
@@ -1535,14 +1530,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
display = null;
}
- if (address != null && !(isPhoneQuery() && isPhoneNumber(address))) {
- // Tokenize out the address in case the address already
- // contained the username as well.
- Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address);
- if (tokenized != null && tokenized.length > 0) {
- address = tokenized[0].getAddress();
- }
- }
if (!TextUtils.isEmpty(display)) {
return display;
} else if (!TextUtils.isEmpty(address)){
@@ -1618,12 +1605,12 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
String destination = item.getDestination();
if (!isPhoneQuery() && item.getContactId() == RecipientEntry.GENERATED_CONTACT) {
entry = RecipientEntry.constructGeneratedEntry(item.getDisplayName(),
- destination);
+ destination, item.isValid());
} else if (RecipientEntry.isCreatedRecipient(item.getContactId())
&& (TextUtils.isEmpty(item.getDisplayName())
|| TextUtils.equals(item.getDisplayName(), destination)
|| (mValidator != null && !mValidator.isValid(destination)))) {
- entry = RecipientEntry.constructFakeEntry(destination);
+ entry = RecipientEntry.constructFakeEntry(destination, item.isValid());
} else {
entry = item;
}
@@ -1888,10 +1875,17 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
if (shouldShowEditableText(currentChip)) {
CharSequence text = currentChip.getValue();
Editable editable = getText();
- getSpannable().removeSpan(currentChip);
+ Spannable spannable = getSpannable();
+ int spanStart = spannable.getSpanStart(currentChip);
+ int spanEnd = spannable.getSpanEnd(currentChip);
+ spannable.removeSpan(currentChip);
+ editable.delete(spanStart, spanEnd);
setCursorVisible(true);
setSelection(editable.length());
- return new RecipientChip(null, RecipientEntry.constructFakeEntry((String) text), -1);
+ editable.append(text);
+ return constructChipSpan(
+ RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())),
+ getSelectionStart(), true, false);
} else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT) {
int start = getChipStart(currentChip);
int end = getChipEnd(currentChip);
@@ -2142,16 +2136,23 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
return;
}
// If the user is editing a chip, don't clear it.
- if (mSelectedChip != null
- && shouldShowEditableText(mSelectedChip)) {
- setCursorVisible(true);
- setSelection(getText().length());
- clearSelectedChip();
+ if (mSelectedChip != null) {
+ if (!isGeneratedContact(mSelectedChip)) {
+ setCursorVisible(true);
+ setSelection(getText().length());
+ clearSelectedChip();
+ } else {
+ return;
+ }
}
int length = s.length();
// Make sure there is content there to parse and that it is
// not just the commit character.
if (length > 1) {
+ if (lastCharacterIsCommitCharacter(s)) {
+ commitByCharacter();
+ return;
+ }
char last;
int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
int len = length() - 1;
@@ -2160,9 +2161,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
} else {
last = s.charAt(len);
}
- if (last == COMMIT_CHAR_SEMICOLON || last == COMMIT_CHAR_COMMA) {
- commitByCharacter();
- } else if (last == COMMIT_CHAR_SPACE) {
+ if (last == COMMIT_CHAR_SPACE) {
if (!isPhoneQuery()) {
// Check if this is a valid email address. If it is,
// commit it.
@@ -2181,9 +2180,10 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- // This is a delete; check to see if the insertion point is on a space
+ // The user deleted some text OR some text was replaced; check to
+ // see if the insertion point is on a space
// following a chip.
- if (before > count) {
+ if (before - count == 1) {
// If the item deleted is a space, and the thing before the
// space is a chip, delete the entire span.
int selStart = getSelectionStart();
@@ -2203,6 +2203,13 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
getSpannable().removeSpan(repl[0]);
}
} else if (count > before) {
+ if (mSelectedChip != null
+ && isGeneratedContact(mSelectedChip)) {
+ if (lastCharacterIsCommitCharacter(s)) {
+ commitByCharacter();
+ return;
+ }
+ }
scrollBottomIntoView();
}
}
@@ -2219,6 +2226,24 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
}
}
+ public boolean lastCharacterIsCommitCharacter(CharSequence s) {
+ char last;
+ int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
+ int len = length() - 1;
+ if (end != len) {
+ last = s.charAt(end);
+ } else {
+ last = s.charAt(len);
+ }
+ return last == COMMIT_CHAR_COMMA || last == COMMIT_CHAR_SEMICOLON;
+ }
+
+ public boolean isGeneratedContact(RecipientChip chip) {
+ long contactId = chip.getContactId();
+ return contactId == RecipientEntry.INVALID_CONTACT
+ || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT);
+ }
+
/**
* Handles pasting a {@link ClipData} to this {@link RecipientEditTextView}.
*/
@@ -2559,14 +2584,14 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
- Rect rect = mChip.getDrawable().getBounds();
+ Rect rect = mChip.getBounds();
shadowSize.set(rect.width(), rect.height());
shadowTouchPoint.set(rect.centerX(), rect.centerY());
}
@Override
public void onDrawShadow(Canvas canvas) {
- mChip.getDrawable().draw(canvas);
+ mChip.draw(canvas);
}
}
diff --git a/chips/src/com/android/ex/chips/RecipientEntry.java b/chips/src/com/android/ex/chips/RecipientEntry.java
index 0448229..98a35e6 100644
--- a/chips/src/com/android/ex/chips/RecipientEntry.java
+++ b/chips/src/com/android/ex/chips/RecipientEntry.java
@@ -65,29 +65,16 @@ public class RecipientEntry {
private final Uri mPhotoThumbnailUri;
+ private boolean mIsValid;
/**
* This can be updated after this object being constructed, when the photo is fetched
* from remote directories.
*/
private byte[] mPhotoBytes;
- private RecipientEntry(int entryType) {
- mEntryType = entryType;
- mDisplayName = null;
- mDestination = null;
- mDestinationType = INVALID_DESTINATION_TYPE;
- mDestinationLabel = null;
- mContactId = -1;
- mDataId = -1;
- mPhotoThumbnailUri = null;
- mPhotoBytes = null;
- mIsDivider = true;
- }
-
- private RecipientEntry(
- int entryType, String displayName,
- String destination, int destinationType, String destinationLabel,
- long contactId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel) {
+ private RecipientEntry(int entryType, String displayName, String destination,
+ int destinationType, String destinationLabel, long contactId, long dataId,
+ Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid) {
mEntryType = entryType;
mIsFirstLevel = isFirstLevel;
mDisplayName = displayName;
@@ -99,6 +86,11 @@ public class RecipientEntry {
mPhotoThumbnailUri = photoThumbnailUri;
mPhotoBytes = null;
mIsDivider = false;
+ mIsValid = isValid;
+ }
+
+ public boolean isValid() {
+ return mIsValid;
}
/**
@@ -114,10 +106,10 @@ public class RecipientEntry {
* This address has not been resolved to a contact and therefore does not
* have a contact id or photo.
*/
- public static RecipientEntry constructFakeEntry(String address) {
+ public static RecipientEntry constructFakeEntry(String address, boolean isValid) {
return new RecipientEntry(ENTRY_TYPE_PERSON, address, address,
INVALID_DESTINATION_TYPE, null,
- INVALID_CONTACT, INVALID_CONTACT, null, true);
+ INVALID_CONTACT, INVALID_CONTACT, null, true, isValid);
}
/**
@@ -136,41 +128,37 @@ public class RecipientEntry {
* with both an associated display name. This address has not been resolved
* to a contact and therefore does not have a contact id or photo.
*/
- public static RecipientEntry constructGeneratedEntry(String display, String address) {
- return new RecipientEntry(ENTRY_TYPE_PERSON, display,
- address, INVALID_DESTINATION_TYPE, null,
- GENERATED_CONTACT, GENERATED_CONTACT, null, true);
- }
-
- public static RecipientEntry constructTopLevelEntry(
- String displayName, int displayNameSource, String destination, int destinationType,
- String destinationLabel, long contactId, long dataId, Uri photoThumbnailUri) {
- return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName,
- destination),
- destination, destinationType, destinationLabel,
- contactId, dataId,
- photoThumbnailUri, true);
- }
-
- public static RecipientEntry constructTopLevelEntry(
- String displayName, int displayNameSource, String destination, int destinationType,
- String destinationLabel, long contactId, long dataId,
- String thumbnailUriAsString) {
- return new RecipientEntry(
- ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination),
- destination, destinationType, destinationLabel,
- contactId, dataId,
- (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) : null), true);
- }
-
- public static RecipientEntry constructSecondLevelEntry(
- String displayName, int displayNameSource, String destination, int destinationType,
- String destinationLabel, long contactId, long dataId, String thumbnailUriAsString) {
- return new RecipientEntry(
- ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination),
- destination, destinationType, destinationLabel,
- contactId, dataId,
- (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) : null), false);
+ public static RecipientEntry constructGeneratedEntry(String display, String address,
+ boolean isValid) {
+ return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE,
+ null, GENERATED_CONTACT, GENERATED_CONTACT, null, true, isValid);
+ }
+
+ public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
+ String destination, int destinationType, String destinationLabel, long contactId,
+ long dataId, Uri photoThumbnailUri, boolean isValid) {
+ return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
+ displayName, destination), destination, destinationType, destinationLabel,
+ contactId, dataId, photoThumbnailUri, true, isValid);
+ }
+
+ public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
+ String destination, int destinationType, String destinationLabel, long contactId,
+ long dataId, String thumbnailUriAsString, boolean isValid) {
+ return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
+ displayName, destination), destination, destinationType, destinationLabel,
+ contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString)
+ : null), true, isValid);
+ }
+
+ public static RecipientEntry constructSecondLevelEntry(String displayName,
+ int displayNameSource, String destination, int destinationType,
+ String destinationLabel, long contactId, long dataId, String thumbnailUriAsString,
+ boolean isValid) {
+ return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
+ displayName, destination), destination, destinationType, destinationLabel,
+ contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString)
+ : null), false, isValid);
}
public int getEntryType() {
diff --git a/chips/src/com/android/ex/chips/VisibleRecipientChip.java b/chips/src/com/android/ex/chips/VisibleRecipientChip.java
new file mode 100644
index 0000000..257e3f4
--- /dev/null
+++ b/chips/src/com/android/ex/chips/VisibleRecipientChip.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.android.ex.chips;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
+
+/**
+ * VisibleRecipientChip defines an ImageSpan that contains information relevant to a
+ * particular recipient and renders a background asset to go with it.
+ */
+/* package */class VisibleRecipientChip extends ImageSpan implements RecipientChip {
+ private final CharSequence mDisplay;
+
+ private final CharSequence mValue;
+
+ private final long mContactId;
+
+ private final long mDataId;
+
+ private RecipientEntry mEntry;
+
+ private boolean mSelected = false;
+
+ private CharSequence mOriginalText;
+
+ public VisibleRecipientChip(Drawable drawable, RecipientEntry entry, int offset) {
+ super(drawable, DynamicDrawableSpan.ALIGN_BOTTOM);
+ mDisplay = entry.getDisplayName();
+ mValue = entry.getDestination().trim();
+ mContactId = entry.getContactId();
+ mDataId = entry.getDataId();
+ mEntry = entry;
+ }
+
+ /**
+ * Set the selected state of the chip.
+ * @param selected
+ */
+ public void setSelected(boolean selected) {
+ mSelected = selected;
+ }
+
+ /**
+ * Return true if the chip is selected.
+ */
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ /**
+ * Get the text displayed in the chip.
+ */
+ public CharSequence getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Get the text value this chip represents.
+ */
+ public CharSequence getValue() {
+ return mValue;
+ }
+
+ /**
+ * Get the id of the contact associated with this chip.
+ */
+ public long getContactId() {
+ return mContactId;
+ }
+
+ /**
+ * Get the id of the data associated with this chip.
+ */
+ public long getDataId() {
+ return mDataId;
+ }
+
+ /**
+ * Get associated RecipientEntry.
+ */
+ public RecipientEntry getEntry() {
+ return mEntry;
+ }
+
+ public void setOriginalText(String text) {
+ if (!TextUtils.isEmpty(text)) {
+ text = text.trim();
+ }
+ mOriginalText = text;
+ }
+
+ public CharSequence getOriginalText() {
+ return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination();
+ }
+
+ @Override
+ public Rect getBounds() {
+ return getDrawable().getBounds();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ getDrawable().draw(canvas);
+ }
+}