Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Markdown in Descriptions #129

Merged
merged 5 commits into from
Aug 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ public BaseParser(EncodingFixer encodingFixer) {
String fixEncoding(String input) {
return encodingFixer.fixHtmlEscapes(input);
}

String fixMarkdown(String input) {
return encodingFixer.convertMarkdownToHtml(input);
}

String convertSubredditsToLinks(String input) {
return encodingFixer.convertSubredditsToLinks(input);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ public DescriptionColumnParser(EncodingFixer encodingFixer) {
@Override
public void parse(AppInfo appInfo, Map<Column, String> rawColumns) {
final String rawDescriptionString = rawColumns.get(Column.DESCRIPTION);
// TODO we need to deal with markdown in the descriptions
//noinspection deprecation
appInfo.setDescription(fixEncoding(rawDescriptionString));

String fixedDescription = fixEncoding(rawDescriptionString);
fixedDescription = fixMarkdown(fixedDescription);
fixedDescription = convertSubredditsToLinks(fixedDescription);

appInfo.setDescription(fixedDescription);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,65 @@

import android.text.Html;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EncodingFixer {
private static final Pattern URL_MARKDOWN_PATTERN = Pattern.compile("(\\[(.*?)\\]\\s{0,1}\\((http.*?)\\))");
private static final Pattern BOLD_MARKDOWN_PATTERN = Pattern.compile("(\\*\\*(.*?)\\*\\*)");
private static final Pattern SUBREDDIT_PATTERN = Pattern.compile("(\\/r\\/.*?(?!\\S))");

public String fixHtmlEscapes(String input) {
//noinspection deprecation
return Html.fromHtml(input).toString();
}

public String convertMarkdownToHtml(String input) {
input = urlMarkdownToHtml(input);
input = boldMarkdownToHtml(input);

return input;
}

public String convertSubredditsToLinks(String input) {
// Converts subreddit mentions to links in HTML
Matcher subredditMatcher = SUBREDDIT_PATTERN.matcher(input);
while (subredditMatcher.find()) {
String matchedSubreddit = subredditMatcher.group(1);

String fixedString = "<a href=\"https://www.reddit.com" + matchedSubreddit +
"\">" + matchedSubreddit + "</a>";

input = input.replaceAll(matchedSubreddit + "(?!\\S)", fixedString);
}
return input;
}

// Converts string with link markdown to HTML
private String urlMarkdownToHtml(String string) {
Matcher urlMarkdownMatcher = URL_MARKDOWN_PATTERN.matcher(string);
while (urlMarkdownMatcher.find()) {
String matchedString = urlMarkdownMatcher.group(2);
String matchedUrl = urlMarkdownMatcher.group(3);

String fixedString = "<a href=\"" + matchedUrl + "\">" + matchedString + "</a>";

string = string.replace(urlMarkdownMatcher.group(1), fixedString);
}
return string;
}

// Converts string with bold markdown to HTML
private String boldMarkdownToHtml(String string) {
Matcher boldMarkdownMatcher = BOLD_MARKDOWN_PATTERN.matcher(string);
while (boldMarkdownMatcher.find()) {
String matchedString = boldMarkdownMatcher.group(2);

String fixedString = "<b>" + matchedString + "</b>";

string = string.replace(boldMarkdownMatcher.group(1), fixedString);
}
return string;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
Expand All @@ -13,6 +14,8 @@
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
Expand Down Expand Up @@ -224,7 +227,14 @@ public void displayDetails(@Nullable AppInfo appInfo) {
secondaryTitle.setText(appInfo.getSecondaryCategory());
downloads = new ArrayList<>(appInfo.getDownloads());
contacts = new ArrayList<>(appInfo.getContacts());
description.setText(appInfo.getDescription());

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
description.setText(Html.fromHtml(appInfo.getDescription(), Html.FROM_HTML_MODE_COMPACT));
} else {
description.setText(Html.fromHtml(appInfo.getDescription()));
}
description.setMovementMethod(LinkMovementMethod.getInstance());

tagContainer.removeAllViews();
for (AppTags appTags : appInfo.getTags()) {
TextView tv = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.view_tagtemplate, tagContainer, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package subreddit.android.appstore.screens.list;

import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand Down Expand Up @@ -91,7 +93,13 @@ public ViewHolder(View itemView) {

public void bind(AppInfo item) {
appName.setText(item.getAppName());
description.setText(item.getDescription());

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
description.setText(Html.fromHtml(item.getDescription(), Html.FROM_HTML_MODE_COMPACT));
} else {
description.setText(Html.fromHtml(item.getDescription()));
}

if (PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean(SettingsActivity.PREF_KEY_LOAD_MEDIA, true)) {
iconFrame.setVisibility(View.VISIBLE);
GlideApp.with(getContext())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.reactivex.subjects.ReplaySubject;
import subreddit.android.appstore.backend.data.AppInfo;
import subreddit.android.appstore.backend.data.AppTags;
import subreddit.android.appstore.backend.reddit.wiki.parser.EncodingFixer;


public class FakeWikiRepository implements WikiRepository {
Expand All @@ -28,6 +29,7 @@ public Observable<Collection<AppInfo>> getAppList() {
}

private Observable<Collection<AppInfo>> loadData() {
EncodingFixer encodingFixer = new EncodingFixer();
return Observable.defer(() -> {
Collection<AppInfo> testValues = new ArrayList<>();
AppInfo app1 = new AppInfo();
Expand All @@ -46,6 +48,31 @@ private Observable<Collection<AppInfo>> loadData() {
app2.addTag(AppTags.PAID);
app2.addTag(AppTags.NEW);
testValues.add(app2);

// Test app with bold and link markdown in description
AppInfo app3 = new AppInfo();
app3.setAppName("Markdown Test");
app3.setDescription(
encodingFixer.convertMarkdownToHtml("Tutorial **screencast** for [Propellerheads Reason](https://www.propellerheads.se/products/reason/)")
);
app3.setCategories(new ArrayList<String>(Arrays.asList("Testing", "Examples", "Descriptions")));
app3.addTag(AppTags.WEAR);
app3.addTag(AppTags.PAID);
app3.addTag(AppTags.NEW);
testValues.add(app3);

// Test app with Subreddit link conversion
AppInfo app4 = new AppInfo();
app4.setAppName("Subreddit Link Conversion Test");
app4.setDescription(
encodingFixer.convertSubredditsToLinks("Wow! /r/Android is the bestest ever.")
);
app4.setCategories(new ArrayList<String>(Arrays.asList("Testing", "Examples", "Descriptions")));
app4.addTag(AppTags.WEAR);
app4.addTag(AppTags.PAID);
app4.addTag(AppTags.NEW);
testValues.add(app4);

for (int i = 0; i < 1000; i++) {
AppInfo randomApp = new AppInfo();
randomApp.setAppName(UUID.randomUUID().toString() + " app");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,19 @@ public String answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgument(0);
}
});
when(encodingFixer.convertMarkdownToHtml(anyString())).then(new Answer<String>() {
public String answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgument(0);
}
});
when(encodingFixer.convertSubredditsToLinks(anyString())).then(new Answer<String>() {
public String answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgument(0);
}
});
parser = new DescriptionColumnParser(encodingFixer);
}

@Test
public void testParse() throws IOException {
String rawDescriptionData = "This is a description";
Expand All @@ -44,15 +55,4 @@ public void testParse() throws IOException {
assertFalse(appInfo.getDescription().isEmpty());
assertEquals("This is a description", appInfo.getDescription());
}

@Test
public void testParse_withMarkdown() throws IOException {
String rawDescriptionData = "";
Map<AppParser.Column, String> rawColumnMap = new HashMap<>();
rawColumnMap.put(AppParser.Column.DESCRIPTION, rawDescriptionData);
AppInfo appInfo = new AppInfo();
parser.parse(appInfo, rawColumnMap);

//TODO: Implement markdown parsing in DescriptionColumnParser as well
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package subreddit.android.appstore.backend.reddit.wiki.parser;

import org.junit.Before;
import org.junit.Test;

import static junit.framework.Assert.assertEquals;

public class EncodingFixerTest {
private String descriptionBoldMarkdown, descriptionLinkMarkdown, descriptionSubreddits;
private EncodingFixer encodingFixer;

@Before
public void setup() {
encodingFixer = new EncodingFixer();

descriptionBoldMarkdown = "New ingenious full feature Reddit client. **Read AMA in magazine" +
"style QA format.** Add upcoming AMA to your calendar. **Only app with Text to " +
"Speech: Listen to the content.** Zero wait, streams all GIF instantly.";
descriptionLinkMarkdown = "Tutorial screencast for [Propellerheads Reason](https://www" +
".propellerheads.se/products/reason/) and [Link 2](https://www.google.com)";
descriptionSubreddits = "You can setup playlists, geofences, volume control, and share" +
" playlists with others. Share playlists at /r/androidrrm and /r/android";
}

@Test
public void testConvertMarkdownToHtml() {
String output = encodingFixer.convertMarkdownToHtml("");
assertEquals("", output);

output = encodingFixer.convertMarkdownToHtml("This description has no markdown");
assertEquals("This description has no markdown", output);
}

@Test
public void testConvertMarkdownToHtml_bold() {
String output = encodingFixer.convertMarkdownToHtml(descriptionBoldMarkdown);
assertEquals("New ingenious full feature Reddit client. <b>Read AMA in magazine" +
"style QA format.</b> Add upcoming AMA to your calendar. <b>Only app with Text to " +
"Speech: Listen to the content.</b> Zero wait, streams all GIF instantly.", output);
}

@Test
public void testConvertMarkdownToHtml_link() {
String output = encodingFixer.convertMarkdownToHtml(descriptionLinkMarkdown);
assertEquals("Tutorial screencast for " +
"<a href=\"https://www.propellerheads.se/products/reason/\">" +
"Propellerheads Reason</a> and <a href=\"https://www.google.com\">Link 2</a>", output);
}

@Test
public void testConvertSubredditsToLinks() {
String output = encodingFixer.convertSubredditsToLinks(descriptionSubreddits);
assertEquals("You can setup playlists, geofences, volume control, and share " +
"playlists with others. Share playlists at " +
"<a href=\"https://www.reddit.com/r/androidrrm\">/r/androidrrm</a> " +
"and <a href=\"https://www.reddit.com/r/android\">/r/android</a>", output);
}
}