Example usage for android.text SpannableStringBuilder toString

List of usage examples for android.text SpannableStringBuilder toString

Introduction

In this page you can find the example usage for android.text SpannableStringBuilder toString.

Prototype

@Override
public String toString() 

Source Link

Document

Return a String containing a copy of the chars in this buffer.

Usage

From source file:com.tct.mail.utils.NotificationUtils.java

/**
 * Configure the notification for one conversation.  When there are multiple conversations,
 * this method is used to configure bundled notification for Android Wear.
 *///  w w  w .ja v  a  2  s.  c o m
private static ConfigResult configureNotifForOneConversation(Context context, Account account,
        FolderPreferences folderPreferences, NotificationCompat.Builder notification,
        NotificationCompat.WearableExtender wearExtender, Cursor conversationCursor, Intent notificationIntent,
        Folder folder, long when, Resources res, String notificationAccountDisplayName,
        String notificationAccountEmail, boolean isInbox, String notificationLabelName, int notificationId,
        final ContactPhotoFetcher photoFetcher) {

    final ConfigResult result = new ConfigResult();

    final Conversation conversation = new Conversation(conversationCursor);

    Cursor cursor = null;
    MessageCursor messageCursor = null;
    boolean multipleUnseenThread = false;
    String from = null;
    try {
        final Uri uri = conversation.messageListUri.buildUpon()
                .appendQueryParameter(UIProvider.LABEL_QUERY_PARAMETER, folder.persistentId).build();
        cursor = context.getContentResolver().query(uri, UIProvider.MESSAGE_PROJECTION, null, null, null);
        messageCursor = new MessageCursor(cursor);
        // Use the information from the last sender in the conversation that triggered
        // this notification.

        String fromAddress = "";
        if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) {
            final Message message = messageCursor.getMessage();
            fromAddress = message.getFrom();
            if (fromAddress == null) {
                // No sender. Go back to default value.
                LogUtils.e(LOG_TAG, "No sender found for message: %d", message.getId());
                fromAddress = "";
            }
            from = getDisplayableSender(fromAddress);
            result.contactIconInfo = getContactIcon(context, account.getAccountManagerAccount().name, from,
                    getSenderAddress(fromAddress), folder, photoFetcher);
            notification.setLargeIcon(result.contactIconInfo.icon);
        }

        // Assume that the last message in this conversation is unread
        int firstUnseenMessagePos = messageCursor.getPosition();
        while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) {
            final Message message = messageCursor.getMessage();
            final boolean unseen = !message.seen;
            if (unseen) {
                firstUnseenMessagePos = messageCursor.getPosition();
                if (!multipleUnseenThread && !fromAddress.contentEquals(message.getFrom())) {
                    multipleUnseenThread = true;
                }
            }
        }

        final String subject = ConversationItemView.filterTag(context, conversation.subject);

        // TODO(skennedy) Can we remove this check?
        if (Utils.isRunningJellybeanOrLater()) {
            // For a new-style notification

            if (multipleUnseenThread) {
                // The title of a single conversation is the list of senders.
                int sendersLength = res.getInteger(R.integer.swipe_senders_length);

                final SpannableStringBuilder sendersBuilder = getStyledSenders(context, conversationCursor,
                        sendersLength, notificationAccountEmail);

                from = sendersBuilder.toString();
                notification.setContentText(sendersBuilder);
                // For a single new conversation, the ticker is based on the sender's name.
                result.notificationTicker = sendersBuilder.toString();
            } else {
                from = getWrappedFromString(from);
                // The title of a single message the sender.
                notification.setContentText(from);
                // For a single new conversation, the ticker is based on the sender's name.
                result.notificationTicker = from;
            }

            // The notification content will be the subject of the conversation.

            /*TS: linzhou 2016-01-06 EMAIL 1271037 DEL_S*/
            notification.setContentTitle(getSingleMessageLittleText(context, subject));
            /*TS: linzhou 2016-01-06 EMAIL 1271037 DEL_S*/

            // The notification subtext will be the subject of the conversation for inbox
            // notifications, or will based on the the label name for user label
            // notifications.
            notification.setSubText(isInbox ? notificationAccountDisplayName : notificationLabelName);

            if (multipleUnseenThread) {
                notification.setLargeIcon(getDefaultNotificationIcon(context, folder, true));
            }
            final NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle(notification);

            // Seek the message cursor to the first unread message
            final Message message;
            if (messageCursor.moveToPosition(firstUnseenMessagePos)) {
                message = messageCursor.getMessage();
                bigText.bigText(getSingleMessageBigText(context, message, from));
            } else {
                LogUtils.e(LOG_TAG, "Failed to load message");
                message = null;
            }

            if (message != null) {
                // TS: zhaotianyong 2015-05-1003318 EMAIL BUGFIX_1003318 MOD_S
                final Set<String> notificationActions = folderPreferences.getNotificationActions(account,
                        message);
                // TS: zhaotianyong 2015-05-1003318 EMAIL BUGFIX_1003318 MOD_E

                NotificationActionUtils.addNotificationActions(context, notificationIntent, notification,
                        wearExtender, account, conversation, message, folder, notificationId, when,
                        notificationActions);
            }
        } else {
            // For an old-style notification

            // The title of a single conversation notification is built from both the sender
            // and subject of the new message.
            notification.setContentTitle(getSingleMessageNotificationTitle(context, from, subject));

            // The notification content will be the subject of the conversation for inbox
            // notifications, or will based on the the label name for user label
            // notifications.
            notification.setContentText(isInbox ? notificationAccountDisplayName : notificationLabelName);

            // For a single new conversation, the ticker is based on the sender's name.
            result.notificationTicker = from;
        }
    } finally {
        if (messageCursor != null) {
            messageCursor.close();
        }
        if (cursor != null) {
            cursor.close();
        }
    }
    return result;
}

From source file:com.android.mail.utils.NotificationUtils.java

