chapters, String shownotes, int duration) {
FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
item.setChapters(chapters);
item.setContentEncoded(shownotes);
FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
- media.setDuration(Integer.MAX_VALUE);
+ media.setDuration(duration);
item.setMedia(media);
return media;
}
@@ -44,7 +44,17 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11:12";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
- Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
");
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeHHMMSSMoreThen24HoursNoChapters() throws Exception {
+ final String timeStr = "25:00:00";
+ final long time = 25 * 60 * 60 * 1000;
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -54,17 +64,67 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
");
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ public void testProcessShownotesAddTimecodeMMSSNoChapters() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 10 * 60 * 1000 + 11 * 1000;
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", 11 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeHMMSSNoChapters() throws Exception {
+ final String timeStr = "2:11:12";
+ final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeMSSNoChapters() throws Exception {
+ final String timeStr = "1:12";
+ final long time = 60 * 1000 + 12 * 1000;
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStr + " here.
", 2 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() throws Exception {
+ final String[] timeStrings = new String[]{ "10:12", "1:10:12" };
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!
", 2 * 60 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000 }, timeStrings);
+ }
+
+ public void testProcessShownotesAddTimecodeMultipleShortFormatNoChapters() throws Exception {
+
+ // One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS.
+ final String[] timeStrings = new String[]{ "10:12", "2:12" };
+
+ Playable p = newTestPlayable(null, " Some test text with a timecode " + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!
", 3 * 60 * 60 * 1000);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{ 10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000 }, timeStrings);
+ }
+
public void testProcessShownotesAddTimecodeParentheses() throws Exception {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, " Some test text with a timecode (" + timeStr + ") here.
");
+ Playable p = newTestPlayable(null, " Some test text with a timecode (" + timeStr + ") here.
", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -74,7 +134,7 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, " Some test text with a timecode [" + timeStr + "] here.
");
+ Playable p = newTestPlayable(null, " Some test text with a timecode [" + timeStr + "] here.
", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
@@ -84,12 +144,27 @@ public class TimelineTest extends InstrumentationTestCase {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, " Some test text with a timecode <" + timeStr + "> here.
");
+ Playable p = newTestPlayable(null, " Some test text with a timecode <" + timeStr + "> here.
", Integer.MAX_VALUE);
Timeline t = new Timeline(context, p);
String res = t.processShownotes(true);
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
+ public void testProcessShownotesAndInvalidTimecode() throws Exception {
+ final String[] timeStrs = new String[] {"2:1", "0:0", "000", "00", "00:000"};
+
+ StringBuilder shownotes = new StringBuilder(" Some test text with timecodes ");
+ for (String timeStr : timeStrs) {
+ shownotes.append(timeStr).append(" ");
+ }
+ shownotes.append("here.
");
+
+ Playable p = newTestPlayable(null, shownotes.toString(), Integer.MAX_VALUE);
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[0], new String[0]);
+ }
+
private void checkLinkCorrect(String res, long[] timecodes, String[] timecodeStr) {
assertNotNull(res);
Document d = Jsoup.parse(res);
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
index afe15f1b2..8d2408b45 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
@@ -56,6 +56,11 @@ public class AtomGenerator implements FeedGenerator{
xml.text(feed.getDescription());
xml.endTag(null, "subtitle");
}
+ if (feed.getImageUrl() != null) {
+ xml.startTag(null, "logo");
+ xml.text(feed.getImageUrl());
+ xml.endTag(null, "logo");
+ }
if (feed.getPaymentLink() != null) {
GeneratorUtil.addPaymentLink(xml, feed.getPaymentLink(), false);
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
index 7f6f0fb0f..89542d222 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
@@ -8,6 +8,7 @@ import java.io.IOException;
* Utility methods for FeedGenerator
*/
class GeneratorUtil {
+ private GeneratorUtil(){}
public static void addPaymentLink(XmlSerializer xml, String paymentLink, boolean withNamespace) throws IOException {
String ns = (withNamespace) ? "http://www.w3.org/2005/Atom" : null;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
index f2d53799d..5f8b4d18c 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
@@ -54,6 +54,13 @@ public class RSS2Generator implements FeedGenerator{
xml.text(feed.getLanguage());
xml.endTag(null, "language");
}
+ if (feed.getImageUrl() != null) {
+ xml.startTag(null, "image");
+ xml.startTag(null, "url");
+ xml.text(feed.getImageUrl());
+ xml.endTag(null, "url");
+ xml.endTag(null, "image");
+ }
if (feed.getPaymentLink() != null) {
GeneratorUtil.addPaymentLink(xml, feed.getPaymentLink(), true);
diff --git a/app/src/free/play b/app/src/free/play
new file mode 120000
index 000000000..e9d641154
--- /dev/null
+++ b/app/src/free/play
@@ -0,0 +1 @@
+../main/play
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c7541cb59..216afc6b7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,14 +1,8 @@
-
+ android:installLocation="auto">
+
@@ -47,10 +41,13 @@
+
@@ -391,10 +388,6 @@
android:resource="@xml/provider_paths"/>
-
-
diff --git a/app/src/main/assets/LICENSE_SIL.txt b/app/src/main/assets/LICENSE_SIL.txt
new file mode 100644
index 000000000..f5ed6fa72
--- /dev/null
+++ b/app/src/main/assets/LICENSE_SIL.txt
@@ -0,0 +1,91 @@
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/app/src/main/assets/logo.png b/app/src/main/assets/logo.png
index d0e988a6d..3b5261b28 100755
Binary files a/app/src/main/assets/logo.png and b/app/src/main/assets/logo.png differ
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 3abf7557a..fde9af16f 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -20,7 +20,7 @@ public class PodcastApp extends Application {
try {
Class.forName("de.danoeh.antennapod.config.ClientConfigurator");
} catch (Exception e) {
- throw new RuntimeException("ClientConfigurator not found");
+ throw new RuntimeException("ClientConfigurator not found", e);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
index 4d9b50073..ecfdf24b0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -21,10 +21,10 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import rx.Single;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Single;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'about' screen
@@ -35,7 +35,7 @@ public class AboutActivity extends AppCompatActivity {
private WebView webView;
private LinearLayout webViewContainer;
- private Subscription subscription;
+ private Disposable disposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -43,8 +43,8 @@ public class AboutActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.about);
- webViewContainer = (LinearLayout) findViewById(R.id.webViewContainer);
- webView = (WebView) findViewById(R.id.webViewAbout);
+ webViewContainer = findViewById(R.id.webViewContainer);
+ webView = findViewById(R.id.webViewAbout);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
@@ -69,13 +69,16 @@ public class AboutActivity extends AppCompatActivity {
}
private void loadAsset(String filename) {
- subscription = Single.create(subscriber -> {
+ disposable = Single.create(subscriber -> {
InputStream input = null;
try {
TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
- new int[] { android.R.attr.textColorPrimary });
- int colorResource = res.getColor(0, 0);
- String colorString = String.format("#%06X", 0xFFFFFF & colorResource);
+ new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background,
+ R.attr.about_screen_card_background, R.attr.about_screen_card_border});
+ String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0));
+ String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0));
+ String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0));
+ String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0));
res.recycle();
input = getAssets().open(filename);
String webViewData = IOUtils.toString(input, Charset.defaultCharset());
@@ -92,7 +95,7 @@ public class AboutActivity extends AppCompatActivity {
" src: url('file:///android_asset/Roboto-Light.ttf');" +
" }" +
" * {" +
- " color: %s;" +
+ " color: @fontcolor@;" +
" font-family: roboto-Light;" +
" font-size: 8pt;" +
" }" +
@@ -100,7 +103,10 @@ public class AboutActivity extends AppCompatActivity {
"" + webViewData + "