diff options
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); + } +} |