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:
parent
6b0ae5be95
commit
5d621cecda
7 changed files with 83 additions and 60 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue