Add markdown support to notifications

This commit is contained in:
Sternagfonkel
2021-08-21 19:56:42 +02:00
committed by GitHub
parent f6ecfce5d4
commit 9ba9405b0f
4 changed files with 139 additions and 18 deletions

View File

@@ -0,0 +1,119 @@
package com.github.gotify;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import androidx.annotation.NonNull;
import com.squareup.picasso.Picasso;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.core.CorePlugin;
import io.noties.markwon.core.CoreProps;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.ext.tables.TableAwareMovementMethod;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.image.picasso.PicassoImagesPlugin;
import io.noties.markwon.movement.MovementMethodPlugin;
import java.util.Collections;
import org.commonmark.ext.gfm.tables.TableCell;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Code;
import org.commonmark.node.Emphasis;
import org.commonmark.node.Heading;
import org.commonmark.node.Link;
import org.commonmark.node.ListItem;
import org.commonmark.node.StrongEmphasis;
import org.commonmark.parser.Parser;
public class MarkwonFactory {
public static Markwon createForMessage(Context context, Picasso picasso) {
return Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(MovementMethodPlugin.create(TableAwareMovementMethod.create()))
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.build();
}
public static Markwon createForNotification(Context context, Picasso picasso) {
final float[] headingSizes = {
2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
};
final int bulletGapWidth =
(int) (8 * context.getResources().getDisplayMetrics().density + 0.5F);
return Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(
new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(
@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(
Heading.class,
(configuration, props) ->
new Object[] {
new RelativeSizeSpan(
headingSizes[
CoreProps.HEADING_LEVEL
.require(
props)
- 1]),
new StyleSpan(Typeface.BOLD)
})
.setFactory(
Emphasis.class,
(configuration, props) ->
new StyleSpan(Typeface.ITALIC))
.setFactory(
StrongEmphasis.class,
(configuration, props) ->
new StyleSpan(Typeface.BOLD))
.setFactory(
BlockQuote.class,
(configuration, props) -> new QuoteSpan())
.setFactory(
Code.class,
(configuration, props) ->
new Object[] {
new BackgroundColorSpan(Color.LTGRAY),
new TypefaceSpan("monospace")
})
.setFactory(
ListItem.class,
(configuration, props) ->
new BulletSpan(bulletGapWidth))
.setFactory(Link.class, ((configuration, props) -> null));
}
@Override
public void configureParser(@NonNull Parser.Builder builder) {
builder.extensions(Collections.singleton(TablesExtension.create()));
}
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(
TableCell.class,
(visitor, node) -> {
visitor.visitChildren(node);
visitor.builder().append(' ');
});
}
})
.build();
}
}

View File

@@ -7,11 +7,15 @@ public final class Extras {
private Extras() {} private Extras() {}
public static boolean useMarkdown(Message message) { public static boolean useMarkdown(Message message) {
if (message.getExtras() == null) { return useMarkdown(message.getExtras());
}
public static boolean useMarkdown(Map<String, Object> extras) {
if (extras == null) {
return false; return false;
} }
Object display = message.getExtras().get("client::display"); Object display = extras.get("client::display");
if (!(display instanceof Map)) { if (!(display instanceof Map)) {
return false; return false;
} }

View File

@@ -18,6 +18,7 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import com.github.gotify.MarkwonFactory;
import com.github.gotify.R; import com.github.gotify.R;
import com.github.gotify.Settings; import com.github.gotify.Settings;
import com.github.gotify.Utils; import com.github.gotify.Utils;
@@ -25,12 +26,6 @@ import com.github.gotify.client.model.Message;
import com.github.gotify.messages.provider.MessageWithImage; import com.github.gotify.messages.provider.MessageWithImage;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import io.noties.markwon.Markwon; import io.noties.markwon.Markwon;
import io.noties.markwon.core.CorePlugin;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.ext.tables.TableAwareMovementMethod;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.image.picasso.PicassoImagesPlugin;
import io.noties.markwon.movement.MovementMethodPlugin;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -61,14 +56,7 @@ public class ListMessageAdapter extends RecyclerView.Adapter<ListMessageAdapter.
this.items = items; this.items = items;
this.delete = delete; this.delete = delete;
this.markwon = this.markwon = MarkwonFactory.createForMessage(context, picasso);
Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(MovementMethodPlugin.create(TableAwareMovementMethod.create()))
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.build();
TIME_FORMAT_RELATIVE = TIME_FORMAT_RELATIVE =
context.getResources().getString(R.string.time_format_value_relative); context.getResources().getString(R.string.time_format_value_relative);

View File

@@ -17,6 +17,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.github.gotify.MarkwonFactory;
import com.github.gotify.MissedMessageUtil; import com.github.gotify.MissedMessageUtil;
import com.github.gotify.NotificationSupport; import com.github.gotify.NotificationSupport;
import com.github.gotify.R; import com.github.gotify.R;
@@ -31,6 +32,7 @@ import com.github.gotify.log.UncaughtExceptionHandler;
import com.github.gotify.messages.Extras; import com.github.gotify.messages.Extras;
import com.github.gotify.messages.MessagesActivity; import com.github.gotify.messages.MessagesActivity;
import com.github.gotify.picasso.PicassoHandler; import com.github.gotify.picasso.PicassoHandler;
import io.noties.markwon.Markwon;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -51,6 +53,7 @@ public class WebSocketService extends Service {
private MissedMessageUtil missingMessageUtil; private MissedMessageUtil missingMessageUtil;
private PicassoHandler picassoHandler; private PicassoHandler picassoHandler;
private Markwon markwon;
@Override @Override
public void onCreate() { public void onCreate() {
@@ -61,6 +64,7 @@ public class WebSocketService extends Service {
missingMessageUtil = new MissedMessageUtil(client.createService(MessageApi.class)); missingMessageUtil = new MissedMessageUtil(client.createService(MessageApi.class));
Log.i("Create " + getClass().getSimpleName()); Log.i("Create " + getClass().getSimpleName());
picassoHandler = new PicassoHandler(this, settings); picassoHandler = new PicassoHandler(this, settings);
markwon = MarkwonFactory.createForNotification(this, picassoHandler.get());
} }
@Override @Override
@@ -297,13 +301,19 @@ public class WebSocketService extends Service {
.setTicker(getString(R.string.app_name) + " - " + title) .setTicker(getString(R.string.app_name) + " - " + title)
.setGroup(NotificationSupport.Group.MESSAGES) .setGroup(NotificationSupport.Group.MESSAGES)
.setContentTitle(title) .setContentTitle(title)
.setContentText(message)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND) .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND)
.setLights(Color.CYAN, 1000, 5000) .setLights(Color.CYAN, 1000, 5000)
.setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary))
.setContentIntent(contentIntent); .setContentIntent(contentIntent);
CharSequence formattedMessage = message;
if (Extras.useMarkdown(extras)) {
formattedMessage = markwon.toMarkdown(message);
message = formattedMessage.toString();
}
b.setContentText(message);
b.setStyle(new NotificationCompat.BigTextStyle().bigText(formattedMessage));
NotificationManager notificationManager = NotificationManager notificationManager =
(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(Utils.longToInt(id), b.build()); notificationManager.notify(Utils.longToInt(id), b.build());