private static void configureLatestEventInfoFromConversation(final Context context, final Account account,
        final FolderPreferences folderPreferences, final NotificationCompat.Builder notificationBuilder,
        final NotificationCompat.WearableExtender wearableExtender,
        final Map<Integer, NotificationBuilders> msgNotifications, final int summaryNotificationId,
        final Cursor conversationCursor, final PendingIntent clickIntent, final Intent notificationIntent,
        final int unreadCount, final int unseenCount, final Folder folder, final long when,
        final ContactFetcher contactFetcher) {
    final Resources res = context.getResources();
    final boolean multipleUnseen = unseenCount > 1;

    LogUtils.i(LOG_TAG, "Showing notification with unreadCount of %d and unseenCount of %d", unreadCount,
            unseenCount);// w w  w .j a  v a 2s  .co m

    String notificationTicker = null;

    // Boolean indicating that this notification is for a non-inbox label.
    final boolean isInbox = folder.folderUri.fullUri.equals(account.settings.defaultInbox);

    // Notification label name for user label notifications.
    final String notificationLabelName = isInbox ? null : folder.name;

    if (multipleUnseen) {
        // Build the string that describes the number of new messages
        final String newMessagesString = createTitle(context, unseenCount);

        // The ticker initially start as the new messages string.
        notificationTicker = newMessagesString;

        // The title of the notification is the new messages string
        notificationBuilder.setContentTitle(newMessagesString);

        // TODO(skennedy) Can we remove this check?
        if (com.android.mail.utils.Utils.isRunningJellybeanOrLater()) {
            // For a new-style notification
            final int maxNumDigestItems = context.getResources()
                    .getInteger(R.integer.max_num_notification_digest_items);

            // The body of the notification is the account name, or the label name.
            notificationBuilder.setSubText(isInbox ? account.getDisplayName() : notificationLabelName);

            final NotificationCompat.InboxStyle digest = new NotificationCompat.InboxStyle(notificationBuilder);

            // Group by account and folder
            final String notificationGroupKey = createGroupKey(account, folder);
            // Track all senders to later tag them along with the digest notification
            final HashSet<String> senderAddressesSet = new HashSet<String>();
            notificationBuilder.setGroup(notificationGroupKey).setGroupSummary(true);

            ConfigResult firstResult = null;
            int numDigestItems = 0;
            do {
                final Conversation conversation = new Conversation(conversationCursor);

                if (!conversation.read) {
                    boolean multipleUnreadThread = false;
                    // TODO(cwren) extract this pattern into a helper

                    Cursor cursor = null;
                    MessageCursor messageCursor = null;
                    try {
                        final Uri.Builder uriBuilder = conversation.messageListUri.buildUpon();
                        uriBuilder.appendQueryParameter(UIProvider.LABEL_QUERY_PARAMETER,
                                notificationLabelName);
                        cursor = context.getContentResolver().query(uriBuilder.build(),
                                UIProvider.MESSAGE_PROJECTION, null, null, null);
                        messageCursor = new MessageCursor(cursor);

                        String from = "";
                        String fromAddress = "";
                        if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) {
                            final Message message = messageCursor.getMessage();
                            fromAddress = message.getFrom();
                            if (fromAddress == null) {
                                fromAddress = "";
                            }
                            from = getDisplayableSender(fromAddress);
                            addEmailAddressToSet(fromAddress, senderAddressesSet);
                        }
                        while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) {
                            final Message message = messageCursor.getMessage();
                            if (!message.read && !fromAddress.contentEquals(message.getFrom())) {
                                multipleUnreadThread = true;
                                addEmailAddressToSet(message.getFrom(), senderAddressesSet);
                            }
                        }
                        final SpannableStringBuilder sendersBuilder;
                        if (multipleUnreadThread) {
                            final int sendersLength = res.getInteger(R.integer.swipe_senders_length);

                            sendersBuilder = getStyledSenders(context, conversationCursor, sendersLength,
                                    account);
                        } else {
                            sendersBuilder = new SpannableStringBuilder(getWrappedFromString(from));
                        }
                        final CharSequence digestLine = getSingleMessageInboxLine(context,
                                sendersBuilder.toString(),
                                ConversationItemView.filterTag(context, conversation.subject),
                                conversation.getSnippet());
                        digest.addLine(digestLine);
                        numDigestItems++;

                        // Adding conversation notification for Wear.
                        NotificationCompat.Builder conversationNotif = new NotificationCompat.Builder(context);
                        conversationNotif.setCategory(NotificationCompat.CATEGORY_EMAIL);

                        conversationNotif.setSmallIcon(R.drawable.ic_notification_multiple_mail_24dp);

                        if (com.android.mail.utils.Utils.isRunningLOrLater()) {
                            conversationNotif
                                    .setColor(context.getResources().getColor(R.color.notification_icon_color));
                        }
                        conversationNotif.setContentText(digestLine);
                        Intent conversationNotificationIntent = createViewConversationIntent(context, account,
                                folder, conversationCursor);
                        PendingIntent conversationClickIntent = createClickPendingIntent(context,
                                conversationNotificationIntent);
                        conversationNotif.setContentIntent(conversationClickIntent);
                        conversationNotif.setAutoCancel(true);

                        // Conversations are sorted in descending order, but notification sort
                        // key is in ascending order.  Invert the order key to get the right
                        // order.  Left pad 19 zeros because it's a long.
                        String groupSortKey = String.format("%019d", (Long.MAX_VALUE - conversation.orderKey));
                        conversationNotif.setGroup(notificationGroupKey);
                        conversationNotif.setSortKey(groupSortKey);
                        conversationNotif.setWhen(conversation.dateMs);

                        int conversationNotificationId = getNotificationId(summaryNotificationId,
                                conversation.hashCode());

                        final NotificationCompat.WearableExtender conversationWearExtender = new NotificationCompat.WearableExtender();
                        final ConfigResult result = configureNotifForOneConversation(context, account,
                                folderPreferences, conversationNotif, conversationWearExtender,
                                conversationCursor, notificationIntent, folder, when, res, isInbox,
                                notificationLabelName, conversationNotificationId, contactFetcher);
                        msgNotifications.put(conversationNotificationId,
                                NotificationBuilders.of(conversationNotif, conversationWearExtender));

                        if (firstResult == null) {
                            firstResult = result;
                        }
                    } finally {
                        if (messageCursor != null) {
                            messageCursor.close();
                        }
                        if (cursor != null) {
                            cursor.close();
                        }
                    }
                }
            } while (numDigestItems <= maxNumDigestItems && conversationCursor.moveToNext());

            // Tag main digest notification with the senders
            tagNotificationsWithPeople(notificationBuilder, senderAddressesSet);

            if (firstResult != null && firstResult.contactIconInfo != null) {
                wearableExtender.setBackground(firstResult.contactIconInfo.wearableBg);
            } else {
                LogUtils.w(LOG_TAG, "First contact icon is null!");
                wearableExtender.setBackground(getDefaultWearableBg(context));
            }
        } else {
            // The body of the notification is the account name, or the label name.
            notificationBuilder.setContentText(isInbox ? account.getDisplayName() : notificationLabelName);
        }
    } else {
        // For notifications for a single new conversation, we want to get the information
        // from the conversation

        // Move the cursor to the most recent unread conversation
        seekToLatestUnreadConversation(conversationCursor);

        final ConfigResult result = configureNotifForOneConversation(context, account, folderPreferences,
                notificationBuilder, wearableExtender, conversationCursor, notificationIntent, folder, when,
                res, isInbox, notificationLabelName, summaryNotificationId, contactFetcher);
        notificationTicker = result.notificationTicker;

        if (result.contactIconInfo != null) {
            wearableExtender.setBackground(result.contactIconInfo.wearableBg);
        } else {
            wearableExtender.setBackground(getDefaultWearableBg(context));
        }
    }

    // Build the notification ticker
    if (notificationLabelName != null && notificationTicker != null) {
        // This is a per label notification, format the ticker with that information
        notificationTicker = res.getString(R.string.label_notification_ticker, notificationLabelName,
                notificationTicker);
    }

    if (notificationTicker != null) {
        // If we didn't generate a notification ticker, it will default to account name
        notificationBuilder.setTicker(notificationTicker);
    }

    // Set the number in the notification
    if (unreadCount > 1) {
        notificationBuilder.setNumber(unreadCount);
    }

    notificationBuilder.setContentIntent(clickIntent);
}

From source file:com.tct.mail.utils.NotificationUtils.java

private static void configureLatestEventInfoFromConversation(final Context context, final Account account,
        final FolderPreferences folderPreferences, final NotificationCompat.Builder notification,
        final NotificationCompat.WearableExtender wearableExtender,
        final Map<Integer, NotificationBuilders> msgNotifications, final int summaryNotificationId,
        final Cursor conversationCursor, final PendingIntent clickIntent, final Intent notificationIntent,
        final int unreadCount, final int unseenCount, final Folder folder, final long when,
        final ContactPhotoFetcher photoFetcher) {
    final Resources res = context.getResources();
    final String notificationAccountDisplayName = account.getDisplayName();
    final String notificationAccountEmail = account.getEmailAddress();
    final boolean multipleUnseen = unseenCount > 1;

    LogUtils.i(LOG_TAG, "Showing notification with unreadCount of %d and unseenCount of %d", unreadCount,
            unseenCount);//from  w w  w  . ja  va 2s.  co m

    String notificationTicker = null;

    // Boolean indicating that this notification is for a non-inbox label.
    final boolean isInbox = folder.folderUri.fullUri.equals(account.settings.defaultInbox);

    // Notification label name for user label notifications.
    final String notificationLabelName = isInbox ? null : folder.name;

    if (multipleUnseen) {
        // Build the string that describes the number of new messages
        final String newMessagesString = createTitle(context, unseenCount);

        // Use the default notification icon
        notification
                .setLargeIcon(getDefaultNotificationIcon(context, folder, true /* multiple new messages */));

        // The ticker initially start as the new messages string.
        notificationTicker = newMessagesString;

        // The title of the notification is the new messages string
        notification.setContentTitle(newMessagesString);

        // TODO(skennedy) Can we remove this check?
        if (com.tct.mail.utils.Utils.isRunningJellybeanOrLater()) {
            // For a new-style notification
            final int maxNumDigestItems = context.getResources()
                    .getInteger(R.integer.max_num_notification_digest_items);

            // The body of the notification is the account name, or the label name.
            notification.setSubText(isInbox ? notificationAccountDisplayName : notificationLabelName);

            final NotificationCompat.InboxStyle digest = new NotificationCompat.InboxStyle(notification);

            // Group by account and folder
            final String notificationGroupKey = createGroupKey(account, folder);
            notification.setGroup(notificationGroupKey).setGroupSummary(true);

            ConfigResult firstResult = null;
            int numDigestItems = 0;
            do {
                final Conversation conversation = new Conversation(conversationCursor);

                if (!conversation.read) {
                    boolean multipleUnreadThread = false;
                    // TODO(cwren) extract this pattern into a helper

                    Cursor cursor = null;
                    MessageCursor messageCursor = null;
                    try {
                        final Uri.Builder uriBuilder = conversation.messageListUri.buildUpon();
                        uriBuilder.appendQueryParameter(UIProvider.LABEL_QUERY_PARAMETER,
                                notificationLabelName);
                        cursor = context.getContentResolver().query(uriBuilder.build(),
                                UIProvider.MESSAGE_PROJECTION, null, null, null);
                        messageCursor = new MessageCursor(cursor);

                        String from = "";
                        String fromAddress = "";
                        if (messageCursor.moveToPosition(messageCursor.getCount() - 1)) {
                            final Message message = messageCursor.getMessage();
                            fromAddress = message.getFrom();
                            if (fromAddress == null) {
                                fromAddress = "";
                            }
                            from = getDisplayableSender(fromAddress);
                        }
                        while (messageCursor.moveToPosition(messageCursor.getPosition() - 1)) {
                            final Message message = messageCursor.getMessage();
                            if (!message.read && !fromAddress.contentEquals(message.getFrom())) {
                                multipleUnreadThread = true;
                                break;
                            }
                        }
                        final SpannableStringBuilder sendersBuilder;
                        if (multipleUnreadThread) {
                            final int sendersLength = res.getInteger(R.integer.swipe_senders_length);

                            sendersBuilder = getStyledSenders(context, conversationCursor, sendersLength,
                                    notificationAccountEmail);
                        } else {
                            sendersBuilder = new SpannableStringBuilder(getWrappedFromString(from));
                        }
                        //TS: zheng.zou 2015-03-20 EMAIL BUGFIX_-954980 MOD_S
                        CharSequence digestLine = getSingleMessageInboxLine(context, sendersBuilder.toString(),
                                ConversationItemView.filterTag(context, conversation.subject),
                                conversation.getSnippet());
                        if (digestLine == null) {
                            digestLine = "";
                        }
                        //TS: zheng.zou 2015-03-20 EMAIL BUGFIX_-954980 MOD_E
                        digest.addLine(digestLine);
                        numDigestItems++;

                        // Adding conversation notification for Wear.
                        NotificationCompat.Builder conversationNotif = new NotificationCompat.Builder(context);

                        // TODO(shahrk) - fix for multiple mail
                        // Check that the group's folder is assigned an icon res (one of the
                        // 4 sections). If it is, we can add the gmail badge. If not, it is
                        // accompanied by the multiple_mail_24dp icon and we don't want a badge
                        // if (folder.notificationIconResId != 0) {
                        conversationNotif.setSmallIcon(R.drawable.ic_notification_mail_24dp);

                        if (com.tct.mail.utils.Utils.isRunningLOrLater()) {
                            conversationNotif.setColor(
                                    context.getResources().getColor(R.color.notification_icon_mail_orange));
                        }
                        conversationNotif.setContentText(digestLine);
                        Intent conversationNotificationIntent = createViewConversationIntent(context, account,
                                folder, conversationCursor);
                        PendingIntent conversationClickIntent = createClickPendingIntent(context,
                                conversationNotificationIntent);
                        conversationNotif.setContentIntent(conversationClickIntent);
                        conversationNotif.setAutoCancel(true);

                        // Conversations are sorted in descending order, but notification sort
                        // key is in ascending order.  Invert the order key to get the right
                        // order.  Left pad 19 zeros because it's a long.
                        String groupSortKey = String.format("%019d", (Long.MAX_VALUE - conversation.orderKey));
                        conversationNotif.setGroup(notificationGroupKey);
                        conversationNotif.setSortKey(groupSortKey);

                        int conversationNotificationId = getNotificationId(summaryNotificationId,
                                conversation.hashCode());

                        final NotificationCompat.WearableExtender conversationWearExtender = new NotificationCompat.WearableExtender();
                        final ConfigResult result = configureNotifForOneConversation(context, account,
                                folderPreferences, conversationNotif, conversationWearExtender,
                                conversationCursor, notificationIntent, folder, when, res,
                                notificationAccountDisplayName, notificationAccountEmail, isInbox,
                                notificationLabelName, conversationNotificationId, photoFetcher);
                        // TS: chenyanhua 2015-02-11 EMAIL BUGFIX-926747 ADD_S
                        result.contactIconInfo = getContactIcon(context, folder, photoFetcher);
                        notification.setLargeIcon(result.contactIconInfo.icon);
                        // TS: chenyanhua 2015-02-11 EMAIL BUGFIX-926747 ADD_E
                        msgNotifications.put(conversationNotificationId,
                                NotificationBuilders.of(conversationNotif, conversationWearExtender));

                        if (firstResult == null) {
                            firstResult = result;
                        }
                    } finally {
                        if (messageCursor != null) {
                            messageCursor.close();
                        }
                        if (cursor != null) {
                            cursor.close();
                        }
                    }
                }
            } while (numDigestItems <= maxNumDigestItems && conversationCursor.moveToNext());

            if (firstResult != null && firstResult.contactIconInfo != null) {
                wearableExtender.setBackground(firstResult.contactIconInfo.wearableBg);
            } else {
                LogUtils.w(LOG_TAG, "First contact icon is null!");
                wearableExtender.setBackground(getDefaultWearableBg(context));
            }
        } else {
            // The body of the notification is the account name, or the label name.
            notification.setContentText(isInbox ? notificationAccountDisplayName : notificationLabelName);
        }
    } else {
        // For notifications for a single new conversation, we want to get the information
        // from the conversation

        // Move the cursor to the most recent unread conversation
        seekToLatestUnreadConversation(conversationCursor);

        final ConfigResult result = configureNotifForOneConversation(context, account, folderPreferences,
                notification, wearableExtender, conversationCursor, notificationIntent, folder, when, res,
                notificationAccountDisplayName, notificationAccountEmail, isInbox, notificationLabelName,
                summaryNotificationId, photoFetcher);
        notificationTicker = result.notificationTicker;

        if (result.contactIconInfo != null) {
            wearableExtender.setBackground(result.contactIconInfo.wearableBg);
        } else {
            wearableExtender.setBackground(getDefaultWearableBg(context));
        }
    }

    // Build the notification ticker
    if (notificationLabelName != null && notificationTicker != null) {
        // This is a per label notification, format the ticker with that information
        notificationTicker = res.getString(R.string.label_notification_ticker, notificationLabelName,
                notificationTicker);
    }

    if (notificationTicker != null) {
        // If we didn't generate a notification ticker, it will default to account name
        notification.setTicker(notificationTicker);
    }

    // Set the number in the notification
    if (unreadCount > 1) {
        notification.setNumber(unreadCount);
    }

    notification.setContentIntent(clickIntent);
}

From source file:com.android.mms.ui.MessageListItem.java

private CharSequence formatMessage(MessageItem msgItem, String body, String subject, Pattern highlight,
        String contentType) {//from ww w . j av  a2s  .  c  o m
    SpannableStringBuilder buf = new SpannableStringBuilder();

    boolean hasSubject = !TextUtils.isEmpty(subject);
    SmileyParser parser = SmileyParser.getInstance();
    if (hasSubject) {
        CharSequence smilizedSubject = parser.addSmileySpans(subject);
        // Can't use the normal getString() with extra arguments for string replacement
        // because it doesn't preserve the SpannableText returned by addSmileySpans.
        // We have to manually replace the %s with our text.
        buf.append(TextUtils.replace(mContext.getResources().getString(R.string.inline_subject),
                new String[] { "%s" }, new CharSequence[] { smilizedSubject }));
    }

    if (!TextUtils.isEmpty(body)) {
        // Converts html to spannable if ContentType is "text/html".
        if (contentType != null && ContentType.TEXT_HTML.equals(contentType)) {
            buf.append("\n");
            buf.append(Html.fromHtml(body));
        } else {
            if (hasSubject) {
                buf.append(" - ");
            }
            buf.append(parser.addSmileySpans(body));
        }
    }

    if (highlight != null) {
        Matcher m = highlight.matcher(buf.toString());
        while (m.find()) {
            buf.setSpan(new StyleSpan(Typeface.BOLD), m.start(), m.end(), 0);
        }
    }
    return buf;
}

From source file:com.android.mail.browse.ConversationItemView.java

SpannableStringBuilder elideParticipants(List<SpannableString> parts) {
    final SpannableStringBuilder builder = new SpannableStringBuilder();
    float totalWidth = 0;
    boolean ellipsize = false;
    float width;//from ww w.  j  av a  2 s  . c o  m
    boolean skipToHeader = false;

    // start with "To: " if we're showing recipients
    if (mDisplayedFolder.shouldShowRecipients() && !parts.isEmpty()) {
        final SpannableString toHeader = SendersView.getFormattedToHeader();
        CharacterStyle[] spans = toHeader.getSpans(0, toHeader.length(), CharacterStyle.class);
        // There is only 1 character style span; make sure we apply all the
        // styles to the paint object before measuring.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        totalWidth += sPaint.measureText(toHeader.toString());
        builder.append(toHeader);
        skipToHeader = true;
    }

    final SpannableStringBuilder messageInfoString = mHeader.messageInfoString;
    if (!TextUtils.isEmpty(messageInfoString)) {
        CharacterStyle[] spans = messageInfoString.getSpans(0, messageInfoString.length(),
                CharacterStyle.class);
        // There is only 1 character style span; make sure we apply all the
        // styles to the paint object before measuring.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        // Paint the message info string to see if we lose space.
        float messageInfoWidth = sPaint.measureText(messageInfoString.toString());
        totalWidth += messageInfoWidth;
    }
    SpannableString prevSender = null;
    SpannableString ellipsizedText;
    for (SpannableString sender : parts) {
        // There may be null sender strings if there were dupes we had to remove.
        if (sender == null) {
            continue;
        }
        // No more width available, we'll only show fixed fragments.
        if (ellipsize) {
            break;
        }
        CharacterStyle[] spans = sender.getSpans(0, sender.length(), CharacterStyle.class);
        // There is only 1 character style span.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        // If there are already senders present in this string, we need to
        // make sure we prepend the dividing token
        if (SendersView.sElidedString.equals(sender.toString())) {
            sender = copyStyles(spans, sElidedPaddingToken + sender + sElidedPaddingToken);
        } else if (!skipToHeader && builder.length() > 0
                && (prevSender == null || !SendersView.sElidedString.equals(prevSender.toString()))) {
            sender = copyStyles(spans, sSendersSplitToken + sender);
        } else {
            skipToHeader = false;
        }
        prevSender = sender;

        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        // Measure the width of the current sender and make sure we have space
        width = (int) sPaint.measureText(sender.toString());
        if (width + totalWidth > mSendersWidth) {
            // The text is too long, new line won't help. We have to
            // ellipsize text.
            ellipsize = true;
            width = mSendersWidth - totalWidth; // ellipsis width?
            ellipsizedText = copyStyles(spans, TextUtils.ellipsize(sender, sPaint, width, TruncateAt.END));
            width = (int) sPaint.measureText(ellipsizedText.toString());
        } else {
            ellipsizedText = null;
        }
        totalWidth += width;

        final CharSequence fragmentDisplayText;
        if (ellipsizedText != null) {
            fragmentDisplayText = ellipsizedText;
        } else {
            fragmentDisplayText = sender;
        }
        builder.append(fragmentDisplayText);
    }
    mHeader.styledMessageInfoStringOffset = builder.length();
    if (!TextUtils.isEmpty(messageInfoString)) {
        builder.append(messageInfoString);
    }
    return builder;
}

