Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Threema/app/src/main/java/ch/threema/app/ui/     Datei vom 25.3.2026 mit Größe 6 kB image not shown  

Quelle  MentionSpan.java

  Sprache: JAVA
 

package ch.threema.app.ui;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.style.ReplacementSpan;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import ch.threema.app.R;
import ch.threema.app.ThreemaApplication;
import ch.threema.app.managers.ServiceManager;
import ch.threema.app.services.ContactService;
import ch.threema.app.services.UserService;
import ch.threema.app.utils.NameUtil;
import ch.threema.app.utils.TestUtil;
import ch.threema.data.datatypes.ContactNameFormat;

import static ch.threema.app.emojis.EmojiMarkupUtil.MENTION_INDICATOR;

public class MentionSpan extends ReplacementSpan {

    // ReplacementSpan does not support Spans that span multiple lines in a TextView!
    // Hack: Limit label length to 16 chars
    private static final int LABEL_MAX_LENGTH = 16;

    private static final int PADDING = ThreemaApplication.getAppContext().getResources().getDimensionPixelSize(R.dimen.mention_padding);
    private static final int RADIUS = ThreemaApplication.getAppContext().getResources().getDimensionPixelSize(R.dimen.mention_radius);

    private @Nullable ContactService contactService;
    private @Nullable UserService userService;

    private int width = 0;
    private final Paint backgroundPaint;
    private final Paint invertedPaint;
    private final @ColorInt int textColor;
    private final @ColorInt int invertedTextColor;
    private boolean strikeThroughText;
    private @NonNull ContactNameFormat contactNameFormat = ContactNameFormat.DEFAULT;

    public MentionSpan(
        @ColorInt int backgroundColor,
        @ColorInt int invertedColor,
        @ColorInt int textColor,
        @ColorInt int invertedTextColor,
        boolean strikeThroughText
    ) {
        super();

        try {
            final @NonNull ServiceManager serviceManager = ThreemaApplication.requireServiceManager();
            contactService = serviceManager.getContactService();
            userService = serviceManager.getUserService();
            contactNameFormat = serviceManager.getPreferenceService().getContactNameFormat();
        } catch (Exception e) {
            //
        }

        backgroundPaint = new Paint();
        backgroundPaint.setStyle(Paint.Style.FILL);
        backgroundPaint.setColor(backgroundColor);

        invertedPaint = new Paint();
        invertedPaint.setStyle(Paint.Style.FILL);
        invertedPaint.setColor(invertedColor);

        this.textColor = textColor;
        this.invertedTextColor = invertedTextColor;

        this.strikeThroughText = strikeThroughText;
    }

    public void setStrikeThrough(boolean strikeThrough) {
        this.strikeThroughText = strikeThrough;
    }

    @NonNull
    private String getMentionLabelText(@NonNull CharSequence text, int start, int end) {
        final String identity = text.toString().substring(start + 2, end - 1);

        String label = NameUtil.getQuoteName(identity, this.contactService, this.userService, contactNameFormat);
        if (label.length() > LABEL_MAX_LENGTH) {
            label = label.substring(0, LABEL_MAX_LENGTH).trim() + "…";
        }
        return label;
    }

    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
        if (!TestUtil.isBlankOrNull(text) && end - start == 11) {
            String labelText = getMentionLabelText(text, start, end);
            if (!TestUtil.isEmptyOrNull(labelText)) {
                if (fm != null) {
                    // The span is not rendered if it spans the entire text and no height is set. Therefore, we need to apply the font metrics
                    // here such that a height can be calculated.
                    final Paint.FontMetrics fontMetrics = paint.getFontMetrics();
                    fm.ascent = (int) fontMetrics.ascent;
                    fm.bottom = (int) fontMetrics.bottom;
                    fm.descent = (int) fontMetrics.descent;
                    fm.leading = (int) fontMetrics.leading;
                    fm.top = (int) fontMetrics.top;
                }

                width = (int) paint.measureText(MENTION_INDICATOR + labelText) + (PADDING * 2);
                return width;
            }
        }
        return 0;
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
        if (width != 0 && !TestUtil.isBlankOrNull(text) && end - start == 11) {
            int alpha = paint.getAlpha();
            String identity = text.subSequence(start + 2, end - 1).toString();

            if (identity.equals(ContactService.ALL_USERS_PLACEHOLDER_ID) || identity.equals(userService.getIdentity())) {
                canvas.drawRoundRect(new RectF(x, top + 1f, x + width, bottom), RADIUS, RADIUS, invertedPaint);
                paint.setColor(this.invertedTextColor);
                paint.setAlpha(0x78);
            } else {
                canvas.drawRoundRect(new RectF(x, top + 1f, x + width, bottom), RADIUS, RADIUS, backgroundPaint);
                paint.setColor(this.textColor);
                paint.setAlpha(0x50);
            }
            canvas.drawText(MENTION_INDICATOR, x + PADDING, y, paint);
            paint.setAlpha(0xFF);

            canvas.drawText(getMentionLabelText(text, start, end), x + PADDING + paint.measureText(MENTION_INDICATOR), y, paint);
            paint.setAlpha(alpha);

            if (strikeThroughText) {
                setStrikeThroughText(canvas, paint, x, y);
            }
        }
    }

    private void setStrikeThroughText(@NonNull Canvas canvas, @NonNull Paint paint, float x, float y) {
        // Draw an empty string that covers the canvas with strike through flag
        boolean isStrikeThruText = paint.isStrikeThruText();
        paint.setStrikeThruText(true);

        // Create empty string that covers the whole canvas width (font dependent)
        int numSpaces = (int) Math.ceil(width / paint.measureText(" "));
        StringBuilder spaces = new StringBuilder();
        for (int i = 0; i < numSpaces; i++) {
            spaces.append(" ");
        }

        canvas.drawText(spaces.toString(), x, y, paint);

        // Restore strike through state to prevent side effects
        paint.setStrikeThruText(isStrikeThruText);
    }
}

Messung V0.5 in Prozent
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.