Load custom emoji in statuses (#400)
This commit is contained in:
parent
2859a5075c
commit
3adef27bbb
6 changed files with 199 additions and 82 deletions
|
@ -49,6 +49,7 @@ dependencies {
|
||||||
compile "com.squareup.retrofit2:converter-gson:2.3.0"
|
compile "com.squareup.retrofit2:converter-gson:2.3.0"
|
||||||
compile "com.squareup.picasso:picasso:2.5.2"
|
compile "com.squareup.picasso:picasso:2.5.2"
|
||||||
compile "com.squareup.okhttp3:okhttp:3.9.0"
|
compile "com.squareup.okhttp3:okhttp:3.9.0"
|
||||||
|
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||||
compile "com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0"
|
compile "com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0"
|
||||||
compile "com.pkmmte.view:circularimageview:1.1"
|
compile "com.pkmmte.view:circularimageview:1.1"
|
||||||
compile "com.github.varunest:sparkbutton:1.0.5"
|
compile "com.github.varunest:sparkbutton:1.0.5"
|
||||||
|
|
|
@ -50,6 +50,7 @@ import okhttp3.Interceptor;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
|
@ -126,13 +127,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
protected void createMastodonApi() {
|
protected void createMastodonApi() {
|
||||||
mastodonApiDispatcher = new Dispatcher();
|
mastodonApiDispatcher = new Dispatcher();
|
||||||
|
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder().registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
|
||||||
.registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
|
|
||||||
.registerTypeAdapter(StringWithEmoji.class, new StringWithEmojiTypeAdapter())
|
.registerTypeAdapter(StringWithEmoji.class, new StringWithEmojiTypeAdapter())
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder()
|
OkHttpClient.Builder okBuilder =
|
||||||
.addInterceptor(new Interceptor() {
|
OkHttpUtils.getCompatibleClientBuilder().addInterceptor(new Interceptor() {
|
||||||
@Override
|
@Override
|
||||||
public Response intercept(Chain chain) throws IOException {
|
public Response intercept(Chain chain) throws IOException {
|
||||||
Request originalRequest = chain.request();
|
Request originalRequest = chain.request();
|
||||||
|
@ -140,20 +140,21 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
Request.Builder builder = originalRequest.newBuilder();
|
Request.Builder builder = originalRequest.newBuilder();
|
||||||
String accessToken = getAccessToken();
|
String accessToken = getAccessToken();
|
||||||
if (accessToken != null) {
|
if (accessToken != null) {
|
||||||
builder.header("Authorization", String.format("Bearer %s",
|
builder.header("Authorization", String.format("Bearer %s", accessToken));
|
||||||
accessToken));
|
|
||||||
}
|
}
|
||||||
Request newRequest = builder.build();
|
Request newRequest = builder.build();
|
||||||
|
|
||||||
return chain.proceed(newRequest);
|
return chain.proceed(newRequest);
|
||||||
}
|
}
|
||||||
})
|
}).dispatcher(mastodonApiDispatcher);
|
||||||
.dispatcher(mastodonApiDispatcher)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Retrofit retrofit = new Retrofit.Builder()
|
if (BuildConfig.DEBUG) {
|
||||||
.baseUrl(getBaseUrl())
|
okBuilder.addInterceptor(
|
||||||
.client(okHttpClient)
|
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
|
||||||
|
}
|
||||||
|
|
||||||
|
Retrofit retrofit = new Retrofit.Builder().baseUrl(getBaseUrl())
|
||||||
|
.client(okBuilder.build())
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -161,11 +162,11 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createTuskyApi() {
|
protected void createTuskyApi() {
|
||||||
Retrofit retrofit = new Retrofit.Builder()
|
Retrofit retrofit =
|
||||||
.baseUrl("https://" + getString(R.string.tusky_api_url))
|
new Retrofit.Builder().baseUrl("https://" + getString(R.string.tusky_api_url))
|
||||||
.client(OkHttpUtils.getCompatibleClient())
|
.client(OkHttpUtils.getCompatibleClient())
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
tuskyApi = retrofit.create(TuskyApi.class);
|
tuskyApi = retrofit.create(TuskyApi.class);
|
||||||
}
|
}
|
||||||
|
@ -208,27 +209,25 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
long checkInterval = 1000 * 60 * minutes;
|
long checkInterval = 1000 * 60 * minutes;
|
||||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||||
Intent intent = new Intent(this, PullNotificationService.class);
|
Intent intent = new Intent(this, PullNotificationService.class);
|
||||||
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE,
|
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
|
||||||
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),
|
||||||
SystemClock.elapsedRealtime(), checkInterval, serviceAlarmIntent);
|
checkInterval, serviceAlarmIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void disablePushNotifications() {
|
protected void disablePushNotifications() {
|
||||||
// Cancel the repeating call for "pull" notifications.
|
// Cancel the repeating call for "pull" notifications.
|
||||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||||
Intent intent = new Intent(this, PullNotificationService.class);
|
Intent intent = new Intent(this, PullNotificationService.class);
|
||||||
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE,
|
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
|
||||||
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
alarmManager.cancel(serviceAlarmIntent);
|
alarmManager.cancel(serviceAlarmIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clearNotifications() {
|
protected void clearNotifications() {
|
||||||
SharedPreferences notificationPreferences = getApplicationContext()
|
SharedPreferences notificationPreferences =
|
||||||
.getSharedPreferences("Notifications", MODE_PRIVATE);
|
getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE);
|
||||||
notificationPreferences.edit()
|
notificationPreferences.edit().putString("current", "[]").apply();
|
||||||
.putString("current", "[]")
|
|
||||||
.apply();
|
|
||||||
|
|
||||||
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
manager.cancel(PullNotificationService.NOTIFY_ID);
|
manager.cancel(PullNotificationService.NOTIFY_ID);
|
||||||
|
@ -238,10 +237,10 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
long checkInterval = 1000 * 60 * minutes;
|
long checkInterval = 1000 * 60 * minutes;
|
||||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||||
Intent intent = new Intent(this, PullNotificationService.class);
|
Intent intent = new Intent(this, PullNotificationService.class);
|
||||||
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE,
|
PendingIntent serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
|
||||||
intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
alarmManager.cancel(serviceAlarmIntent);
|
alarmManager.cancel(serviceAlarmIntent);
|
||||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),
|
||||||
SystemClock.elapsedRealtime(), checkInterval, serviceAlarmIntent);
|
checkInterval, serviceAlarmIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,11 @@ package com.keylesspalace.tusky.adapter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
|
@ -9,7 +14,9 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.content.res.AppCompatResources;
|
import android.support.v7.content.res.AppCompatResources;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
import android.text.style.ReplacementSpan;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
@ -25,11 +32,17 @@ import com.keylesspalace.tusky.util.LinkHelper;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
import com.keylesspalace.tusky.view.RoundedTransformation;
|
import com.keylesspalace.tusky.view.RoundedTransformation;
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||||
|
import com.squareup.picasso.Callback;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
import com.squareup.picasso.Target;
|
||||||
import com.varunest.sparkbutton.SparkButton;
|
import com.varunest.sparkbutton.SparkButton;
|
||||||
import com.varunest.sparkbutton.SparkEventListener;
|
import com.varunest.sparkbutton.SparkEventListener;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private View container;
|
private View container;
|
||||||
|
@ -80,10 +93,8 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
videoIndicator = itemView.findViewById(R.id.status_video_indicator);
|
videoIndicator = itemView.findViewById(R.id.status_video_indicator);
|
||||||
mediaLabel = itemView.findViewById(R.id.status_media_label);
|
mediaLabel = itemView.findViewById(R.id.status_media_label);
|
||||||
contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar);
|
contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar);
|
||||||
contentWarningDescription =
|
contentWarningDescription = itemView.findViewById(R.id.status_content_warning_description);
|
||||||
itemView.findViewById(R.id.status_content_warning_description);
|
contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
|
||||||
contentWarningButton =
|
|
||||||
itemView.findViewById(R.id.status_content_warning_button);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDisplayName(String name) {
|
private void setDisplayName(String name) {
|
||||||
|
@ -97,14 +108,43 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
username.setText(usernameText);
|
username.setText(usernameText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setContent(Spanned content, Status.Mention[] mentions,
|
private Callback spanCallback = new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
content.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void setContent(Spanned content, Status.Mention[] mentions, List<Status.Emoji> emojis,
|
||||||
StatusActionListener listener) {
|
StatusActionListener listener) {
|
||||||
|
|
||||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(content);
|
||||||
|
if (!emojis.isEmpty()) {
|
||||||
|
CharSequence text = builder.subSequence(0, builder.length());
|
||||||
|
for (Status.Emoji emoji : emojis) {
|
||||||
|
CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
|
||||||
|
Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
|
||||||
|
while (matcher.find()) {
|
||||||
|
EmojiSpan span = new EmojiSpan();
|
||||||
|
span.setCallback(spanCallback);
|
||||||
|
builder.setSpan(span, matcher.start(), matcher.end(), 0);
|
||||||
|
Picasso.with(container.getContext())
|
||||||
|
.load(emoji.getUrl())
|
||||||
|
.into(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Redirect URLSpan's in the status content to the listener for viewing tag pages and
|
/* Redirect URLSpan's in the status content to the listener for viewing tag pages and
|
||||||
* account pages. */
|
* account pages. */
|
||||||
Context context = this.content.getContext();
|
Context context = this.content.getContext();
|
||||||
boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(context)
|
boolean useCustomTabs =
|
||||||
.getBoolean("customTabs", true);
|
PreferenceManager.getDefaultSharedPreferences(context).getBoolean("customTabs", true);
|
||||||
LinkHelper.setClickableText(this.content, content, mentions, useCustomTabs, listener);
|
LinkHelper.setClickableText(this.content, builder, mentions, useCustomTabs, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAvatar(String url, @Nullable String rebloggedUrl) {
|
void setAvatar(String url, @Nullable String rebloggedUrl) {
|
||||||
|
@ -177,15 +217,13 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private void setMediaPreviews(final Status.MediaAttachment[] attachments, boolean sensitive,
|
private void setMediaPreviews(final Status.MediaAttachment[] attachments, boolean sensitive,
|
||||||
final StatusActionListener listener, boolean showingSensitive) {
|
final StatusActionListener listener, boolean showingSensitive) {
|
||||||
final ImageView[] previews = {
|
final ImageView[] previews = {
|
||||||
mediaPreview0,
|
mediaPreview0, mediaPreview1, mediaPreview2, mediaPreview3
|
||||||
mediaPreview1,
|
|
||||||
mediaPreview2,
|
|
||||||
mediaPreview3
|
|
||||||
};
|
};
|
||||||
Context context = mediaPreview0.getContext();
|
Context context = mediaPreview0.getContext();
|
||||||
|
|
||||||
int mediaPreviewUnloadedId = ThemeUtils.getDrawableId(itemView.getContext(),
|
int mediaPreviewUnloadedId =
|
||||||
R.attr.media_preview_unloaded_drawable, android.R.color.black);
|
ThemeUtils.getDrawableId(itemView.getContext(), R.attr.media_preview_unloaded_drawable,
|
||||||
|
android.R.color.black);
|
||||||
|
|
||||||
final int n = Math.min(attachments.length, Status.MAX_MEDIA_ATTACHMENTS);
|
final int n = Math.min(attachments.length, Status.MAX_MEDIA_ATTACHMENTS);
|
||||||
|
|
||||||
|
@ -200,9 +238,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
previews[i].setVisibility(View.VISIBLE);
|
previews[i].setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
if (previewUrl == null || previewUrl.isEmpty()) {
|
if (previewUrl == null || previewUrl.isEmpty()) {
|
||||||
Picasso.with(context)
|
Picasso.with(context).load(mediaPreviewUnloadedId).into(previews[i]);
|
||||||
.load(mediaPreviewUnloadedId)
|
|
||||||
.into(previews[i]);
|
|
||||||
} else {
|
} else {
|
||||||
Picasso.with(context)
|
Picasso.with(context)
|
||||||
.load(previewUrl)
|
.load(previewUrl)
|
||||||
|
@ -211,8 +247,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Status.MediaAttachment.Type type = attachments[i].type;
|
final Status.MediaAttachment.Type type = attachments[i].type;
|
||||||
if (type == Status.MediaAttachment.Type.VIDEO
|
if (type == Status.MediaAttachment.Type.VIDEO | type == Status.MediaAttachment.Type.GIFV) {
|
||||||
| type == Status.MediaAttachment.Type.GIFV) {
|
|
||||||
videoIndicator.setVisibility(View.VISIBLE);
|
videoIndicator.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +268,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
if (sensitive && (!isAlwayShowSensitive)) {
|
if (sensitive && (!isAlwayShowSensitive)) {
|
||||||
sensitiveMediaWarning.setVisibility(showingSensitive ? View.GONE : View.VISIBLE);
|
sensitiveMediaWarning.setVisibility(showingSensitive ? View.GONE : View.VISIBLE);
|
||||||
sensitiveMediaShow.setVisibility(showingSensitive ? View.VISIBLE : View.GONE);
|
sensitiveMediaShow.setVisibility(showingSensitive ? View.VISIBLE : View.GONE);
|
||||||
sensitiveMediaShow.setOnClickListener(new View.OnClickListener(){
|
sensitiveMediaShow.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
v.setVisibility(View.GONE);
|
v.setVisibility(View.GONE);
|
||||||
|
@ -330,20 +365,19 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
contentWarningDescription.setText(spoilerText);
|
contentWarningDescription.setText(spoilerText);
|
||||||
contentWarningBar.setVisibility(View.VISIBLE);
|
contentWarningBar.setVisibility(View.VISIBLE);
|
||||||
contentWarningButton.setChecked(expanded);
|
contentWarningButton.setChecked(expanded);
|
||||||
contentWarningButton.setOnCheckedChangeListener(
|
contentWarningButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
new CompoundButton.OnCheckedChangeListener() {
|
@Override
|
||||||
@Override
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
listener.onExpandedChange(isChecked, getAdapterPosition());
|
||||||
listener.onExpandedChange(isChecked, getAdapterPosition());
|
}
|
||||||
}
|
if (isChecked) {
|
||||||
if (isChecked) {
|
content.setVisibility(View.VISIBLE);
|
||||||
content.setVisibility(View.VISIBLE);
|
} else {
|
||||||
} else {
|
content.setVisibility(View.GONE);
|
||||||
content.setVisibility(View.GONE);
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
content.setVisibility(View.VISIBLE);
|
content.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -441,7 +475,7 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
setDisplayName(status.getUserFullName());
|
setDisplayName(status.getUserFullName());
|
||||||
setUsername(status.getNickname());
|
setUsername(status.getNickname());
|
||||||
setCreatedAt(status.getCreatedAt());
|
setCreatedAt(status.getCreatedAt());
|
||||||
setContent(status.getContent(), status.getMentions(), listener);
|
setContent(status.getContent(), status.getMentions(), status.getEmojis(), listener);
|
||||||
setAvatar(status.getAvatar(), status.getRebloggedAvatar());
|
setAvatar(status.getAvatar(), status.getRebloggedAvatar());
|
||||||
setReblogged(status.isReblogged());
|
setReblogged(status.isReblogged());
|
||||||
setFavourited(status.isFavourited());
|
setFavourited(status.isFavourited());
|
||||||
|
@ -478,4 +512,58 @@ class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
setSpoilerText(status.getSpoilerText(), status.isExpanded(), listener);
|
setSpoilerText(status.getSpoilerText(), status.isExpanded(), listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class EmojiSpan extends ReplacementSpan implements Target {
|
||||||
|
|
||||||
|
private @Nullable
|
||||||
|
Drawable imageDrawable;
|
||||||
|
private WeakReference<Callback> callbackWeakReference;
|
||||||
|
|
||||||
|
public void setImageDrawable(@Nullable Drawable imageDrawable) {
|
||||||
|
this.imageDrawable = imageDrawable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(Callback callback) {
|
||||||
|
this.callbackWeakReference = new WeakReference<Callback>(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
|
||||||
|
@Nullable Paint.FontMetricsInt fm) {
|
||||||
|
if (imageDrawable == null) return 0;
|
||||||
|
Rect sizeRect = imageDrawable.getBounds();
|
||||||
|
return sizeRect.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 (imageDrawable == null) return;
|
||||||
|
canvas.save();
|
||||||
|
int transY = bottom - imageDrawable.getBounds().bottom;
|
||||||
|
transY -= paint.getFontMetricsInt().descent;
|
||||||
|
canvas.translate(x, transY);
|
||||||
|
imageDrawable.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||||
|
imageDrawable = new BitmapDrawable(bitmap);
|
||||||
|
imageDrawable.setBounds(0, 0, imageDrawable.getIntrinsicWidth() + 10,
|
||||||
|
imageDrawable.getIntrinsicHeight() + 10);
|
||||||
|
if (callbackWeakReference != null) {
|
||||||
|
Callback cb = callbackWeakReference.get();
|
||||||
|
if (cb != null) cb.onSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBitmapFailed(Drawable errorDrawable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareLoad(Drawable placeHolderDrawable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Status {
|
public class Status {
|
||||||
private Status actionableStatus;
|
private Status actionableStatus;
|
||||||
|
@ -79,6 +80,8 @@ public class Status {
|
||||||
|
|
||||||
public boolean sensitive;
|
public boolean sensitive;
|
||||||
|
|
||||||
|
public List<Emoji> emojis;
|
||||||
|
|
||||||
@SerializedName("spoiler_text")
|
@SerializedName("spoiler_text")
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
|
|
||||||
|
@ -179,4 +182,17 @@ public class Status {
|
||||||
public String name;
|
public String name;
|
||||||
public String website;
|
public String website;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Emoji {
|
||||||
|
private String shortcode;
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
public String getShortcode() {
|
||||||
|
return shortcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ public final class ViewDataUtils {
|
||||||
public static StatusViewData statusToViewData(@Nullable Status status) {
|
public static StatusViewData statusToViewData(@Nullable Status status) {
|
||||||
if (status == null) return null;
|
if (status == null) return null;
|
||||||
Status visibleStatus = status.reblog == null ? status : status.reblog;
|
Status visibleStatus = status.reblog == null ? status : status.reblog;
|
||||||
return new StatusViewData.Builder()
|
return new StatusViewData.Builder().setId(status.id)
|
||||||
.setId(status.id)
|
|
||||||
.setAttachments(visibleStatus.attachments)
|
.setAttachments(visibleStatus.attachments)
|
||||||
.setAvatar(visibleStatus.account.avatar)
|
.setAvatar(visibleStatus.account.avatar)
|
||||||
.setContent(visibleStatus.content)
|
.setContent(visibleStatus.content)
|
||||||
|
@ -44,6 +43,7 @@ public final class ViewDataUtils {
|
||||||
.setSenderId(visibleStatus.account.id)
|
.setSenderId(visibleStatus.account.id)
|
||||||
.setRebloggingEnabled(visibleStatus.rebloggingAllowed())
|
.setRebloggingEnabled(visibleStatus.rebloggingAllowed())
|
||||||
.setApplication(visibleStatus.application)
|
.setApplication(visibleStatus.application)
|
||||||
|
.setEmojis(visibleStatus.emojis)
|
||||||
.createStatusViewData();
|
.createStatusViewData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@ public final class ViewDataUtils {
|
||||||
statusToViewData(notification.status));
|
statusToViewData(notification.status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<NotificationViewData>
|
public static List<NotificationViewData> notificationListToViewDataList(
|
||||||
notificationListToViewDataList(List<Notification> notifications) {
|
List<Notification> notifications) {
|
||||||
List<NotificationViewData> viewDatas = new ArrayList<>(notifications.size());
|
List<NotificationViewData> viewDatas = new ArrayList<>(notifications.size());
|
||||||
for (Notification n : notifications) {
|
for (Notification n : notifications) {
|
||||||
viewDatas.add(notificationToViewData(n));
|
viewDatas.add(notificationToViewData(n));
|
||||||
|
|
|
@ -5,7 +5,9 @@ import android.text.Spanned;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 11/07/2017.
|
* Created by charlag on 11/07/2017.
|
||||||
|
@ -41,16 +43,15 @@ public final class StatusViewData {
|
||||||
private final String senderId;
|
private final String senderId;
|
||||||
private final boolean rebloggingEnabled;
|
private final boolean rebloggingEnabled;
|
||||||
private final Status.Application application;
|
private final Status.Application application;
|
||||||
|
private final List<Status.Emoji> emojis;
|
||||||
|
|
||||||
public StatusViewData(String id, Spanned content, boolean reblogged, boolean favourited,
|
public StatusViewData(String id, Spanned content, boolean reblogged, boolean favourited,
|
||||||
String spoilerText, Status.Visibility visibility,
|
String spoilerText, Status.Visibility visibility, Status.MediaAttachment[] attachments,
|
||||||
Status.MediaAttachment[] attachments, String rebloggedByUsername,
|
String rebloggedByUsername, String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
||||||
String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
boolean isShowingSensitiveWarning, String userFullName, String nickname, String avatar,
|
||||||
boolean isShowingSensitiveWarning, String userFullName, String nickname,
|
Date createdAt, String reblogsCount, String favouritesCount, String inReplyToId,
|
||||||
String avatar, Date createdAt, String reblogsCount,
|
Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
|
||||||
String favouritesCount, String inReplyToId, Status.Mention[] mentions,
|
Status.Application application, List<Status.Emoji> emojis) {
|
||||||
String senderId, boolean rebloggingEnabled,
|
|
||||||
Status.Application application) {
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.reblogged = reblogged;
|
this.reblogged = reblogged;
|
||||||
|
@ -74,6 +75,7 @@ public final class StatusViewData {
|
||||||
this.senderId = senderId;
|
this.senderId = senderId;
|
||||||
this.rebloggingEnabled = rebloggingEnabled;
|
this.rebloggingEnabled = rebloggingEnabled;
|
||||||
this.application = application;
|
this.application = application;
|
||||||
|
this.emojis = emojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -173,6 +175,10 @@ public final class StatusViewData {
|
||||||
return application;
|
return application;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Status.Emoji> getEmojis() {
|
||||||
|
return emojis;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private String id;
|
private String id;
|
||||||
private Spanned content;
|
private Spanned content;
|
||||||
|
@ -197,6 +203,7 @@ public final class StatusViewData {
|
||||||
private String senderId;
|
private String senderId;
|
||||||
private boolean rebloggingEnabled;
|
private boolean rebloggingEnabled;
|
||||||
private Status.Application application;
|
private Status.Application application;
|
||||||
|
private List<Status.Emoji> emojis;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
|
@ -225,6 +232,7 @@ public final class StatusViewData {
|
||||||
senderId = viewData.senderId;
|
senderId = viewData.senderId;
|
||||||
rebloggingEnabled = viewData.rebloggingEnabled;
|
rebloggingEnabled = viewData.rebloggingEnabled;
|
||||||
application = viewData.application;
|
application = viewData.application;
|
||||||
|
emojis = viewData.getEmojis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setId(String id) {
|
public Builder setId(String id) {
|
||||||
|
@ -342,12 +350,17 @@ public final class StatusViewData {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setEmojis(List<Status.Emoji> emojis) {
|
||||||
|
this.emojis = emojis;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public StatusViewData createStatusViewData() {
|
public StatusViewData createStatusViewData() {
|
||||||
|
if (this.emojis == null) emojis = Collections.emptyList();
|
||||||
return new StatusViewData(id, content, reblogged, favourited, spoilerText, visibility,
|
return new StatusViewData(id, content, reblogged, favourited, spoilerText, visibility,
|
||||||
attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||||
isShowingSensitiveContent, userFullName, nickname, avatar, createdAt,
|
isShowingSensitiveContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
||||||
reblogsCount, favouritesCount, inReplyToId, mentions, senderId,
|
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application, emojis);
|
||||||
rebloggingEnabled, application);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue