Changes mention and tag highlighting in the composer to use Mastodon's regex. Closes #145 Also, does some haphazard cleanup.

This commit is contained in:
Vavassor 2017-07-01 20:32:35 -04:00
parent 6b0ae5be95
commit 5d621cecda
7 changed files with 83 additions and 60 deletions

View File

@ -244,7 +244,6 @@ public class AccountActivity extends BaseActivity {
String subtitle = String.format(getString(R.string.status_username_format), String subtitle = String.format(getString(R.string.status_username_format),
account.username); account.username);
getSupportActionBar().setSubtitle(subtitle); getSupportActionBar().setSubtitle(subtitle);
} }
boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this) boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this)

View File

@ -170,7 +170,8 @@ public class EditProfileActivity extends BaseActivity {
Account me = response.body(); Account me = response.body();
priorDisplayName = me.getDisplayName(); priorDisplayName = me.getDisplayName();
priorNote = me.note.toString(); priorNote = me.note.toString();
CircularImageView avatar = (CircularImageView) findViewById(R.id.edit_profile_avatar_preview); CircularImageView avatar =
(CircularImageView) findViewById(R.id.edit_profile_avatar_preview);
ImageView header = (ImageView) findViewById(R.id.edit_profile_header_preview); ImageView header = (ImageView) findViewById(R.id.edit_profile_header_preview);
displayNameEditText.setText(priorDisplayName); displayNameEditText.setText(priorDisplayName);

View File

@ -16,6 +16,7 @@
package com.keylesspalace.tusky.json; package com.keylesspalace.tusky.json;
import android.text.Spanned; import android.text.Spanned;
import android.text.SpannedString;
import com.emojione.Emojione; import com.emojione.Emojione;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
@ -28,7 +29,13 @@ import java.lang.reflect.Type;
public class SpannedTypeAdapter implements JsonDeserializer<Spanned> { public class SpannedTypeAdapter implements JsonDeserializer<Spanned> {
@Override @Override
public Spanned deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public Spanned deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
return HtmlUtils.fromHtml(Emojione.shortnameToUnicode(json.getAsString(), false)); throws JsonParseException {
String string = json.getAsString();
if (string != null) {
return HtmlUtils.fromHtml(Emojione.shortnameToUnicode(string, false));
} else {
return new SpannedString("");
}
} }
} }

View File

