added emoji loader, added status emoji support

This commit is contained in:
nuclearfog 2023-03-21 23:18:15 +01:00
parent f0170c4e19
commit 2519b1732b
No known key found for this signature in database
GPG Key ID: 03488A185C476379
2 changed files with 152 additions and 0 deletions

View File

@ -0,0 +1,118 @@
package org.nuclearfog.twidda.backend.async;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.api.Connection;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.api.ConnectionManager;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.model.Emoji;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.TreeMap;
/**
* Emoji image loader class
*
* @author nuclearfog
*/
public class EmojiLoader extends AsyncExecutor<EmojiLoader.EmojiParam, EmojiLoader.EmojiResult> {
private static final String FOLDER = "emojis";
private File imageFolder;
private Connection connection;
/**
*
*/
public EmojiLoader(Context context) {
connection = ConnectionManager.getDefaultConnection(context);
imageFolder = new File(context.getExternalCacheDir(), FOLDER);
imageFolder.mkdirs();
}
@NonNull
@Override
protected EmojiResult doInBackground(@NonNull EmojiParam param) {
try {
Map<String, File> files = new TreeMap<>();
Map<String, Bitmap> result = new TreeMap<>();
// cache all local image files first
File[] imageFiles = imageFolder.listFiles();
if (imageFiles != null) {
for (File file : imageFiles) {
files.put(file.getName(), file);
}
}
for (Emoji emoji : param.emojis) {
File file = files.get(emoji.getCode());
if (file == null) {
// download image to cache
MediaStatus media = connection.downloadImage(emoji.getUrl());
InputStream input = media.getStream();
file = new File(imageFolder, emoji.getCode());
file.createNewFile();
FileOutputStream output = new FileOutputStream(file);
Bitmap icon = BitmapFactory.decodeStream(input);
icon.compress(Bitmap.CompressFormat.PNG, 1, output);
// resize image
icon = Bitmap.createScaledBitmap(icon, icon.getWidth() / icon.getHeight() * param.size, param.size, false);
result.put(emoji.getCode(), icon);
} else {
// load image from cache
FileInputStream inputStream = new FileInputStream(file);
Bitmap icon = BitmapFactory.decodeStream(inputStream);
// resize image
icon = Bitmap.createScaledBitmap(icon, icon.getWidth() / icon.getHeight() * param.size, param.size, false);
result.put(emoji.getCode(), icon);
}
}
return new EmojiResult(result, null);
} catch (ConnectionException exception) {
return new EmojiResult(null, exception);
} catch (Exception exception) {
return new EmojiResult(null, null);
}
}
/**
*
*/
public static class EmojiParam {
Emoji[] emojis;
int size;
public EmojiParam(Emoji[] emojis, int size) {
this.emojis = emojis;
this.size = size;
}
}
/**
*
*/
public static class EmojiResult {
@Nullable
public Map<String, Bitmap> images;
@Nullable
public ConnectionException exception;
EmojiResult(@Nullable Map<String, Bitmap> images, @Nullable ConnectionException exception) {
this.exception = exception;
this.images = images;
}
}
}

View File

@ -17,10 +17,16 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter; import android.graphics.BlurMaskFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.SubMenu; import android.view.SubMenu;
@ -51,6 +57,9 @@ import org.nuclearfog.textviewtool.LinkAndScrollMovement;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.api.ConnectionException; import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback; import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiParam;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiResult;
import org.nuclearfog.twidda.backend.async.NotificationAction; import org.nuclearfog.twidda.backend.async.NotificationAction;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionParam; import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionParam;
import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionResult; import org.nuclearfog.twidda.backend.async.NotificationAction.NotificationActionResult;
@ -87,6 +96,7 @@ import org.nuclearfog.twidda.ui.fragments.StatusFragment;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
@ -190,6 +200,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
private AsyncCallback<PollActionResult> pollResult = this::onPollResult; private AsyncCallback<PollActionResult> pollResult = this::onPollResult;
private AsyncCallback<TranslationResult> translationResult = this::onTranslationResult; private AsyncCallback<TranslationResult> translationResult = this::onTranslationResult;
private AsyncCallback<NotificationActionResult> notificationCallback = this::onNotificationResult; private AsyncCallback<NotificationActionResult> notificationCallback = this::onNotificationResult;
private AsyncCallback<EmojiResult> emojiResultCallback = this::onEmojiResult;
@Nullable @Nullable
private ClipboardManager clip; private ClipboardManager clip;
@ -197,6 +208,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
private PollAction voteAsync; private PollAction voteAsync;
private NotificationAction notificationAsync; private NotificationAction notificationAsync;
private TranslationLoader translationAsync; private TranslationLoader translationAsync;
private EmojiLoader emojiAsync;
private GlobalSettings settings; private GlobalSettings settings;
private Picasso picasso; private Picasso picasso;
private PreviewAdapter adapter; private PreviewAdapter adapter;
@ -257,6 +269,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
voteAsync = new PollAction(this); voteAsync = new PollAction(this);
notificationAsync = new NotificationAction(this); notificationAsync = new NotificationAction(this);
translationAsync = new TranslationLoader(this); translationAsync = new TranslationLoader(this);
emojiAsync = new EmojiLoader(this);
picasso = PicassoBuilder.get(this); picasso = PicassoBuilder.get(this);
settings = GlobalSettings.getInstance(this); settings = GlobalSettings.getInstance(this);
@ -912,6 +925,10 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
cardList.setVisibility(View.GONE); cardList.setVisibility(View.GONE);
statusText.setMaxLines(10); statusText.setMaxLines(10);
} }
if (status.getEmojis().length > 0 && emojiAsync.isIdle()) {
EmojiParam param = new EmojiParam(status.getEmojis(), statusText.getLineHeight());
emojiAsync.execute(param, emojiResultCallback);
}
} }
/** /**
@ -1119,4 +1136,21 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
} }
} }
/**
* set cached status emojis
*/
private void onEmojiResult(EmojiResult result) {
if (result.images != null && statusText.getText() instanceof SpannableString) {
SpannableStringBuilder builder = new SpannableStringBuilder((SpannableString) statusText.getText());
for (Map.Entry<String, Bitmap> item : result.images.entrySet()) {
int idx = TextUtils.indexOf(builder, ':' + item.getKey() + ':');
if (idx >= 0) {
ImageSpan imgSpan = new ImageSpan(getApplicationContext(), item.getValue());
builder.setSpan(imgSpan, idx, idx + item.getKey().length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
statusText.setText(builder);
}
}
} }