Remove text colors from shownotes
This commit is contained in:
parent
504002c48f
commit
7d0b0e57ee
|
@ -14,7 +14,7 @@ import androidx.fragment.app.Fragment;
|
|||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||
import de.danoeh.antennapod.core.util.gui.ShownotesCleaner;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.model.playback.Playable;
|
||||
import de.danoeh.antennapod.view.ShownotesWebView;
|
||||
|
@ -99,8 +99,9 @@ public class ItemDescriptionFragment extends Fragment {
|
|||
}
|
||||
DBReader.loadDescriptionOfFeedItem(feedMedia.getItem());
|
||||
}
|
||||
Timeline timeline = new Timeline(getActivity(), media.getDescription(), media.getDuration());
|
||||
emitter.onSuccess(timeline.processShownotes());
|
||||
ShownotesCleaner shownotesCleaner = new ShownotesCleaner(
|
||||
getActivity(), media.getDescription(), media.getDuration());
|
||||
emitter.onSuccess(shownotesCleaner.processShownotes());
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
|
@ -58,7 +58,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
|
|||
import de.danoeh.antennapod.ui.common.CircularProgressBar;
|
||||
import de.danoeh.antennapod.ui.common.ThemeUtils;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||
import de.danoeh.antennapod.core.util.gui.ShownotesCleaner;
|
||||
import de.danoeh.antennapod.view.ShownotesWebView;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
@ -438,7 +438,7 @@ public class ItemFragment extends Fragment {
|
|||
if (feedItem != null && context != null) {
|
||||
int duration = feedItem.getMedia() != null ? feedItem.getMedia().getDuration() : Integer.MAX_VALUE;
|
||||
DBReader.loadDescriptionOfFeedItem(feedItem);
|
||||
Timeline t = new Timeline(context, feedItem.getDescription(), duration);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, feedItem.getDescription(), duration);
|
||||
webviewData = t.processShownotes();
|
||||
}
|
||||
return feedItem;
|
||||
|
|
|
@ -30,7 +30,7 @@ import de.danoeh.antennapod.core.util.Converter;
|
|||
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
import de.danoeh.antennapod.core.util.ShareUtils;
|
||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||
import de.danoeh.antennapod.core.util.gui.ShownotesCleaner;
|
||||
|
||||
public class ShownotesWebView extends WebView implements View.OnLongClickListener {
|
||||
private static final String TAG = "ShownotesWebView";
|
||||
|
@ -73,8 +73,8 @@ public class ShownotesWebView extends WebView implements View.OnLongClickListene
|
|||
setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (Timeline.isTimecodeLink(url) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener.accept(Timeline.getTimecodeLinkTime(url));
|
||||
if (ShownotesCleaner.isTimecodeLink(url) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener.accept(ShownotesCleaner.getTimecodeLinkTime(url));
|
||||
} else {
|
||||
IntentUtils.openInBrowser(getContext(), url);
|
||||
}
|
||||
|
@ -137,8 +137,8 @@ public class ShownotesWebView extends WebView implements View.OnLongClickListene
|
|||
ViewCompat.setElevation(s.getView(), 100);
|
||||
s.show();
|
||||
} else if (itemId == R.id.go_to_position_item) {
|
||||
if (Timeline.isTimecodeLink(selectedUrl) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener.accept(Timeline.getTimecodeLinkTime(selectedUrl));
|
||||
if (ShownotesCleaner.isTimecodeLink(selectedUrl) && timecodeSelectedListener != null) {
|
||||
timecodeSelectedListener.accept(ShownotesCleaner.getTimecodeLinkTime(selectedUrl));
|
||||
} else {
|
||||
Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: " + selectedUrl);
|
||||
}
|
||||
|
@ -157,9 +157,9 @@ public class ShownotesWebView extends WebView implements View.OnLongClickListene
|
|||
return;
|
||||
}
|
||||
|
||||
if (Timeline.isTimecodeLink(selectedUrl)) {
|
||||
if (ShownotesCleaner.isTimecodeLink(selectedUrl)) {
|
||||
menu.add(Menu.NONE, R.id.go_to_position_item, Menu.NONE, R.string.go_to_position_label);
|
||||
menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedUrl)));
|
||||
menu.setHeaderTitle(Converter.getDurationStringLong(ShownotesCleaner.getTimecodeLinkTime(selectedUrl)));
|
||||
} else {
|
||||
Uri uri = Uri.parse(selectedUrl);
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
package de.danoeh.antennapod.core.util.gui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
|
@ -26,27 +26,26 @@ import de.danoeh.antennapod.core.R;
|
|||
import de.danoeh.antennapod.core.util.Converter;
|
||||
|
||||
/**
|
||||
* Connects chapter information and shownotes of a shownotesProvider, for example by making it possible to use the
|
||||
* shownotes to navigate to another position in the podcast or by highlighting certain parts of the shownotesProvider's
|
||||
* shownotes.
|
||||
* <p/>
|
||||
* A timeline object needs a shownotesProvider from which the chapter information
|
||||
* is retrieved and shownotes are generated.
|
||||
* Cleans up and prepares shownotes:
|
||||
* - Guesses time stamps to make them clickable
|
||||
* - Removes some formatting
|
||||
*/
|
||||
public class Timeline {
|
||||
public class ShownotesCleaner {
|
||||
private static final String TAG = "Timeline";
|
||||
|
||||
private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/(\\d+)");
|
||||
private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>";
|
||||
private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b");
|
||||
private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>");
|
||||
private static final String CSS_COLOR = "(?<=(\\s|;|^))color\\s*:([^;])*;";
|
||||
private static final String CSS_COMMENT = "/\\*.*?\\*/";
|
||||
|
||||
private final String rawShownotes;
|
||||
private final String noShownotesLabel;
|
||||
private final int playableDuration;
|
||||
private final String webviewStyle;
|
||||
|
||||
public Timeline(Context context, @Nullable String rawShownotes, int playableDuration) {
|
||||
public ShownotesCleaner(Context context, @Nullable String rawShownotes, int playableDuration) {
|
||||
this.rawShownotes = rawShownotes;
|
||||
|
||||
noShownotesLabel = context.getString(R.string.no_shownotes_label);
|
||||
|
@ -98,9 +97,8 @@ public class Timeline {
|
|||
}
|
||||
|
||||
Document document = Jsoup.parse(shownotes);
|
||||
cleanCss(document);
|
||||
document.head().appendElement("style").attr("type", "text/css").text(webviewStyle);
|
||||
|
||||
// apply timecode links
|
||||
addTimecodes(document);
|
||||
return document.toString();
|
||||
}
|
||||
|
@ -193,4 +191,18 @@ public class Timeline {
|
|||
element.html(buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanCss(Document document) {
|
||||
for (Element element : document.getAllElements()) {
|
||||
if (element.hasAttr("style")) {
|
||||
element.attr("style", element.attr("style").replaceAll(CSS_COLOR, ""));
|
||||
} else if (element.tagName().equals("style")) {
|
||||
element.html(cleanStyleTag(element.html()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String cleanStyleTag(String oldCss) {
|
||||
return oldCss.replaceAll(CSS_COMMENT, "").replaceAll(CSS_COLOR, "");
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
package de.danoeh.antennapod.core.util.gui;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -7,14 +7,10 @@ import org.jsoup.Jsoup;
|
|||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -23,25 +19,16 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test class for {@link Timeline}.
|
||||
* Test class for {@link ShownotesCleaner}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class TimelineTest {
|
||||
public class ShownotesCleanerTest {
|
||||
|
||||
private Context context;
|
||||
MockedStatic<DBReader> dbReaderMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
// mock DBReader, because Timeline.processShownotes() calls FeedItem.loadShownotes()
|
||||
// which calls DBReader.loadDescriptionOfFeedItem(), but we don't need the database here
|
||||
dbReaderMock = Mockito.mockStatic(DBReader.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
dbReaderMock.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -50,7 +37,7 @@ public class TimelineTest {
|
|||
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -61,7 +48,7 @@ public class TimelineTest {
|
|||
final long time = 25 * 60 * 60 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -72,7 +59,7 @@ public class TimelineTest {
|
|||
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -83,7 +70,7 @@ public class TimelineTest {
|
|||
final long time = 10 * 60 * 1000 + 11 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, 11 * 60 * 1000);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, 11 * 60 * 1000);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -94,7 +81,7 @@ public class TimelineTest {
|
|||
final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -105,7 +92,7 @@ public class TimelineTest {
|
|||
final long time = 60 * 1000 + 12 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, 2 * 60 * 1000);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, 2 * 60 * 1000);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -116,7 +103,7 @@ public class TimelineTest {
|
|||
final int time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, time);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, time);
|
||||
String res = t.processShownotes();
|
||||
Document d = Jsoup.parse(res);
|
||||
assertEquals("Should not parse time codes that equal duration", 0, d.body().getElementsByTag("a").size());
|
||||
|
@ -128,7 +115,7 @@ public class TimelineTest {
|
|||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStrings[0]
|
||||
+ " here. Hey look another one " + timeStrings[1] + " here!</p>";
|
||||
Timeline t = new Timeline(context, shownotes, 2 * 60 * 60 * 1000);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, 2 * 60 * 60 * 1000);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000,
|
||||
60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000}, timeStrings);
|
||||
|
@ -142,7 +129,7 @@ public class TimelineTest {
|
|||
|
||||
String shownotes = "<p> Some test text with a timecode " + timeStrings[0]
|
||||
+ " here. Hey look another one " + timeStrings[1] + " here!</p>";
|
||||
Timeline t = new Timeline(context, shownotes, 3 * 60 * 60 * 1000);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, 3 * 60 * 60 * 1000);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000}, timeStrings);
|
||||
}
|
||||
|
@ -153,7 +140,7 @@ public class TimelineTest {
|
|||
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode (" + timeStr + ") here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -164,7 +151,7 @@ public class TimelineTest {
|
|||
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode [" + timeStr + "] here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -175,7 +162,7 @@ public class TimelineTest {
|
|||
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
|
||||
|
||||
String shownotes = "<p> Some test text with a timecode <" + timeStr + "> here.</p>";
|
||||
Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes, Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
|
||||
}
|
||||
|
@ -190,7 +177,7 @@ public class TimelineTest {
|
|||
}
|
||||
shownotes.append("here.</p>");
|
||||
|
||||
Timeline t = new Timeline(context, shownotes.toString(), Integer.MAX_VALUE);
|
||||
ShownotesCleaner t = new ShownotesCleaner(context, shownotes.toString(), Integer.MAX_VALUE);
|
||||
String res = t.processShownotes();
|
||||
checkLinkCorrect(res, new long[0], new String[0]);
|
||||
}
|
||||
|
@ -216,20 +203,34 @@ public class TimelineTest {
|
|||
|
||||
@Test
|
||||
public void testIsTimecodeLink() {
|
||||
assertFalse(Timeline.isTimecodeLink(null));
|
||||
assertFalse(Timeline.isTimecodeLink("http://antennapod/timecode/123123"));
|
||||
assertFalse(Timeline.isTimecodeLink("antennapod://timecode/"));
|
||||
assertFalse(Timeline.isTimecodeLink("antennapod://123123"));
|
||||
assertFalse(Timeline.isTimecodeLink("antennapod://timecode/123123a"));
|
||||
assertTrue(Timeline.isTimecodeLink("antennapod://timecode/123"));
|
||||
assertTrue(Timeline.isTimecodeLink("antennapod://timecode/1"));
|
||||
assertFalse(ShownotesCleaner.isTimecodeLink(null));
|
||||
assertFalse(ShownotesCleaner.isTimecodeLink("http://antennapod/timecode/123123"));
|
||||
assertFalse(ShownotesCleaner.isTimecodeLink("antennapod://timecode/"));
|
||||
assertFalse(ShownotesCleaner.isTimecodeLink("antennapod://123123"));
|
||||
assertFalse(ShownotesCleaner.isTimecodeLink("antennapod://timecode/123123a"));
|
||||
assertTrue(ShownotesCleaner.isTimecodeLink("antennapod://timecode/123"));
|
||||
assertTrue(ShownotesCleaner.isTimecodeLink("antennapod://timecode/1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimecodeLinkTime() {
|
||||
assertEquals(-1, Timeline.getTimecodeLinkTime(null));
|
||||
assertEquals(-1, Timeline.getTimecodeLinkTime("http://timecode/123"));
|
||||
assertEquals(123, Timeline.getTimecodeLinkTime("antennapod://timecode/123"));
|
||||
assertEquals(-1, ShownotesCleaner.getTimecodeLinkTime(null));
|
||||
assertEquals(-1, ShownotesCleaner.getTimecodeLinkTime("http://timecode/123"));
|
||||
assertEquals(123, ShownotesCleaner.getTimecodeLinkTime("antennapod://timecode/123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanupColors() {
|
||||
final String input = "/* /* */ .foo { text-decoration: underline;color:#f00;font-weight:bold;}"
|
||||
+ "#bar { text-decoration: underline;color:#f00;font-weight:bold; }"
|
||||
+ "div {text-decoration: underline; color /* */ : /* */ #f00 /* */; font-weight:bold; }"
|
||||
+ "#foobar { /* color: */ text-decoration: underline; /* color: */font-weight:bold /* ; */; }"
|
||||
+ "baz { background-color:#f00;border: solid 2px;border-color:#0f0;text-decoration: underline; }";
|
||||
final String expected = " .foo { text-decoration: underline;font-weight:bold;}"
|
||||
+ "#bar { text-decoration: underline;font-weight:bold; }"
|
||||
+ "div {text-decoration: underline; font-weight:bold; }"
|
||||
+ "#foobar { text-decoration: underline; font-weight:bold ; }"
|
||||
+ "baz { background-color:#f00;border: solid 2px;border-color:#0f0;text-decoration: underline; }";
|
||||
assertEquals(expected, ShownotesCleaner.cleanStyleTag(input));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue