Add markwon-recycler module (work in progress)
@ -49,6 +49,8 @@ ext {
|
|||||||
'push-aar-gradle': 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle'
|
'push-aar-gradle': 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// for now 27.1.1 is used because it's the last one distributed with source files
|
||||||
|
// next version with sources is androidx one (we wait until migration)
|
||||||
final def supportVersion = '27.1.1'
|
final def supportVersion = '27.1.1'
|
||||||
final def commonMarkVersion = '0.12.1'
|
final def commonMarkVersion = '0.12.1'
|
||||||
final def daggerVersion = '2.10'
|
final def daggerVersion = '2.10'
|
||||||
@ -56,6 +58,7 @@ ext {
|
|||||||
deps = [
|
deps = [
|
||||||
'support-annotations' : "com.android.support:support-annotations:$supportVersion",
|
'support-annotations' : "com.android.support:support-annotations:$supportVersion",
|
||||||
'support-app-compat' : "com.android.support:appcompat-v7:$supportVersion",
|
'support-app-compat' : "com.android.support:appcompat-v7:$supportVersion",
|
||||||
|
'support-recycler-view' : "com.android.support:recyclerview-v7:$supportVersion",
|
||||||
'commonmark' : "com.atlassian.commonmark:commonmark:$commonMarkVersion",
|
'commonmark' : "com.atlassian.commonmark:commonmark:$commonMarkVersion",
|
||||||
'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion",
|
'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion",
|
||||||
'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion",
|
'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion",
|
||||||
|
@ -7,8 +7,6 @@ import android.text.TextUtils;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import ru.noties.markwon.renderer.R;
|
|
||||||
|
|
||||||
abstract class TableRowsScheduler {
|
abstract class TableRowsScheduler {
|
||||||
|
|
||||||
static void schedule(@NonNull final TextView view) {
|
static void schedule(@NonNull final TextView view) {
|
||||||
|
6
markwon-ext-tables/src/main/res/values/ids.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<item name="markwon_tables_scheduler" type="id" />
|
||||||
|
|
||||||
|
</resources>
|
25
markwon-recycler/build.gradle
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
|
||||||
|
compileSdkVersion config['compile-sdk']
|
||||||
|
buildToolsVersion config['build-tools']
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion config['min-sdk']
|
||||||
|
targetSdkVersion config['target-sdk']
|
||||||
|
versionCode 1
|
||||||
|
versionName version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
api project(':markwon')
|
||||||
|
|
||||||
|
deps.with {
|
||||||
|
api it['support-recycler-view']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerArtifact(this)
|
1
markwon-recycler/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="ru.noties.markwon.recycler" />
|
@ -0,0 +1,110 @@
|
|||||||
|
package ru.noties.markwon.recycler;
|
||||||
|
|
||||||
|
import android.support.annotation.IdRes;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
|
||||||
|
// each node block will be rendered by a simple TextView, but we can provide own entries for each block
|
||||||
|
public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter.Holder> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Builder builder() {
|
||||||
|
return new MarkwonAdapterImpl.BuilderImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonAdapter create() {
|
||||||
|
return new MarkwonAdapterImpl.BuilderImpl().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for an adapter with only one entry (all blocks are rendered the same with this entry)
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonAdapter create(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry) {
|
||||||
|
return new MarkwonAdapterImpl.BuilderImpl().defaultEntry(defaultEntry).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MarkwonAdapter create(@LayoutRes int layoutResId) {
|
||||||
|
return new MarkwonAdapterImpl.BuilderImpl().defaultEntry(layoutResId).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Builder {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
<N extends Node> Builder include(
|
||||||
|
@NonNull Class<N> node,
|
||||||
|
@NonNull Entry<? extends Holder, ? super N> entry);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Builder defaultEntry(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Builder defaultEntry(@LayoutRes int layoutResId);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Builder reducer(@NonNull Reducer reducer);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
MarkwonAdapter build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Entry<H extends Holder, N extends Node> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
H createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent);
|
||||||
|
|
||||||
|
void bindHolder(@NonNull Markwon markwon, @NonNull H holder, @NonNull N node);
|
||||||
|
|
||||||
|
long id(@NonNull N node);
|
||||||
|
|
||||||
|
// will be called when new content is available (clear internal cache if any)
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Reducer {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
List<Node> reduce(@NonNull Node root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setMarkdown(@NonNull Markwon markwon, @NonNull String markdown);
|
||||||
|
|
||||||
|
public abstract void setParsedMarkdown(@NonNull Markwon markwon, @NonNull Node document);
|
||||||
|
|
||||||
|
public abstract void setParsedMarkdown(@NonNull Markwon markwon, @NonNull List<Node> nodes);
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public static class Holder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
public Holder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// please note that this method should be called after constructor
|
||||||
|
@Nullable
|
||||||
|
protected <V extends View> V findView(@IdRes int id) {
|
||||||
|
return itemView.findViewById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// please note that this method should be called after constructor
|
||||||
|
@NonNull
|
||||||
|
protected <V extends View> V requireView(@IdRes int id) {
|
||||||
|
final V v = itemView.findViewById(id);
|
||||||
|
if (v == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
package ru.noties.markwon.recycler;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
|
||||||
|
class MarkwonAdapterImpl extends MarkwonAdapter {
|
||||||
|
|
||||||
|
private final SparseArray<Entry<Holder, Node>> entries;
|
||||||
|
private final Entry<Holder, Node> defaultEntry;
|
||||||
|
private final Reducer reducer;
|
||||||
|
|
||||||
|
private LayoutInflater layoutInflater;
|
||||||
|
|
||||||
|
private Markwon markwon;
|
||||||
|
private List<Node> nodes;
|
||||||
|
|
||||||
|
MarkwonAdapterImpl(
|
||||||
|
@NonNull SparseArray<Entry<Holder, Node>> entries,
|
||||||
|
@NonNull Entry<Holder, Node> defaultEntry,
|
||||||
|
@NonNull Reducer reducer) {
|
||||||
|
this.entries = entries;
|
||||||
|
this.defaultEntry = defaultEntry;
|
||||||
|
this.reducer = reducer;
|
||||||
|
setHasStableIds(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMarkdown(@NonNull Markwon markwon, @NonNull String markdown) {
|
||||||
|
setParsedMarkdown(markwon, markwon.parse(markdown));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParsedMarkdown(@NonNull Markwon markwon, @NonNull Node document) {
|
||||||
|
setParsedMarkdown(markwon, reducer.reduce(document));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParsedMarkdown(@NonNull Markwon markwon, @NonNull List<Node> nodes) {
|
||||||
|
// clear all entries before applying
|
||||||
|
|
||||||
|
defaultEntry.clear();
|
||||||
|
|
||||||
|
for (int i = 0, size = entries.size(); i < size; i++) {
|
||||||
|
entries.valueAt(i).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markwon = markwon;
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
|
||||||
|
if (layoutInflater == null) {
|
||||||
|
layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Entry<Holder, Node> entry = viewType == 0
|
||||||
|
? defaultEntry
|
||||||
|
: entries.get(viewType);
|
||||||
|
|
||||||
|
return entry.createHolder(layoutInflater, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull Holder holder, int position) {
|
||||||
|
|
||||||
|
final Node node = nodes.get(position);
|
||||||
|
final int viewType = getNodeViewType(node.getClass());
|
||||||
|
|
||||||
|
final Entry<Holder, Node> entry = viewType == 0
|
||||||
|
? defaultEntry
|
||||||
|
: entries.get(viewType);
|
||||||
|
|
||||||
|
entry.bindHolder(markwon, holder, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return nodes != null
|
||||||
|
? nodes.size()
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<Node> getItems() {
|
||||||
|
return nodes != null
|
||||||
|
? Collections.unmodifiableList(nodes)
|
||||||
|
: Collections.<Node>emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return getNodeViewType(nodes.get(position).getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
final Node node = nodes.get(position);
|
||||||
|
final int type = getNodeViewType(node.getClass());
|
||||||
|
final Entry<Holder, Node> entry = type == 0
|
||||||
|
? defaultEntry
|
||||||
|
: entries.get(type);
|
||||||
|
return entry.id(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNodeViewType(@NonNull Class<? extends Node> node) {
|
||||||
|
// if has registered -> then return it, else 0
|
||||||
|
final int hash = node.hashCode();
|
||||||
|
if (entries.indexOfKey(hash) > -1) {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BuilderImpl implements Builder {
|
||||||
|
|
||||||
|
private final SparseArray<Entry<Holder, Node>> entries = new SparseArray<>(3);
|
||||||
|
|
||||||
|
private Entry<Holder, Node> defaultEntry;
|
||||||
|
private Reducer reducer;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public <N extends Node> Builder include(
|
||||||
|
@NonNull Class<N> node,
|
||||||
|
@NonNull Entry<? extends Holder, ? super N> entry) {
|
||||||
|
//noinspection unchecked
|
||||||
|
entries.append(node.hashCode(), (Entry<Holder, Node>) entry);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Builder defaultEntry(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry) {
|
||||||
|
//noinspection unchecked
|
||||||
|
this.defaultEntry = (Entry<Holder, Node>) defaultEntry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Builder defaultEntry(int layoutResId) {
|
||||||
|
//noinspection unchecked
|
||||||
|
this.defaultEntry = (Entry<Holder, Node>) (Entry) new SimpleNodeEntry(layoutResId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Builder reducer(@NonNull Reducer reducer) {
|
||||||
|
this.reducer = reducer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MarkwonAdapter build() {
|
||||||
|
|
||||||
|
if (defaultEntry == null) {
|
||||||
|
//noinspection unchecked
|
||||||
|
defaultEntry = (Entry<Holder, Node>) (Entry) new SimpleNodeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reducer == null) {
|
||||||
|
reducer = new ReducerImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MarkwonAdapterImpl(entries, defaultEntry, reducer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReducerImpl implements Reducer {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public List<Node> reduce(@NonNull Node root) {
|
||||||
|
|
||||||
|
final List<Node> list = new ArrayList<>();
|
||||||
|
|
||||||
|
// // we will extract all blocks that are direct children of Document
|
||||||
|
Node node = root.getFirstChild();
|
||||||
|
Node temp;
|
||||||
|
|
||||||
|
while (node != null) {
|
||||||
|
list.add(node);
|
||||||
|
temp = node.getNext();
|
||||||
|
node.unlink();
|
||||||
|
node = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.e("NODES", list.toString());
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package ru.noties.markwon.recycler;
|
||||||
|
|
||||||
|
public class MarkwonRecycler {
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package ru.noties.markwon.recycler;
|
||||||
|
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
|
||||||
|
public class SimpleNodeEntry implements MarkwonAdapter.Entry<SimpleNodeEntry.Holder, Node> {
|
||||||
|
|
||||||
|
private static final NoCopySpannableFactory FACTORY = new NoCopySpannableFactory();
|
||||||
|
|
||||||
|
// small cache, maybe add pre-compute of text, also spannableFactory (so no copying of spans)
|
||||||
|
private final Map<Node, Spanned> cache = new HashMap<>();
|
||||||
|
|
||||||
|
private final int layoutResId;
|
||||||
|
|
||||||
|
public SimpleNodeEntry() {
|
||||||
|
this(R.layout.adapter_simple_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleNodeEntry(@LayoutRes int layoutResId) {
|
||||||
|
this.layoutResId = layoutResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Holder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
|
||||||
|
return new Holder(inflater.inflate(layoutResId, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindHolder(@NonNull Markwon markwon, @NonNull Holder holder, @NonNull Node node) {
|
||||||
|
Spanned spanned = cache.get(node);
|
||||||
|
if (spanned == null) {
|
||||||
|
spanned = markwon.render(node);
|
||||||
|
cache.put(node, spanned);
|
||||||
|
}
|
||||||
|
markwon.setParsedMarkdown(holder.textView, spanned);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long id(@NonNull Node node) {
|
||||||
|
return node.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Holder extends MarkwonAdapter.Holder {
|
||||||
|
|
||||||
|
final TextView textView;
|
||||||
|
|
||||||
|
Holder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
this.textView = requireView(R.id.text);
|
||||||
|
this.textView.setSpannableFactory(FACTORY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NoCopySpannableFactory extends Spannable.Factory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spannable newSpannable(CharSequence source) {
|
||||||
|
return source instanceof Spannable
|
||||||
|
? (Spannable) source
|
||||||
|
: new SpannableString(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dip"
|
||||||
|
android:layout_marginRight="16dip"
|
||||||
|
android:lineSpacingExtra="2dip"
|
||||||
|
android:paddingTop="8dip"
|
||||||
|
android:paddingBottom="8dip"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="# Hello there!" />
|
@ -2,6 +2,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<item name="markwon_drawables_scheduler" type="id" />
|
<item name="markwon_drawables_scheduler" type="id" />
|
||||||
<item name="markwon_tables_scheduler" type="id" />
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -19,4 +19,11 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':markwon')
|
implementation project(':markwon')
|
||||||
|
|
||||||
|
implementation project(':markwon-image-svg')
|
||||||
|
implementation project(':markwon-recycler')
|
||||||
|
implementation project(':markwon-ext-tables')
|
||||||
|
implementation project(':markwon-html')
|
||||||
|
|
||||||
|
implementation deps['debug']
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="ru.noties.markwon.sample.extension">
|
package="ru.noties.markwon.sample.extension">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
|
||||||
|
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -16,6 +18,11 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".recycler.MarkwonRecyclerActivity"
|
||||||
|
android:exported="true" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
313
sample-custom-extension/src/main/assets/README.md
Normal file
@ -0,0 +1,313 @@
|
|||||||
|

|
||||||
|
|
||||||
|
# Markwon
|
||||||
|
|
||||||
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon%22)
|
||||||
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-image-loader%22)
|
||||||
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22)
|
||||||
|
[](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-view%22)
|
||||||
|
|
||||||
|
[](https://travis-ci.org/noties/Markwon)
|
||||||
|
|
||||||
|
**Markwon** is a markdown library for Android. It parses markdown
|
||||||
|
following [commonmark-spec] with the help of amazing [commonmark-java]
|
||||||
|
library and renders result as _Android-native_ Spannables. **No HTML**
|
||||||
|
is involved as an intermediate step. <u>**No WebView** is required</u>.
|
||||||
|
It's extremely fast, feature-rich and extensible.
|
||||||
|
|
||||||
|
It gives ability to display markdown in all TextView widgets
|
||||||
|
(**TextView**, **Button**, **Switch**, **CheckBox**, etc), **Toasts**
|
||||||
|
and all other places that accept **Spanned content**. Library provides
|
||||||
|
reasonable defaults to display style of a markdown content but also
|
||||||
|
gives all the means to tweak the appearance if desired. All markdown
|
||||||
|
features listed in [commonmark-spec] are supported
|
||||||
|
(including support for **inlined/block HTML code**, **markdown tables**,
|
||||||
|
**images** and **syntax highlight**).
|
||||||
|
|
||||||
|
[commonmark-spec]: https://spec.commonmark.org/0.28/
|
||||||
|
[commonmark-java]: https://github.com/atlassian/commonmark-java/blob/master/README.md
|
||||||
|
|
||||||
|
<sup>*</sup>*This file is displayed by default in the [sample-apk] (`markwon-sample-{latest-version}-debug.apk`) application. Which is a generic markdown viewer with support to display markdown via `http`, `https` & `file` schemes and 2 themes included: Light & Dark*
|
||||||
|
|
||||||
|
[sample-apk]: https://github.com/noties/Markwon/releases
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```groovy
|
||||||
|
implementation "ru.noties:markwon:${markwonVersion}"
|
||||||
|
implementation "ru.noties:markwon-image-loader:${markwonVersion}" // optional
|
||||||
|
implementation "ru.noties:markwon-syntax-highlight:${markwonVersion}" // optional
|
||||||
|
implementation "ru.noties:markwon-view:${markwonVersion}" // optional
|
||||||
|
```
|
||||||
|
|
||||||
|
Please visit [documentation] web-site for further reference
|
||||||
|
|
||||||
|
## Supported markdown features:
|
||||||
|
* Emphasis (`*`, `_`)
|
||||||
|
* Strong emphasis (`**`, `__`)
|
||||||
|
* Strike-through (`~~`)
|
||||||
|
* Headers (`#{1,6}`)
|
||||||
|
* Links (`[]()` && `[][]`)
|
||||||
|
* Images
|
||||||
|
* Thematic break (`---`, `***`, `___`)
|
||||||
|
* Quotes & nested quotes (`>{1,}`)
|
||||||
|
* Ordered & non-ordered lists & nested ones
|
||||||
|
* Inline code
|
||||||
|
* Code blocks
|
||||||
|
* Tables (*with limitations*)
|
||||||
|
* Syntax highlight
|
||||||
|
* HTML
|
||||||
|
* Emphasis (`<i>`, `<em>`, `<cite>`, `<dfn>`)
|
||||||
|
* Strong emphasis (`<b>`, `<strong>`)
|
||||||
|
* SuperScript (`<sup>`)
|
||||||
|
* SubScript (`<sub>`)
|
||||||
|
* Underline (`<u>`, `ins`)
|
||||||
|
* Strike-through (`<s>`, `<strike>`, `<del>`)
|
||||||
|
* Link (`a`)
|
||||||
|
* Lists (`ul`, `ol`)
|
||||||
|
* Images (`img` will require configured image loader)
|
||||||
|
* Blockquote (`blockquote`)
|
||||||
|
* Heading (`h1`, `h2`, `h3`, `h4`, `h5`, `h6`)
|
||||||
|
* there is support to render any HTML tag
|
||||||
|
* Task lists:
|
||||||
|
- [ ] Not _done_
|
||||||
|
- [X] **Done** with `X`
|
||||||
|
- [x] ~~and~~ **or** small `x`
|
||||||
|
---
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Taken with default configuration (except for image loading):
|
||||||
|
|
||||||
|
<a href="./art/mw_light_01.png"><img src="./art/mw_light_01.png" width="30%" /></a>
|
||||||
|
<a href="./art/mw_light_02.png"><img src="./art/mw_light_02.png" width="30%" /></a>
|
||||||
|
<a href="./art/mw_light_03.png"><img src="./art/mw_light_03.png" width="30%" /></a>
|
||||||
|
<a href="./art/mw_dark_01.png"><img src="./art/mw_dark_01.png" width="30%" /></a>
|
||||||
|
|
||||||
|
By default configuration uses TextView textColor for styling, so changing textColor changes style
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Please visit [documentation] web-site for reference
|
||||||
|
|
||||||
|
[documentation]: https://noties.github.io/Markwon
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Applications using Markwon
|
||||||
|
|
||||||
|
* [Partiko](https://partiko.app)
|
||||||
|
* [FairNote Notepad](https://play.google.com/store/apps/details?id=com.rgiskard.fairnote)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Demo
|
||||||
|
Based on [this cheatsheet][cheatsheet]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Headers
|
||||||
|
---
|
||||||
|
# Header 1
|
||||||
|
## Header 2
|
||||||
|
### Header 3
|
||||||
|
#### Header 4
|
||||||
|
##### Header 5
|
||||||
|
###### Header 6
|
||||||
|
---
|
||||||
|
|
||||||
|
## Emphasis
|
||||||
|
|
||||||
|
Emphasis, aka italics, with *asterisks* or _underscores_.
|
||||||
|
|
||||||
|
Strong emphasis, aka bold, with **asterisks** or __underscores__.
|
||||||
|
|
||||||
|
Combined emphasis with **asterisks and _underscores_**.
|
||||||
|
|
||||||
|
Strikethrough uses two tildes. ~~Scratch this.~~
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
1. First ordered list item
|
||||||
|
2. Another item
|
||||||
|
* Unordered sub-list.
|
||||||
|
1. Actual numbers don't matter, just that it's a number
|
||||||
|
1. Ordered sub-list
|
||||||
|
4. And another item.
|
||||||
|
|
||||||
|
You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown).
|
||||||
|
|
||||||
|
To have a line break without a paragraph, you will need to use two trailing spaces.
|
||||||
|
Note that this line is separate, but within the same paragraph.
|
||||||
|
(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.)
|
||||||
|
|
||||||
|
* Unordered list can use asterisks
|
||||||
|
- Or minuses
|
||||||
|
+ Or pluses
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
[I'm an inline-style link](https://www.google.com)
|
||||||
|
|
||||||
|
[I'm a reference-style link][Arbitrary case-insensitive reference text]
|
||||||
|
|
||||||
|
[I'm a relative reference to a repository file](../blob/master/LICENSE)
|
||||||
|
|
||||||
|
[You can use numbers for reference-style link definitions][1]
|
||||||
|
|
||||||
|
Or leave it empty and use the [link text itself].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
Inline `code` has `back-ticks around` it.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var s = "JavaScript syntax highlighting";
|
||||||
|
alert(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
s = "Python syntax highlighting"
|
||||||
|
print s
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* Helper method to obtain a Parser with registered strike-through & table extensions
|
||||||
|
* & task lists (added in 1.0.1)
|
||||||
|
*
|
||||||
|
* @return a Parser instance that is supported by this library
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static Parser createParser() {
|
||||||
|
return new Parser.Builder()
|
||||||
|
.extensions(Arrays.asList(
|
||||||
|
StrikethroughExtension.create(),
|
||||||
|
TablesExtension.create(),
|
||||||
|
TaskListExtension.create()
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scroll_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="?android:attr/actionBarSize">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dip"
|
||||||
|
android:lineSpacingExtra="2dip"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:context="ru.noties.markwon.MainActivity"
|
||||||
|
tools:text="yo\nman" />
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
No language indicated, so no syntax highlighting.
|
||||||
|
But let's throw in a <b>tag</b>.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
Colons can be used to align columns.
|
||||||
|
|
||||||
|
| Tables | Are | Cool |
|
||||||
|
| ------------- |:-------------:| -----:|
|
||||||
|
| col 3 is | right-aligned | $1600 |
|
||||||
|
| col 2 is | centered | $12 |
|
||||||
|
| zebra stripes | are neat | $1 |
|
||||||
|
|
||||||
|
There must be at least 3 dashes separating each header cell.
|
||||||
|
The outer pipes (|) are optional, and you don't need to make the
|
||||||
|
raw Markdown line up prettily. You can also use inline Markdown.
|
||||||
|
|
||||||
|
Markdown | Less | Pretty
|
||||||
|
--- | --- | ---
|
||||||
|
*Still* | `renders` | **nicely**
|
||||||
|
1 | 2 | 3
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blockquotes
|
||||||
|
|
||||||
|
> Blockquotes are very handy in email to emulate reply text.
|
||||||
|
> This line is part of the same quote.
|
||||||
|
|
||||||
|
Quote break.
|
||||||
|
|
||||||
|
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
|
||||||
|
|
||||||
|
Nested quotes
|
||||||
|
> Hello!
|
||||||
|
>> And to you!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inline HTML
|
||||||
|
|
||||||
|
```html
|
||||||
|
<u><i>H<sup>T<sub>M</sub></sup><b><s>L</s></b></i></u>
|
||||||
|
```
|
||||||
|
|
||||||
|
<u><i>H<sup>T<sub>M</sub></sup><b><s>L</s></b></i></u>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Horizontal Rule
|
||||||
|
|
||||||
|
Three or more...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Hyphens (`-`)
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
Asterisks (`*`)
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
Underscores (`_`)
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright 2017 Dimitry Ivanov (mail@dimitryivanov.ru)
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
```
|
||||||
|
|
||||||
|
[cheatsheet]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
|
||||||
|
|
||||||
|
[arbitrary case-insensitive reference text]: https://www.mozilla.org
|
||||||
|
[1]: http://slashdot.org
|
||||||
|
[link text itself]: http://www.reddit.com
|
@ -0,0 +1,143 @@
|
|||||||
|
package ru.noties.markwon.sample.extension.recycler;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||||
|
import org.commonmark.node.FencedCodeBlock;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
import ru.noties.debug.AndroidLogDebugOutput;
|
||||||
|
import ru.noties.debug.Debug;
|
||||||
|
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
import ru.noties.markwon.MarkwonConfiguration;
|
||||||
|
import ru.noties.markwon.core.CorePlugin;
|
||||||
|
import ru.noties.markwon.ext.tables.TablePlugin;
|
||||||
|
import ru.noties.markwon.html.HtmlPlugin;
|
||||||
|
import ru.noties.markwon.image.ImagesPlugin;
|
||||||
|
import ru.noties.markwon.image.svg.SvgPlugin;
|
||||||
|
import ru.noties.markwon.recycler.MarkwonAdapter;
|
||||||
|
import ru.noties.markwon.recycler.SimpleNodeEntry;
|
||||||
|
import ru.noties.markwon.sample.extension.R;
|
||||||
|
import ru.noties.markwon.urlprocessor.UrlProcessor;
|
||||||
|
import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
|
||||||
|
|
||||||
|
// we will create a standalone `sample` module with all these samples, right now this
|
||||||
|
// module has unrelated things
|
||||||
|
public class MarkwonRecyclerActivity extends Activity {
|
||||||
|
|
||||||
|
static {
|
||||||
|
Debug.init(new AndroidLogDebugOutput(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_recycler);
|
||||||
|
|
||||||
|
final MarkwonAdapter adapter = MarkwonAdapter.builder()
|
||||||
|
.include(FencedCodeBlock.class, new SimpleNodeEntry(R.layout.adapter_fenced_code_block))
|
||||||
|
.include(TableBlock.class, new TableNodeEntry())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final RecyclerView recyclerView = findViewById(R.id.recycler_view);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
recyclerView.setHasFixedSize(true);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
final Markwon markwon = markwon(this);
|
||||||
|
adapter.setMarkdown(markwon, loadReadMe(this));
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static Markwon markwon(@NonNull Context context) {
|
||||||
|
return Markwon.builder(context)
|
||||||
|
.usePlugin(CorePlugin.create())
|
||||||
|
.usePlugin(ImagesPlugin.createWithAssets(context))
|
||||||
|
.usePlugin(SvgPlugin.create(context.getResources()))
|
||||||
|
.usePlugin(TablePlugin.create(context))
|
||||||
|
.usePlugin(HtmlPlugin.create())
|
||||||
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
|
@Override
|
||||||
|
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||||
|
builder.urlProcessor(new UrlProcessorInitialReadme());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loadReadMe(@NonNull Context context) {
|
||||||
|
InputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = context.getAssets().open("README.md");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return readStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readStream(@Nullable InputStream inputStream) {
|
||||||
|
|
||||||
|
String out = null;
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
builder.append(line)
|
||||||
|
.append('\n');
|
||||||
|
}
|
||||||
|
out = builder.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UrlProcessorInitialReadme implements UrlProcessor {
|
||||||
|
|
||||||
|
private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/";
|
||||||
|
|
||||||
|
private final UrlProcessorRelativeToAbsolute processor
|
||||||
|
= new UrlProcessorRelativeToAbsolute(GITHUB_BASE);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String process(@NonNull String destination) {
|
||||||
|
String out;
|
||||||
|
final Uri uri = Uri.parse(destination);
|
||||||
|
if (TextUtils.isEmpty(uri.getScheme())) {
|
||||||
|
out = processor.process(destination);
|
||||||
|
} else {
|
||||||
|
out = destination;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,289 @@
|
|||||||
|
package ru.noties.markwon.sample.extension.recycler;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TableLayout;
|
||||||
|
import android.widget.TableRow;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||||
|
import org.commonmark.node.AbstractVisitor;
|
||||||
|
import org.commonmark.node.BlockQuote;
|
||||||
|
import org.commonmark.node.BulletList;
|
||||||
|
import org.commonmark.node.Code;
|
||||||
|
import org.commonmark.node.CustomBlock;
|
||||||
|
import org.commonmark.node.CustomNode;
|
||||||
|
import org.commonmark.node.Document;
|
||||||
|
import org.commonmark.node.Emphasis;
|
||||||
|
import org.commonmark.node.FencedCodeBlock;
|
||||||
|
import org.commonmark.node.HardLineBreak;
|
||||||
|
import org.commonmark.node.Heading;
|
||||||
|
import org.commonmark.node.HtmlBlock;
|
||||||
|
import org.commonmark.node.HtmlInline;
|
||||||
|
import org.commonmark.node.Image;
|
||||||
|
import org.commonmark.node.IndentedCodeBlock;
|
||||||
|
import org.commonmark.node.Link;
|
||||||
|
import org.commonmark.node.ListItem;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.node.OrderedList;
|
||||||
|
import org.commonmark.node.Paragraph;
|
||||||
|
import org.commonmark.node.SoftLineBreak;
|
||||||
|
import org.commonmark.node.StrongEmphasis;
|
||||||
|
import org.commonmark.node.Text;
|
||||||
|
import org.commonmark.node.ThematicBreak;
|
||||||
|
|
||||||
|
import ru.noties.debug.Debug;
|
||||||
|
import ru.noties.markwon.Markwon;
|
||||||
|
import ru.noties.markwon.recycler.MarkwonAdapter;
|
||||||
|
import ru.noties.markwon.sample.extension.R;
|
||||||
|
|
||||||
|
// do not use in real applications, this is just a showcase
|
||||||
|
public class TableNodeEntry implements MarkwonAdapter.Entry<TableNodeEntry.TableNodeHolder, TableBlock> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public TableNodeHolder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
|
||||||
|
return new TableNodeHolder(inflater.inflate(R.layout.adapter_table_block, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindHolder(@NonNull Markwon markwon, @NonNull TableNodeHolder holder, @NonNull TableBlock node) {
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
Debug.e("###############");
|
||||||
|
Debug.e("NODE: %s", node);
|
||||||
|
node.accept(new PrintVisitor());
|
||||||
|
Debug.e("NODE: %s", node);
|
||||||
|
Debug.e("###############");
|
||||||
|
}
|
||||||
|
|
||||||
|
final TableLayout layout = holder.layout;
|
||||||
|
layout.removeAllViews();
|
||||||
|
|
||||||
|
// each child represents a row (head or regular)
|
||||||
|
// first direct child is TableHead or TableBody
|
||||||
|
Node child = node.getFirstChild().getFirstChild();
|
||||||
|
Node temp;
|
||||||
|
|
||||||
|
while (child != null) {
|
||||||
|
Log.e("BIND-ROWS", String.valueOf(child));
|
||||||
|
temp = child.getNext();
|
||||||
|
addRow(markwon, layout, child);
|
||||||
|
child = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.e("BIND", String.valueOf(layout.getChildCount()));
|
||||||
|
if (true) {
|
||||||
|
final ViewGroup group = (ViewGroup) layout.getChildAt(0);
|
||||||
|
Log.e("BIND-GROUP", String.valueOf(group.getChildCount()));
|
||||||
|
for (int i = 0; i < group.getChildCount(); i++) {
|
||||||
|
Log.e("BIND-CHILD-" + i, String.valueOf(group.getChildAt(i)) + ", " + ((TextView) group.getChildAt(i)).getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRow(@NonNull Markwon markwon, @NonNull TableLayout layout, @NonNull Node node) {
|
||||||
|
|
||||||
|
final TableRow tableRow = new TableRow(layout.getContext());
|
||||||
|
// final TableRow.LayoutParams params = new TableRow.LayoutParams(100, 100);
|
||||||
|
tableRow.setBackgroundColor(0x80ff0000);
|
||||||
|
|
||||||
|
TextView textView;
|
||||||
|
RenderNode renderNode;
|
||||||
|
Node temp;
|
||||||
|
|
||||||
|
// each child in a row represents a cell
|
||||||
|
Node child = node.getFirstChild();
|
||||||
|
while (child != null) {
|
||||||
|
Log.e("BIND-CELL", String.valueOf(child));
|
||||||
|
textView = new TextView(layout.getContext());
|
||||||
|
textView.setLayoutParams(new TableRow.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
renderNode = new RenderNode();
|
||||||
|
temp = child.getNext();
|
||||||
|
copy(child, renderNode);
|
||||||
|
tableRow.addView(textView);
|
||||||
|
markwon.setParsedMarkdown(textView, markwon.render(renderNode));
|
||||||
|
child = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.addView(tableRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(@NonNull Node from, @NonNull Node to) {
|
||||||
|
Node child = from.getFirstChild();
|
||||||
|
Node temp;
|
||||||
|
while (child != null) {
|
||||||
|
Log.e("BIND-COPY", String.valueOf(child));
|
||||||
|
temp = child.getNext();
|
||||||
|
to.appendChild(child);
|
||||||
|
child = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long id(@NonNull TableBlock node) {
|
||||||
|
return node.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TableNodeHolder extends MarkwonAdapter.Holder {
|
||||||
|
|
||||||
|
final TableLayout layout;
|
||||||
|
|
||||||
|
TableNodeHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
this.layout = requireView(R.id.table_layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RenderNode extends CustomBlock {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PrintVisitor extends AbstractVisitor {
|
||||||
|
|
||||||
|
private final RenderNode renderNode = new RenderNode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BlockQuote blockQuote) {
|
||||||
|
Debug.i("blockQuote: %s", blockQuote);
|
||||||
|
super.visit(blockQuote);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BulletList bulletList) {
|
||||||
|
Debug.i("bulletList: %s", bulletList);
|
||||||
|
super.visit(bulletList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Code code) {
|
||||||
|
Debug.i("code: %s", code);
|
||||||
|
super.visit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Document document) {
|
||||||
|
Debug.i("document: %s", document);
|
||||||
|
super.visit(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Emphasis emphasis) {
|
||||||
|
Debug.i("emphasis: %s", emphasis);
|
||||||
|
super.visit(emphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(FencedCodeBlock fencedCodeBlock) {
|
||||||
|
Debug.i("fencedCodeBlock: %s", fencedCodeBlock);
|
||||||
|
super.visit(fencedCodeBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(HardLineBreak hardLineBreak) {
|
||||||
|
Debug.i("hardLineBreak: %s", hardLineBreak);
|
||||||
|
super.visit(hardLineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Heading heading) {
|
||||||
|
Debug.i("heading: %s", heading);
|
||||||
|
super.visit(heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ThematicBreak thematicBreak) {
|
||||||
|
Debug.i("thematicBreak: %s", thematicBreak);
|
||||||
|
super.visit(thematicBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(HtmlInline htmlInline) {
|
||||||
|
Debug.i("htmlInline: %s", htmlInline);
|
||||||
|
super.visit(htmlInline);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(HtmlBlock htmlBlock) {
|
||||||
|
Debug.i("htmlBlock: %s", htmlBlock);
|
||||||
|
super.visit(htmlBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Image image) {
|
||||||
|
Debug.i("image: %s", image);
|
||||||
|
super.visit(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IndentedCodeBlock indentedCodeBlock) {
|
||||||
|
Debug.i("indentedCodeBlock: %s", indentedCodeBlock);
|
||||||
|
super.visit(indentedCodeBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Link link) {
|
||||||
|
Debug.i("link: %s", link);
|
||||||
|
super.visit(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ListItem listItem) {
|
||||||
|
Debug.i("listItem: %s", listItem);
|
||||||
|
super.visit(listItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(OrderedList orderedList) {
|
||||||
|
Debug.i("orderedList: %s", orderedList);
|
||||||
|
super.visit(orderedList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Paragraph paragraph) {
|
||||||
|
Debug.i("paragraph: %s", paragraph);
|
||||||
|
super.visit(paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(SoftLineBreak softLineBreak) {
|
||||||
|
Debug.i("softLineBreak: %s", softLineBreak);
|
||||||
|
super.visit(softLineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(StrongEmphasis strongEmphasis) {
|
||||||
|
Debug.i("strongEmphasis: %s", strongEmphasis);
|
||||||
|
super.visit(strongEmphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Text text) {
|
||||||
|
Debug.i("text: %s", text);
|
||||||
|
super.visit(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CustomBlock customBlock) {
|
||||||
|
Debug.i("customBlock: %s", customBlock);
|
||||||
|
super.visit(customBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CustomNode customNode) {
|
||||||
|
Debug.i("customNode: %s", customNode);
|
||||||
|
super.visit(customNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="78.5885"
|
|
||||||
android:endY="90.9159"
|
|
||||||
android:startX="48.7653"
|
|
||||||
android:startY="61.0927"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1" />
|
|
||||||
</vector>
|
|
@ -1,170 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillColor="#26A69A"
|
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingLeft="16dip"
|
||||||
|
android:paddingRight="16dip"
|
||||||
|
android:scrollbarStyle="outsideInset">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lineSpacingExtra="2dip"
|
||||||
|
android:paddingTop="8dip"
|
||||||
|
android:paddingBottom="8dip"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="# Hello there! and taskjs" />
|
||||||
|
|
||||||
|
</HorizontalScrollView>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingLeft="16dip"
|
||||||
|
android:paddingRight="16dip">
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
android:id="@+id/table_layout"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</HorizontalScrollView>
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -5,11 +5,12 @@ include ':app',
|
|||||||
':markwon-ext-strikethrough',
|
':markwon-ext-strikethrough',
|
||||||
':markwon-ext-tables',
|
':markwon-ext-tables',
|
||||||
':markwon-ext-tasklist',
|
':markwon-ext-tasklist',
|
||||||
|
':markwon-html',
|
||||||
|
':markwon-image-gif',
|
||||||
':markwon-image-okhttp',
|
':markwon-image-okhttp',
|
||||||
':markwon-image-svg',
|
':markwon-image-svg',
|
||||||
':markwon-image-gif',
|
':markwon-recycler',
|
||||||
':markwon-syntax-highlight',
|
':markwon-syntax-highlight',
|
||||||
':markwon-html',
|
|
||||||
':markwon-test-span',
|
':markwon-test-span',
|
||||||
':sample-custom-extension',
|
':sample-custom-extension',
|
||||||
':sample-latex-math'
|
':sample-latex-math'
|
||||||
|