From source file:ru.valle.btc.MainActivity.java

private void showSpendPanelForKeyPair(KeyPair keyPair) {
    if (keyPair != null && keyPair.privateKey.privateKeyDecoded == null) {
        keyPair = null;//from  w  ww  .  j ava  2  s . co m
    }
    if (keyPair != null && !TextUtils.isEmpty(keyPair.address)) {
        currentKeyPair = keyPair;
        final String address = keyPair.address;
        String descStr = getString(R.string.raw_tx_description_header, address);
        SpannableStringBuilder builder = new SpannableStringBuilder(descStr);
        int spanBegin = descStr.indexOf(address);
        if (spanBegin >= 0) {
            ForegroundColorSpan addressColorSpan = new ForegroundColorSpan(
                    getColor(MainActivity.this, R.color.dark_orange));
            builder.setSpan(addressColorSpan, spanBegin, spanBegin + address.length(),
                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
        }
        rawTxDescriptionHeaderView.setText(builder);
        String wutLink = getString(R.string.raw_tx_description_wut_link);
        String jsonLink = getString(R.string.raw_tx_description_json_link);
        builder = new SpannableStringBuilder(getString(R.string.raw_tx_description, wutLink, jsonLink));

        spanBegin = builder.toString().indexOf(wutLink);
        ClickableSpan urlSpan = new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                SpannableStringBuilder builder = new SpannableStringBuilder(
                        getText(R.string.raw_tx_description_wut));
                setUrlSpanForAddress("blockexplorer.com", address, builder);
                setUrlSpanForAddress("blockchain.info", address, builder);
                TextView messageView = new TextView(MainActivity.this);
                messageView.setText(builder);
                messageView.setMovementMethod(LinkMovementMethod.getInstance());
                int padding = (int) (16 * (getResources().getDisplayMetrics().densityDpi / 160f));
                messageView.setPadding(padding, padding, padding, padding);
                new AlertDialog.Builder(MainActivity.this).setView(messageView)
                        .setPositiveButton(android.R.string.ok, null).show();
            }
        };
        builder.setSpan(urlSpan, spanBegin, spanBegin + wutLink.length(),
                SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);

        spanBegin = builder.toString().indexOf(jsonLink);
        urlSpan = new URLSpan("http://blockchain.info/unspent?active=" + address);
        builder.setSpan(urlSpan, spanBegin, spanBegin + jsonLink.length(),
                SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);

        rawTxDescriptionView.setText(builder);
        rawTxDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());
        onUnspentOutputsInfoChanged();
    }
    sendLayout.setVisibility(keyPair != null ? View.VISIBLE : View.GONE);
    enterPrivateKeyAck.setVisibility(keyPair == null ? View.VISIBLE : View.GONE);
}

From source file:com.tct.mail.browse.ConversationItemView.java

SpannableStringBuilder elideParticipants(List<SpannableString> parts) {
    final SpannableStringBuilder builder = new SpannableStringBuilder();
    float totalWidth = 0;
    boolean ellipsize = false;
    float width;/*w  w  w  . j  av a2s.c  om*/
    boolean skipToHeader = false;

    // start with "To: " if we're showing recipients
    if (mDisplayedFolder.shouldShowRecipients() && !parts.isEmpty()) {
        final SpannableString toHeader = SendersView.getFormattedToHeader();
        CharacterStyle[] spans = toHeader.getSpans(0, toHeader.length(), CharacterStyle.class);
        // There is only 1 character style span; make sure we apply all the
        // styles to the paint object before measuring.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        totalWidth += sPaint.measureText(toHeader.toString());
        builder.append(toHeader);
        skipToHeader = true;
    }

    final SpannableStringBuilder messageInfoString = mHeader.messageInfoString;
    if (messageInfoString.length() > 0) {
        CharacterStyle[] spans = messageInfoString.getSpans(0, messageInfoString.length(),
                CharacterStyle.class);
        // There is only 1 character style span; make sure we apply all the
        // styles to the paint object before measuring.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        // Paint the message info string to see if we lose space.
        float messageInfoWidth = sPaint.measureText(messageInfoString.toString());
        totalWidth += messageInfoWidth;
    }
    SpannableString prevSender = null;
    SpannableString ellipsizedText;
    for (SpannableString sender : parts) {
        // There may be null sender strings if there were dupes we had to remove.
        if (sender == null) {
            continue;
        }
        // No more width available, we'll only show fixed fragments.
        if (ellipsize) {
            break;
        }
        CharacterStyle[] spans = sender.getSpans(0, sender.length(), CharacterStyle.class);
        // There is only 1 character style span.
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        // If there are already senders present in this string, we need to
        // make sure we prepend the dividing token
        if (SendersView.sElidedString.equals(sender.toString())) {
            prevSender = sender;
            sender = copyStyles(spans, sElidedPaddingToken + sender + sElidedPaddingToken);
        } else if (!skipToHeader && builder.length() > 0
                && (prevSender == null || !SendersView.sElidedString.equals(prevSender.toString()))) {
            prevSender = sender;
            sender = copyStyles(spans, sSendersSplitToken + sender);
        } else {
            prevSender = sender;
            skipToHeader = false;
        }
        if (spans.length > 0) {
            spans[0].updateDrawState(sPaint);
        }
        //TS: yanhua.chen 2015-9-2 EMAIL CR_540046 MOD_S
        // Measure the width of the current sender and make sure we have space
        width = (int) sPaint.measureText(sender.toString());
        if (width + totalWidth > mCoordinates.sendersWidth) {
            // The text is too long, new line won't help. We have to
            // ellipsize text.
            ellipsize = true;
            width = mCoordinates.sendersWidth - totalWidth; // ellipsis width?
            ellipsizedText = copyStyles(spans, TextUtils.ellipsize(sender, sPaint, width, TruncateAt.END));
            width = (int) sPaint.measureText(ellipsizedText.toString());
        } else {
            ellipsizedText = null;
        }
        totalWidth += width;
        //TS: yanhua.chen 2015-9-2 EMAIL CR_540046 MOD_E

        //[FEATURE]-Add-BEGIN by CDTS.zhonghua.tuo,05/29/2014,FR 670064
        CharSequence fragmentDisplayText;
        if (ellipsizedText != null) {
            fragmentDisplayText = ellipsizedText;
        } else {
            fragmentDisplayText = sender;
        }
        boolean filterSender = false;
        if (mField == UIProvider.LOCAL_SEARCH_ALL || mField == UIProvider.LOCAL_SEARCH_FROM) {
            filterSender = true;
        }
        if (mQueryText != null && filterSender) {
            fragmentDisplayText = TextUtilities.highlightTermsInText(fragmentDisplayText.toString(),
                    mQueryText);
        }
        //[FEATURE]-Add-END by CDTS.zhonghua.tuo
        builder.append(fragmentDisplayText);
    }
    mHeader.styledMessageInfoStringOffset = builder.length();
    builder.append(messageInfoString);
    return builder;
}

From source file:com.tct.mail.browse.ConversationItemView.java

private void createSubject(final boolean isUnread) {
    final String badgeText = mHeader.badgeText == null ? "" : mHeader.badgeText;
    String subject = filterTag(getContext(), mHeader.conversation.subject);
    subject = Conversation.getSubjectForDisplay(mContext, badgeText, subject);

    /// TCT: add for search term highlight
    // process subject and snippet respectively @{
    SpannableStringBuilder subjectToHighlight = new SpannableStringBuilder(subject);
    boolean hasFilter = (mSearchParams != null && !TextUtils.isEmpty(mSearchParams.mFilter));
    if (hasFilter) {
        boolean fieldMatchedSubject = (mSearchParams != null
                && (SearchParams.SEARCH_FIELD_SUBJECT.equals(mSearchParams.mField)
                        || SearchParams.SEARCH_FIELD_ALL.equals(mSearchParams.mField)));
        /// TCT: Only highlight un-empty subject
        if (fieldMatchedSubject && !TextUtils.isEmpty(subject)) {
            CharSequence subjectChars = TextUtilities.highlightTermsInText(subject, mSearchParams.mFilter);
            subjectToHighlight.replace(0, subject.length(), subjectChars);
        }/*from  w w w  .  j av  a 2 s. c  o  m*/
    }
    /// @}
    final Spannable displayedStringBuilder = new SpannableString(subjectToHighlight);

    // since spans affect text metrics, add spans to the string before measure/layout or fancy
    // ellipsizing

    final int badgeTextLength = formatBadgeText(displayedStringBuilder, badgeText);

    if (!TextUtils.isEmpty(subject)) {
        displayedStringBuilder.setSpan(
                TextAppearanceSpan.wrap(isUnread ? sSubjectTextUnreadSpan : sSubjectTextReadSpan),
                badgeTextLength, subject.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    if (isActivated() && showActivatedText()) {
        displayedStringBuilder.setSpan(sActivatedTextSpan, badgeTextLength, displayedStringBuilder.length(),
                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    }

    final int subjectWidth = mSubjectWidth;//TS: yanhua.chen 2015-9-2 EMAIL CR_540046 MOD
    final int subjectHeight = mCoordinates.subjectHeight;
    mSubjectTextView.setLayoutParams(new ViewGroup.LayoutParams(subjectWidth, subjectHeight));
    mSubjectTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCoordinates.subjectFontSize);
    layoutViewExactly(mSubjectTextView, subjectWidth, subjectHeight);

    //[FEATURE]-Mod-BEGIN by CDTS.zhonghua.tuo,05/29/2014,FR 670064
    SpannableStringBuilder builder = new SpannableStringBuilder();
    boolean filterSubject = false;
    if (mField == UIProvider.LOCAL_SEARCH_ALL || mField == UIProvider.LOCAL_SEARCH_SUBJECT) {
        filterSubject = true;
    }
    if (mQueryText != null && filterSubject) {
        CharSequence formatSubject = displayedStringBuilder;
        formatSubject = TextUtilities.highlightTermsInText(subject, mQueryText);
        builder.append(formatSubject);
        mSubjectTextView.setText(builder);
        // TS: chao.zhang 2015-09-14 EMAIL FEATURE-585337 ADD_S
        //store the displayed subject for calculate the statusView's X and width
        mHeader.subjectText = builder.toString();
        // TS: chao.zhang 2015-09-14 EMAIL FEATURE-585337 ADD_E
    } else {
        mSubjectTextView.setText(displayedStringBuilder);
        // TS: chao.zhang 2015-09-14 EMAIL FEATURE-585337 ADD_S
        mHeader.subjectText = displayedStringBuilder.toString();
        // TS: chao.zhang 2015-09-14 EMAIL FEATURE-585337 ADD_E
    }
    //[FEATURE]-Mod-END by CDTS.zhonghua.tuo
}

From source file:com.tct.mail.browse.ConversationItemView.java

private void layoutParticipantText(SpannableStringBuilder participantText) {
    if (participantText != null) {
        if (isActivated() && showActivatedText()) {
            participantText.setSpan(sActivatedTextSpan, 0, mHeader.styledMessageInfoStringOffset,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else {/* ww w.  ja  v  a  2  s .c  o m*/
            participantText.removeSpan(sActivatedTextSpan);
        }

        //TS: yanhua.chen 2015-9-2 EMAIL CR_540046 MOD_S
        //Note sender width should use define in xml
        final int w = mCoordinates.sendersWidth;
        //TS: yanhua.chen 2015-9-2 EMAIL CR_540046 MOD_E
        final int h = mCoordinates.sendersHeight;
        mSendersTextView.setLayoutParams(new ViewGroup.LayoutParams(w, h));
        mSendersTextView.setMaxLines(mCoordinates.sendersLineCount);
        mSendersTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mCoordinates.sendersFontSize);
        layoutViewExactly(mSendersTextView, w, h);

        /// TCT: add for search term highlight @{
        boolean hasFilter = (mSearchParams != null && !TextUtils.isEmpty(mSearchParams.mFilter));
        boolean fieldMatched = (mSearchParams != null
                && (SearchParams.SEARCH_FIELD_FROM.equals(mSearchParams.mField)
                        || SearchParams.SEARCH_FIELD_ALL.equals(mSearchParams.mField)
                        || SearchParams.SEARCH_FIELD_TO.equals(mSearchParams.mField))); //porting from PR937141
        if (hasFilter && fieldMatched) {
            CharacterStyle[] spans = participantText.getSpans(0, participantText.length(),
                    CharacterStyle.class);
            String senderToHightlight = participantText.toString();
            CharSequence highlightedSender = TextUtilities.highlightTermsInText(senderToHightlight,
                    mSearchParams.mFilter);
            highlightedSender = copyStyles(spans, highlightedSender);
            mSendersTextView.setText(highlightedSender);
        } else {
            mSendersTextView.setText(participantText);
        }
        /// @}
    }
}

From source file:org.tlhInganHol.android.klingonassistant.EntryActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // TTS://ww w.j  a  v a 2  s.co m
    // Initialize text-to-speech. This is an asynchronous operation.
    // The OnInitListener (second argument) is called after initialization completes.
    // Log.d(TAG, "Initialising TTS");
    mTts = new TextToSpeech(this, this, // TextToSpeech.OnInitListener
            "org.tlhInganHol.android.klingonttsengine"); // Requires API 14.

    setDrawerContentView(R.layout.entry);
    Resources resources = getResources();

    JellyBeanSpanFixTextView entryTitle = (JellyBeanSpanFixTextView) findViewById(R.id.entry_title);
    JellyBeanSpanFixTextView entryText = (JellyBeanSpanFixTextView) findViewById(R.id.definition);

    // TODO: Save and restore bundle state to preserve links.

    Uri uri = getIntent().getData();
    // Log.d(TAG, "EntryActivity - uri: " + uri.toString());
    // TODO: Disable the "About" menu item if this is the "About" entry.
    mParentQuery = getIntent().getStringExtra(SearchManager.QUERY);

    // Retrieve the entry's data.
    // Note: managedQuery is deprecated since API 11.
    Cursor cursor = managedQuery(uri, KlingonContentDatabase.ALL_KEYS, null, null, null);
    KlingonContentProvider.Entry entry = new KlingonContentProvider.Entry(cursor, getBaseContext());

    // Handle alternative spellings here.
    if (entry.isAlternativeSpelling()) {
        // TODO: Immediate redirect to query in entry.getDefinition();
    }

    // Get the shared preferences.
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());

    // Set the entry's name (along with info like "slang", formatted in HTML).
    entryTitle.invalidate();
    boolean useKlingonFont = sharedPrefs.getBoolean(Preferences.KEY_KLINGON_FONT_CHECKBOX_PREFERENCE,
            /* default */false);
    Typeface klingonTypeface = KlingonAssistant.getKlingonFontTypeface(getBaseContext());
    if (useKlingonFont) {
        // Preference is set to display this in {pIqaD}!
        entryTitle.setTypeface(klingonTypeface);
        entryTitle.setText(entry.getEntryNameInKlingonFont());
    } else {
        // Boring transcription based on English (Latin) alphabet.
        entryTitle.setText(Html.fromHtml(entry.getFormattedEntryName(/* isHtml */true)));
    }
    mEntryName = entry.getEntryName();

    // Set the colour for the entry name depending on its part of speech.
    boolean useColours = sharedPrefs.getBoolean(Preferences.KEY_USE_COLOURS_CHECKBOX_PREFERENCE,
            /* default */true);
    if (useColours) {
        entryTitle.setTextColor(entry.getTextColor());
    }

    // Create the expanded definition.
    String pos = entry.getFormattedPartOfSpeech(/* isHtml */false);
    String expandedDefinition = pos + entry.getDefinition();

    // Show the German definition.
    String definition_DE = "";
    boolean displayGermanEntry = entry.shouldDisplayGerman();
    int germanDefinitionStart = -1;
    String germanDefinitionHeader = "\n" + resources.getString(R.string.label_german) + ": ";
    if (displayGermanEntry) {
        germanDefinitionStart = expandedDefinition.length();
        definition_DE = entry.getDefinition_DE();
        expandedDefinition += germanDefinitionHeader + definition_DE;
    }

    // Set the share intent.
    setShareEntryIntent(entry);

    // Show the basic notes.
    String notes = entry.getNotes();
    if (!notes.equals("")) {
        expandedDefinition += "\n\n" + notes;
    }

    // If this entry is hypothetical or extended canon, display warnings.
    if (entry.isHypothetical() || entry.isExtendedCanon()) {
        expandedDefinition += "\n\n";
        if (entry.isHypothetical()) {
            expandedDefinition += resources.getString(R.string.warning_hypothetical);
        }
        if (entry.isExtendedCanon()) {
            expandedDefinition += resources.getString(R.string.warning_extended_canon);
        }
    }

    // Show synonyms, antonyms, and related entries.
    String synonyms = entry.getSynonyms();
    String antonyms = entry.getAntonyms();
    String seeAlso = entry.getSeeAlso();
    if (!synonyms.equals("")) {
        expandedDefinition += "\n\n" + resources.getString(R.string.label_synonyms) + ": " + synonyms;
    }
    if (!antonyms.equals("")) {
        expandedDefinition += "\n\n" + resources.getString(R.string.label_antonyms) + ": " + antonyms;
    }
    if (!seeAlso.equals("")) {
        expandedDefinition += "\n\n" + resources.getString(R.string.label_see_also) + ": " + seeAlso;
    }

    // Display components if that field is not empty, unless we are showing an analysis link, in
    // which case we want to hide the components.
    boolean showAnalysis = entry.isSentence() || entry.isDerivative();
    String components = entry.getComponents();
    if (!components.equals("")) {
        // Treat the components column of inherent plurals and their
        // singulars differently than for other entries.
        if (entry.isInherentPlural()) {
            expandedDefinition += "\n\n"
                    + String.format(resources.getString(R.string.info_inherent_plural), components);
        } else if (entry.isSingularFormOfInherentPlural()) {
            expandedDefinition += "\n\n"
                    + String.format(resources.getString(R.string.info_singular_form), components);
        } else if (!showAnalysis) {
            // This is just a regular list of components.
            expandedDefinition += "\n\n" + resources.getString(R.string.label_components) + ": " + components;
        }
    }

    // Display plural information.
    if (!entry.isPlural() && !entry.isInherentPlural() && !entry.isPlural()) {
        if (entry.isBeingCapableOfLanguage()) {
            expandedDefinition += "\n\n" + resources.getString(R.string.info_being);
        } else if (entry.isBodyPart()) {
            expandedDefinition += "\n\n" + resources.getString(R.string.info_body);
        }
    }

    // If the entry is a useful phrase, link back to its category.
    if (entry.isSentence()) {
        String sentenceType = entry.getSentenceType();
        if (!sentenceType.equals("")) {
            // Put the query as a placeholder for the actual category.
            expandedDefinition += "\n\n" + resources.getString(R.string.label_category) + ": {"
                    + entry.getSentenceTypeQuery() + "}";
        }
    }

    // If the entry is a sentence, make a link to analyse its components.
    if (showAnalysis) {
        String analysisQuery = entry.getEntryName();
        if (!components.equals("")) {
            // Strip the brackets around each component so they won't be processed.
            analysisQuery += ":" + entry.getPartOfSpeech();
            int homophoneNumber = entry.getHomophoneNumber();
            if (homophoneNumber != -1) {
                analysisQuery += ":" + homophoneNumber;
            }
            analysisQuery += KlingonContentProvider.Entry.COMPONENTS_MARKER + components.replaceAll("[{}]", "");
        }
        expandedDefinition += "\n\n" + resources.getString(R.string.label_analyze) + ": {" + analysisQuery
                + "}";
    }

    // Show the examples.
    String examples = entry.getExamples();
    if (!examples.equals("")) {
        expandedDefinition += "\n\n" + resources.getString(R.string.label_examples) + ": " + examples;
    }

    // Show the source.
    String source = entry.getSource();
    if (!source.equals("")) {
        expandedDefinition += "\n\n" + resources.getString(R.string.label_sources) + ": " + source;
    }

    // If this is a verb (but not a prefix or suffix), show the transitivity information.
    String transitivity = "";
    if (entry.isVerb()
            && sharedPrefs.getBoolean(Preferences.KEY_SHOW_TRANSITIVITY_CHECKBOX_PREFERENCE, /* default */
                    true)) {
        // This is a verb and show transitivity preference is set to true.
        transitivity = entry.getTransitivityString();
    }
    int transitivityStart = -1;
    String transitivityHeader = "\n\n" + resources.getString(R.string.label_transitivity) + ": ";
    boolean showTransitivityInformation = !transitivity.equals("");
    if (showTransitivityInformation) {
        transitivityStart = expandedDefinition.length();
        expandedDefinition += transitivityHeader + transitivity;
    }

    // Show the hidden notes.
    String hiddenNotes = "";
    if (sharedPrefs.getBoolean(Preferences.KEY_SHOW_ADDITIONAL_INFORMATION_CHECKBOX_PREFERENCE, /* default */
            true)) {
        // Show additional information preference set to true.
        hiddenNotes = entry.getHiddenNotes();
    }
    int hiddenNotesStart = -1;
    String hiddenNotesHeader = "\n\n" + resources.getString(R.string.label_additional_information) + ": ";
    if (!hiddenNotes.equals("")) {
        hiddenNotesStart = expandedDefinition.length();
        expandedDefinition += hiddenNotesHeader + hiddenNotes;
    }

    // Format the expanded definition, including linkifying the links to other entries.
    float smallTextScale = (float) 0.8;
    SpannableStringBuilder ssb = new SpannableStringBuilder(expandedDefinition);
    int intermediateFlags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_INTERMEDIATE;
    int finalFlags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
    if (!pos.equals("")) {
        // Italicise the part of speech.
        ssb.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, pos.length(), finalFlags);
    }
    if (displayGermanEntry) {
        // Reduce the size of the German definition.
        ssb.setSpan(new RelativeSizeSpan(smallTextScale), germanDefinitionStart,
                germanDefinitionStart + germanDefinitionHeader.length() + definition_DE.length(), finalFlags);
    }
    if (showTransitivityInformation) {
        // Reduce the size of the transitivity information.
        ssb.setSpan(new RelativeSizeSpan(smallTextScale), transitivityStart,
                transitivityStart + transitivityHeader.length() + transitivity.length(), finalFlags);
    }
    if (!hiddenNotes.equals("")) {
        // Reduce the size of the hidden notes.
        ssb.setSpan(new RelativeSizeSpan(smallTextScale), hiddenNotesStart,
                hiddenNotesStart + hiddenNotesHeader.length() + hiddenNotes.length(), finalFlags);
    }
    Matcher m = KlingonContentProvider.Entry.ENTRY_PATTERN.matcher(expandedDefinition);
    while (m.find()) {

        // Strip the brackets {} to get the query.
        String query = expandedDefinition.substring(m.start() + 1, m.end() - 1);
        LookupClickableSpan viewLauncher = new LookupClickableSpan(query);

        // Process the linked entry information.
        KlingonContentProvider.Entry linkedEntry = new KlingonContentProvider.Entry(query, getBaseContext());
        // Log.d(TAG, "linkedEntry.getEntryName() = " + linkedEntry.getEntryName());

        // Delete the brackets and metadata parts of the string (which includes analysis components).
        ssb.delete(m.start() + 1 + linkedEntry.getEntryName().length(), m.end());
        ssb.delete(m.start(), m.start() + 1);
        int end = m.start() + linkedEntry.getEntryName().length();

        // Insert link to the category for a useful phrase.
        if (entry.isSentence() && !entry.getSentenceType().equals("")
                && linkedEntry.getEntryName().equals("*")) {
            // Delete the "*" placeholder.
            ssb.delete(m.start(), m.start() + 1);

            // Insert the category name.
            ssb.insert(m.start(), entry.getSentenceType());
            end += entry.getSentenceType().length() - 1;
        }

        // Set the font and link.
        // TODO: Source should link to description of the source.
        // This is true if this entry doesn't launch an EntryActivity.
        boolean disableEntryLink = linkedEntry.doNotLink() || linkedEntry.isSource() || linkedEntry.isURL();
        // The last span set on a range must have finalFlags.
        int maybeFinalFlags = disableEntryLink ? finalFlags : intermediateFlags;
        if (linkedEntry.isSource()) {
            // Names of sources are in italics.
            ssb.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), m.start(), end, maybeFinalFlags);
        } else if (linkedEntry.isURL()) {
            // Linkify URL if there is one.
            String url = linkedEntry.getURL();
            if (!url.equals("")) {
                ssb.setSpan(new URLSpan(url), m.start(), end, maybeFinalFlags);
            }
        } else if (useKlingonFont) {
            // Display the text using the Klingon font. Categories (which have an entry of "*") must
            // be handled specially.
            String klingonEntryName = !linkedEntry.getEntryName().equals("*")
                    ? linkedEntry.getEntryNameInKlingonFont()
                    : KlingonContentProvider.convertStringToKlingonFont(entry.getSentenceType());
            ssb.delete(m.start(), end);
            ssb.insert(m.start(), klingonEntryName);
            end = m.start() + klingonEntryName.length();
            ssb.setSpan(new KlingonTypefaceSpan("", klingonTypeface), m.start(), end, maybeFinalFlags);
        } else {
            // Klingon is in bold serif.
            ssb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), m.start(), end, intermediateFlags);
            ssb.setSpan(new TypefaceSpan("serif"), m.start(), end, maybeFinalFlags);
        }
        // If linked entry is hypothetical or extended canon, insert a "?" in front.
        if (linkedEntry.isHypothetical() || linkedEntry.isExtendedCanon()) {
            ssb.insert(m.start(), "?");
            ssb.setSpan(new RelativeSizeSpan(smallTextScale), m.start(), m.start() + 1, intermediateFlags);
            ssb.setSpan(new SuperscriptSpan(), m.start(), m.start() + 1, maybeFinalFlags);
            end++;
        }
        // Only apply colours to verbs, nouns, and affixes (exclude BLUE and WHITE).
        if (!disableEntryLink) {
            // Link to view launcher.
            ssb.setSpan(viewLauncher, m.start(), end, useColours ? intermediateFlags : finalFlags);
        }
        // Set the colour last, so it's not overridden by other spans.
        if (useColours) {
            ssb.setSpan(new ForegroundColorSpan(linkedEntry.getTextColor()), m.start(), end, finalFlags);
        }
        String linkedPos = linkedEntry.getBracketedPartOfSpeech(/* isHtml */false);
        if (!linkedPos.equals("") && linkedPos.length() > 1) {
            ssb.insert(end, linkedPos);

            int rightBracketLoc = linkedPos.indexOf(")");
            if (rightBracketLoc != -1) {
                // linkedPos is always of the form " (pos)[ (def'n N)]", we want to italicise
                // the "pos" part only.
                ssb.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), end + 2, end + rightBracketLoc,
                        finalFlags);
            }
        }

        // Rinse and repeat.
        expandedDefinition = ssb.toString();
        m = KlingonContentProvider.Entry.ENTRY_PATTERN.matcher(expandedDefinition);
    }

    // Display the entry name and definition.
    entryText.invalidate();
    entryText.setText(ssb);
    entryText.setMovementMethod(LinkMovementMethod.getInstance());
}