@ -190,7 +190,10 @@ public interface MastodonApi {
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/reports") @POST("api/v1/reports")
Call<ResponseBody> report(@Field("account_id") String accountId, @Field("status_ids[]") List<String> statusIds, @Field("comment") String comment); Call<ResponseBody> report(
@Field("account_id") String accountId,
@Field("status_ids[]") List<String> statusIds,
@Field("comment") String comment);
@GET("api/v1/search") @GET("api/v1/search")
Call<SearchResults> search(@Query("q") String q, @Query("resolve") Boolean resolve); Call<SearchResults> search(@Query("q") String q, @Query("resolve") Boolean resolve);

View File

@ -47,8 +47,8 @@ public class LinkHelper {
} }
public static void setClickableText(TextView view, Spanned content, public static void setClickableText(TextView view, Spanned content,
@Nullable Status.Mention[] mentions, boolean useCustomTabs, @Nullable Status.Mention[] mentions, boolean useCustomTabs,
final LinkListener listener) { final LinkListener listener) {
SpannableStringBuilder builder = new SpannableStringBuilder(content); SpannableStringBuilder builder = new SpannableStringBuilder(content);
URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class); URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class);
for (URLSpan span : urlSpans) { for (URLSpan span : urlSpans) {

View File

@ -41,7 +41,7 @@ import com.squareup.picasso.Target;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
public class NotificationMaker { class NotificationMaker {
public static final String TAG = "NotificationMaker"; public static final String TAG = "NotificationMaker";
@ -89,10 +89,12 @@ public class NotificationMaker {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class); stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent); stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class); Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class);
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context) final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_notify) .setSmallIcon(R.drawable.ic_notify)
@ -104,15 +106,16 @@ public class NotificationMaker {
builder.setContentTitle(titleForType(context, body)) builder.setContentTitle(titleForType(context, body))
.setContentText(truncateWithEllipses(bodyForType(body), 40)); .setContentText(truncateWithEllipses(bodyForType(body), 40));
Target mTarget = new Target() { Target target = new Target() {
@Override @Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
builder.setLargeIcon(bitmap); builder.setLargeIcon(bitmap);
setupPreferences(preferences, builder); setupPreferences(preferences, builder);
((NotificationManager) (context.getSystemService(Context.NOTIFICATION_SERVICE))) NotificationManager notificationManager = (NotificationManager)
.notify(notifyId, builder.build()); context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notifyId, builder.build());
} }
@Override @Override
@ -126,12 +129,15 @@ public class NotificationMaker {
.load(body.account.avatar) .load(body.account.avatar)
.placeholder(R.drawable.avatar_default) .placeholder(R.drawable.avatar_default)
.transform(new RoundedTransformation(7, 0)) .transform(new RoundedTransformation(7, 0))
.into(mTarget); .into(target);
} else { } else {
setupPreferences(preferences, builder); setupPreferences(preferences, builder);
try { try {
builder.setContentTitle(String.format(context.getString(R.string.notification_title_summary), currentNotifications.length())) String format = context.getString(R.string.notification_title_summary);
.setContentText(truncateWithEllipses(joinNames(context, currentNotifications), 40)); String title = String.format(format, currentNotifications.length());
String text = truncateWithEllipses(joinNames(context, currentNotifications), 40);
builder.setContentTitle(title)
.setContentText(text);
} catch (JSONException e) { } catch (JSONException e) {
Log.d(TAG, Log.getStackTraceString(e)); Log.d(TAG, Log.getStackTraceString(e));
} }
@ -142,26 +148,23 @@ public class NotificationMaker {
builder.setCategory(android.app.Notification.CATEGORY_SOCIAL); builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
} }
((NotificationManager) (context.getSystemService(Context.NOTIFICATION_SERVICE))) NotificationManager notificationManager =
.notify(notifyId, builder.build()); (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notifyId, builder.build());
} }
private static boolean filterNotification(SharedPreferences preferences, private static boolean filterNotification(SharedPreferences preferences,
Notification notification) { Notification notification) {
switch (notification.type) { switch (notification.type) {
default: default:
case MENTION: { case MENTION:
return preferences.getBoolean("notificationFilterMentions", true); return preferences.getBoolean("notificationFilterMentions", true);
} case FOLLOW:
case FOLLOW: {
return preferences.getBoolean("notificationFilterFollows", true); return preferences.getBoolean("notificationFilterFollows", true);
} case REBLOG:
case REBLOG: {
return preferences.getBoolean("notificationFilterReblogs", true); return preferences.getBoolean("notificationFilterReblogs", true);
} case FAVOURITE:
case FAVOURITE: {
return preferences.getBoolean("notificationFilterFavourites", true); return preferences.getBoolean("notificationFilterFavourites", true);
}
} }
} }
@ -174,7 +177,7 @@ public class NotificationMaker {
} }
private static void setupPreferences(SharedPreferences preferences, private static void setupPreferences(SharedPreferences preferences,
NotificationCompat.Builder builder) { NotificationCompat.Builder builder) {
if (preferences.getBoolean("notificationAlertSound", true)) { if (preferences.getBoolean("notificationAlertSound", true)) {
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
} }
@ -191,11 +194,14 @@ public class NotificationMaker {
@Nullable @Nullable
private static String joinNames(Context context, JSONArray array) throws JSONException { private static String joinNames(Context context, JSONArray array) throws JSONException {
if (array.length() > 3) { if (array.length() > 3) {
return String.format(context.getString(R.string.notification_summary_large), array.get(0), array.get(1), array.get(2), array.length() - 3); return String.format(context.getString(R.string.notification_summary_large),
array.get(0), array.get(1), array.get(2), array.length() - 3);
} else if (array.length() == 3) { } else if (array.length() == 3) {
return String.format(context.getString(R.string.notification_summary_medium), array.get(0), array.get(1), array.get(2)); return String.format(context.getString(R.string.notification_summary_medium),
array.get(0), array.get(1), array.get(2));
} else if (array.length() == 2) { } else if (array.length() == 2) {
return String.format(context.getString(R.string.notification_summary_small), array.get(0), array.get(1)); return String.format(context.getString(R.string.notification_summary_small),
array.get(0), array.get(1));
} }
return null; return null;
@ -205,13 +211,17 @@ public class NotificationMaker {
private static String titleForType(Context context, Notification notification) { private static String titleForType(Context context, Notification notification) {
switch (notification.type) { switch (notification.type) {
case MENTION: case MENTION:
return String.format(context.getString(R.string.notification_mention_format), notification.account.getDisplayName()); return String.format(context.getString(R.string.notification_mention_format),
notification.account.getDisplayName());
case FOLLOW: case FOLLOW:
return String.format(context.getString(R.string.notification_follow_format), notification.account.getDisplayName()); return String.format(context.getString(R.string.notification_follow_format),
notification.account.getDisplayName());
case FAVOURITE: case FAVOURITE:
return String.format(context.getString(R.string.notification_favourite_format), notification.account.getDisplayName()); return String.format(context.getString(R.string.notification_favourite_format),
notification.account.getDisplayName());
case REBLOG: case REBLOG:
return String.format(context.getString(R.string.notification_reblog_format), notification.account.getDisplayName()); return String.format(context.getString(R.string.notification_reblog_format),
notification.account.getDisplayName());
} }
return null; return null;
} }
@ -226,7 +236,6 @@ public class NotificationMaker {
case REBLOG: case REBLOG:
return notification.status.content.toString(); return notification.status.content.toString();
} }
return null; return null;
} }
} }

View File

@ -19,7 +19,17 @@ import android.text.Spannable;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SpanUtils { public class SpanUtils {
private static final String TAG_REGEX = "(?:^|[^/)\\w])#([\\w_]*[\\p{Alpha}_][\\w_]*)";
private static Pattern TAG_PATTERN = Pattern.compile(TAG_REGEX, Pattern.CASE_INSENSITIVE);
private static final String MENTION_REGEX =
"(?:^|[^/[:word:]])@([a-z0-9_]+(?:@[a-z0-9\\.\\-]+[a-z0-9]+)?)";
private static Pattern MENTION_PATTERN =
Pattern.compile(MENTION_REGEX, Pattern.CASE_INSENSITIVE);
private static class FindCharsResult { private static class FindCharsResult {
int charIndex; int charIndex;
int stringIndex; int stringIndex;
@ -63,35 +73,29 @@ public class SpanUtils {
} }
private static int findEndOfHashtag(String string, int fromIndex) { private static int findEndOfHashtag(String string, int fromIndex) {
final int length = string.length(); Matcher matcher = TAG_PATTERN.matcher(string);
for (int i = fromIndex + 1; i < length;) { if (fromIndex >= 1) {
int codepoint = string.codePointAt(i); fromIndex--;
if (Character.isWhitespace(codepoint)) { }
return i; boolean found = matcher.find(fromIndex);
} else if (codepoint == '#') { if (found) {
return -1; return matcher.end();
} } else {
i += Character.charCount(codepoint); return -1;
} }
return length;
} }
private static int findEndOfMention(String string, int fromIndex) { private static int findEndOfMention(String string, int fromIndex) {
int atCount = 0; Matcher matcher = MENTION_PATTERN.matcher(string);
final int length = string.length(); if (fromIndex >= 1) {
for (int i = fromIndex + 1; i < length;) { fromIndex--;
int codepoint = string.codePointAt(i); }
if (Character.isWhitespace(codepoint)) { boolean found = matcher.find(fromIndex);
return i; if (found) {
} else if (codepoint == '@') { return matcher.end();
atCount += 1; } else {
if (atCount >= 2) { return -1;
return -1;
}
}
i += Character.charCount(codepoint);
} }
return length;
} }
public static void highlightSpans(Spannable text, int colour) { public static void highlightSpans(Spannable text, int colour) {