apply styling helper to conversation overview

This commit is contained in:
Daniel Gultsch 2018-05-06 11:22:20 +02:00
parent 1fcd69ce40
commit 7ca719b8be
6 changed files with 158 additions and 15 deletions

View File

@ -548,10 +548,10 @@ public class NotificationService {
} }
/** message preview for Android Auto **/ /** message preview for Android Auto **/
for (Message message : messages) { for (Message message : messages) {
Pair<String, Boolean> preview = UIHelper.getMessagePreview(mXmppConnectionService, message); Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(mXmppConnectionService, message);
// only show user written text // only show user written text
if (!preview.second) { if (!preview.second) {
uBuilder.addMessage(preview.first); uBuilder.addMessage(preview.first.toString());
uBuilder.setLatestTimestamp(message.getTimeSent()); uBuilder.setLatestTimestamp(message.getTimeSent());
} }
} }

View File

@ -156,9 +156,9 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
viewHolder.lastMessageIcon.setVisibility(View.GONE); viewHolder.lastMessageIcon.setVisibility(View.GONE);
showPreviewText = true; showPreviewText = true;
} }
final Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message); final Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(activity, message, viewHolder.lastMessage.getCurrentTextColor());
if (showPreviewText) { if (showPreviewText) {
viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first)); viewHolder.lastMessage.setText(EmojiWrapper.transform(UIHelper.shorten(preview.first)));
} else { } else {
viewHolder.lastMessageIcon.setContentDescription(preview.first); viewHolder.lastMessageIcon.setContentDescription(preview.first);
} }

View File

@ -360,7 +360,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} }
} }
private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) { private void displayInfoMessage(ViewHolder viewHolder, CharSequence text, boolean darkBackground) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package eu.siacs.conversations.utils;
import android.text.Spannable;
import java.util.ArrayList;
import java.util.List;
public class CharSequenceUtils {
private static int getStartIndex(CharSequence input) {
int length = input.length();
int index = 0;
while (Character.isWhitespace(input.charAt(index))) {
++index;
if (index >= length) {
break;
}
}
return index;
}
private static int getEndIndex(CharSequence input) {
int index = input.length() - 1;
while (Character.isWhitespace(input.charAt(index))) {
--index;
if (index < 0) {
break;
}
}
return index;
}
public static CharSequence trim(CharSequence input) {
int begin = getStartIndex(input);
int end = getEndIndex(input);
if (begin > end) {
return "";
} else {
return StylingHelper.subSequence(input, begin, end + 1);
}
}
public static List<CharSequence> split(Spannable charSequence, char c) {
List<CharSequence> out = new ArrayList<>();
int begin = 0;
for (int i = 0; i < charSequence.length(); ++i) {
if (charSequence.charAt(i) == c) {
out.add(StylingHelper.subSequence(charSequence, begin, i));
begin = ++i;
}
}
if (begin < charSequence.length()) {
out.add(StylingHelper.subSequence(charSequence, begin, charSequence.length()));
}
return out;
}
}

View File

@ -36,6 +36,7 @@ import android.support.annotation.ColorInt;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.text.Editable; import android.text.Editable;
import android.text.ParcelableSpan; import android.text.ParcelableSpan;
import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -44,6 +45,7 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan; import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import android.util.Log;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
@ -51,6 +53,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.text.QuoteSpan; import eu.siacs.conversations.ui.text.QuoteSpan;
@ -132,6 +135,44 @@ public class StylingHelper {
} }
static CharSequence subSequence(CharSequence charSequence, int start, int end) {
if (start == 0 && charSequence.length() + 1 == end) {
return charSequence;
}
if (charSequence instanceof Spannable) {
Spannable spannable = (Spannable) charSequence;
Spannable sub = (Spannable) spannable.subSequence(start, end);
for (Class<? extends ParcelableSpan> clazz : SPAN_CLASSES) {
ParcelableSpan[] spannables = spannable.getSpans(start, end, clazz);
for (ParcelableSpan parcelableSpan : spannables) {
int beginSpan = spannable.getSpanStart(parcelableSpan);
int endSpan = spannable.getSpanEnd(parcelableSpan);
if (beginSpan >= start && endSpan <= end) {
continue;
}
sub.setSpan(clone(parcelableSpan), Math.max(beginSpan - start, 0), Math.min(sub.length() - 1, endSpan), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return sub;
} else {
return charSequence.subSequence(start, end);
}
}
private static ParcelableSpan clone(ParcelableSpan span) {
if (span instanceof ForegroundColorSpan) {
return new ForegroundColorSpan(((ForegroundColorSpan) span).getForegroundColor());
} else if (span instanceof TypefaceSpan) {
return new TypefaceSpan(((TypefaceSpan) span).getFamily());
} else if (span instanceof StyleSpan) {
return new StyleSpan(((StyleSpan) span).getStyle());
} else if (span instanceof StrikethroughSpan) {
return new StrikethroughSpan();
} else {
throw new AssertionError("Unknown Span");
}
}
public static boolean isDarkText(TextView textView) { public static boolean isDarkText(TextView textView) {
int argb = textView.getCurrentTextColor(); int argb = textView.getCurrentTextColor();
return Color.red(argb) + Color.green(argb) + Color.blue(argb) == 0; return Color.red(argb) + Color.green(argb) + Color.blue(argb) == 0;
@ -163,7 +204,7 @@ public class StylingHelper {
private static private static
@ColorInt @ColorInt
int transformColor(@ColorInt int c) { int transformColor(@ColorInt int c) {
return Color.argb(Math.round(Color.alpha(c) * 0.6f), Color.red(c), Color.green(c), Color.blue(c)); return Color.argb(Math.round(Color.alpha(c) * 0.45f), Color.red(c), Color.green(c), Color.blue(c));
} }
private static int indexOfIgnoreCase(final String haystack, final String needle, final int start) { private static int indexOfIgnoreCase(final String haystack, final String needle, final int start) {

View File

@ -1,8 +1,12 @@
package eu.siacs.conversations.utils; package eu.siacs.conversations.utils;
import android.content.Context; import android.content.Context;
import android.support.annotation.ColorInt;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.widget.PopupMenu; import android.widget.PopupMenu;
@ -10,6 +14,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@ -244,7 +249,11 @@ public class UIHelper {
} }
} }
public static Pair<String, Boolean> getMessagePreview(final Context context, final Message message) { public static Pair<CharSequence, Boolean> getMessagePreview(final Context context, final Message message) {
return getMessagePreview(context, message, 0);
}
public static Pair<CharSequence, Boolean> getMessagePreview(final Context context, final Message message, @ColorInt int textColor) {
final Transferable d = message.getTransferable(); final Transferable d = message.getTransferable();
if (d != null) { if (d != null) {
switch (d.getStatus()) { switch (d.getStatus()) {
@ -293,14 +302,17 @@ public class UIHelper {
return new Pair<>(context.getString(R.string.x_file_offered_for_download, return new Pair<>(context.getString(R.string.x_file_offered_for_download,
getFileDescriptionString(context, message)), true); getFileDescriptionString(context, message)), true);
} else { } else {
String[] lines = body.split("\n"); SpannableStringBuilder styledBody = new SpannableStringBuilder(body);
StringBuilder builder = new StringBuilder(); if (textColor != 0) {
for (String l : lines) { StylingHelper.format(styledBody, 0, styledBody.length() - 1, textColor);
}
SpannableStringBuilder builder = new SpannableStringBuilder();
for (CharSequence l : CharSequenceUtils.split(styledBody, '\n')) {
if (l.length() > 0) { if (l.length() > 0) {
char first = l.charAt(0); char first = l.charAt(0);
if ((first != '>' || !isPositionFollowedByQuoteableCharacter(l, 0)) && first != '\u00bb') { if ((first != '>' || !isPositionFollowedByQuoteableCharacter(l, 0)) && first != '\u00bb') {
String line = l.trim(); CharSequence line = CharSequenceUtils.trim(l);
if (line.isEmpty()) { if (line.length() == 0) {
continue; continue;
} }
char last = line.charAt(line.length() - 1); char last = line.charAt(line.length() - 1);
@ -317,11 +329,15 @@ public class UIHelper {
if (builder.length() == 0) { if (builder.length() == 0) {
builder.append(body.trim()); builder.append(body.trim());
} }
return new Pair<>(builder.length() > 256 ? builder.substring(0, 256) : builder.toString(), false); return new Pair<>(builder, false);
} }
} }
} }
public static CharSequence shorten(CharSequence input) {
return input.length() > 256 ? StylingHelper.subSequence(input, 0, 256) : input;
}
public static boolean isPositionFollowedByQuoteableCharacter(CharSequence body, int pos) { public static boolean isPositionFollowedByQuoteableCharacter(CharSequence body, int pos) {
return !isPositionFollowedByNumber(body, pos) return !isPositionFollowedByNumber(body, pos)
&& !isPositionFollowedByEmoticon(body, pos) && !isPositionFollowedByEmoticon(body, pos)