Add remote views sample

This commit is contained in:
Dimitry Ivanov 2020-03-12 10:25:18 +03:00
parent 0b813e43f7
commit c425773c84
10 changed files with 278 additions and 1 deletions

View File

@ -37,6 +37,7 @@
<activity android:name=".htmldetails.HtmlDetailsActivity" />
<activity android:name=".tasklist.TaskListActivity" />
<activity android:name=".images.ImagesActivity" />
<activity android:name=".notification.NotificationActivity" />
</application>

View File

@ -28,6 +28,7 @@ import io.noties.markwon.sample.htmldetails.HtmlDetailsActivity;
import io.noties.markwon.sample.images.ImagesActivity;
import io.noties.markwon.sample.inlineparser.InlineParserActivity;
import io.noties.markwon.sample.latex.LatexActivity;
import io.noties.markwon.sample.notification.NotificationActivity;
import io.noties.markwon.sample.precomputed.PrecomputedActivity;
import io.noties.markwon.sample.recycler.RecyclerActivity;
import io.noties.markwon.sample.simpleext.SimpleExtActivity;
@ -142,6 +143,10 @@ public class MainActivity extends Activity {
activity = ImagesActivity.class;
break;
case REMOTE_VIEWS:
activity = NotificationActivity.class;
break;
default:
throw new IllegalStateException("No Activity is associated with sample-item: " + item);
}

View File

@ -31,7 +31,9 @@ public enum Sample {
TASK_LIST(R.string.sample_task_list),
IMAGES(R.string.sample_images);
IMAGES(R.string.sample_images),
REMOTE_VIEWS(R.string.sample_remote_views);
private final int textResId;

View File

@ -0,0 +1,254 @@
package io.noties.markwon.sample.notification;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.os.Build;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BulletSpan;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ImageSpan;
import android.text.style.QuoteSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import androidx.annotation.NonNull;
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Emphasis;
import org.commonmark.node.Heading;
import org.commonmark.node.ListItem;
import org.commonmark.node.StrongEmphasis;
import io.noties.debug.Debug;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.core.CoreProps;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.sample.ActivityWithMenuOptions;
import io.noties.markwon.sample.MenuOptions;
import io.noties.markwon.sample.R;
public class NotificationActivity extends ActivityWithMenuOptions {
private static final String CHANNEL_ID = "whatever";
@NonNull
@Override
public MenuOptions menuOptions() {
return MenuOptions.create()
.add("bold-italic", this::bold_italic)
.add("heading", this::heading)
.add("lists", this::lists)
.add("image", this::image)
.add("link", this::link)
.add("blockquote", this::blockquote)
.add("strikethrough", this::strikethrough);
}
private void bold_italic() {
// Unfortunately we cannot just use Markwon created CharSequence in a RemoteViews context
// because it requires for spans to be platform ones
final String md = "Just a **bold** here and _italic_, but what if **it is bold _and italic_**?";
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder
.setFactory(StrongEmphasis.class, (configuration, props) -> new StyleSpan(Typeface.BOLD))
.setFactory(Emphasis.class, (configuration, props) -> new StyleSpan(Typeface.ITALIC));
}
})
.build();
display(markwon.toMarkdown(md));
}
private void heading() {
// please note that heading doesn't seem to be working in remote views,
// tried both `RelativeSizeSpan` and `AbsoluteSizeSpan` with no effect
final float base = 12;
final float[] sizes = {
2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
};
final String md = "" +
"# H1\n" +
"## H2\n" +
"### H3\n" +
"#### H4\n" +
"##### H5\n" +
"###### H6\n\n";
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(Heading.class, (configuration, props) -> {
final Integer level = CoreProps.HEADING_LEVEL.get(props);
Debug.i(level);
if (level != null && level > 0 && level <= sizes.length) {
// return new RelativeSizeSpan(sizes[level - 1]);
final Object span = new AbsoluteSizeSpan((int) (base * sizes[level - 1] + .5F), true);
return new Object[]{
span,
new StyleSpan(Typeface.BOLD)
};
}
return null;
});
}
})
.build();
display(markwon.toMarkdown(md));
}
private void lists() {
final String md = "" +
"* bullet 1\n" +
"* bullet 2\n" +
"* * bullet 2 1\n" +
" * bullet 2 0 1\n" +
"1) order 1\n" +
"1) order 2\n" +
"1) order 3\n";
// ordered lists _could_ be translated to raw text representation (`1.`, `1)` etc) in resulting markdown
// or they could be _disabled_ all together... (can ordered lists be disabled in parser?)
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(ListItem.class, (configuration, props) -> {
final CoreProps.ListItemType type = CoreProps.LIST_ITEM_TYPE.get(props);
if (type != null) {
// bullet and ordered list share the same markdown node
return new BulletSpan();
}
return null;
});
}
})
.build();
display(markwon.toMarkdown(md));
}
private void image() {
// please note that image _could_ be supported only if it would be available immediately
// debugging possibility
//
// doesn't seem to be working
final Bitmap bitmap = Bitmap.createBitmap(128, 256, Bitmap.Config.ARGB_4444);
final Canvas canvas = new Canvas(bitmap);
canvas.drawColor(0xFFAD1457);
final SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append("An image: ");
final int length = builder.length();
builder.append("[bitmap]");
builder.setSpan(
new ImageSpan(this, bitmap, DynamicDrawableSpan.ALIGN_BOTTOM),
length,
builder.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
builder.append(" okay, and ");
final int start = builder.length();
builder.append("[resource]");
builder.setSpan(
new ImageSpan(this, R.drawable.ic_memory_black_48dp),
start,
builder.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
display(builder);
}
private void link() {
final String md = "" +
"[a link](https://isa.link/) is here, styling yes, clicking - no";
display(Markwon.create(this).toMarkdown(md));
}
private void blockquote() {
final String md = "" +
"> This was once said by me\n" +
"> > And this one also\n\n" +
"Me";
final Markwon markwon = Markwon.builder(this)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(BlockQuote.class, (configuration, props) -> new QuoteSpan());
}
})
.build();
display(markwon.toMarkdown(md));
}
private void strikethrough() {
final String md = "~~strike that!~~";
final Markwon markwon = Markwon.builder(this)
.usePlugin(new StrikethroughPlugin())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.setFactory(Strikethrough.class, (configuration, props) -> new StrikethroughSpan());
}
})
.build();
display(markwon.toMarkdown(md));
}
private void display(@NonNull CharSequence cs) {
final NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager == null) {
throw new IllegalStateException("No NotificationManager is available");
}
ensureChannel(manager);
final Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle("Markwon")
.setContentText(cs)
.setStyle(new Notification.BigTextStyle().bigText(cs));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
manager.notify(1, builder.build());
}
private void ensureChannel(@NonNull NotificationManager manager) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
final NotificationChannel channel = manager.getNotificationChannel(CHANNEL_ID);
if (channel == null) {
manager.createNotificationChannel(new NotificationChannel(
CHANNEL_ID,
CHANNEL_ID,
NotificationManager.IMPORTANCE_DEFAULT));
}
}
}

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956"
android:tint="#FFFFFF">
<group android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FF000000"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z"/>
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -35,4 +35,6 @@
<string name="sample_images"># \# Images\n\nUsage of different images plugins</string>
<string name="sample_remote_views"># \# Notification\n\nExample usage in notifications and other remote views</string>
</resources>