summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml5
-rw-r--r--CHANGELOG.md9
-rw-r--r--assets/LICENSE_BETTERPICKERS.txt13
-rw-r--r--assets/LICENSE_JSOUP.txt21
-rw-r--r--assets/about.html6
-rw-r--r--build.gradle17
-rw-r--r--pom.xml2
-rw-r--r--proguard.cfg3
-rw-r--r--res/layout/addfeed.xml2
-rw-r--r--res/layout/autoflattr_preference_dialog.xml35
-rw-r--r--res/layout/ellipsize_start_listitem.xml19
-rw-r--r--res/layout/gpodnetauth_activity.xml2
-rw-r--r--res/layout/queue_fragment.xml16
-rw-r--r--res/layout/time_dialog.xml2
-rw-r--r--res/menu/queue_context.xml9
-rw-r--r--res/values-az/strings.xml1
-rw-r--r--res/values-ca/strings.xml2
-rw-r--r--res/values-cs-rCZ/strings.xml1
-rw-r--r--res/values-da/strings.xml2
-rw-r--r--res/values-de/strings.xml13
-rw-r--r--res/values-es-rES/strings.xml1
-rw-r--r--res/values-es/strings.xml2
-rw-r--r--res/values-fr/strings.xml2
-rw-r--r--res/values-hi-rIN/strings.xml1
-rw-r--r--res/values-it-rIT/strings.xml1
-rw-r--r--res/values-iw-rIL/strings.xml2
-rw-r--r--res/values-ko/strings.xml2
-rw-r--r--res/values-nl/strings.xml2
-rw-r--r--res/values-pl-rPL/strings.xml52
-rw-r--r--res/values-pt-rBR/strings.xml1
-rw-r--r--res/values-pt/strings.xml2
-rw-r--r--res/values-ro-rRO/strings.xml1
-rw-r--r--res/values-ru/strings.xml2
-rw-r--r--res/values-sv-rSE/strings.xml2
-rw-r--r--res/values-uk-rUA/strings.xml2
-rw-r--r--res/values-zh-rCN/strings.xml1
-rw-r--r--res/values/arrays.xml10
-rw-r--r--res/values/ids.xml4
-rw-r--r--res/values/strings.xml15
-rw-r--r--res/values/styles.xml26
-rw-r--r--res/xml/preferences.xml15
-rw-r--r--src/com/aocate/media/MediaPlayer.java4
-rw-r--r--src/de/danoeh/antennapod/AppConfig.java2
-rw-r--r--src/de/danoeh/antennapod/activity/AudioplayerActivity.java61
-rw-r--r--src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java33
-rw-r--r--src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java6
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java27
-rw-r--r--src/de/danoeh/antennapod/activity/MediaplayerActivity.java49
-rw-r--r--src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java93
-rw-r--r--src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java4
-rw-r--r--src/de/danoeh/antennapod/activity/PreferenceActivity.java28
-rw-r--r--src/de/danoeh/antennapod/activity/StorageErrorActivity.java5
-rw-r--r--src/de/danoeh/antennapod/activity/VideoplayerActivity.java14
-rw-r--r--src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java6
-rw-r--r--src/de/danoeh/antennapod/adapter/ActionButtonUtils.java21
-rw-r--r--src/de/danoeh/antennapod/adapter/ChapterListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java44
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java4
-rw-r--r--src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java4
-rw-r--r--src/de/danoeh/antennapod/adapter/NavListAdapter.java6
-rw-r--r--src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/QueueListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/SearchlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadObserver.java11
-rw-r--r--src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java376
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageDiskCache.java10
-rw-r--r--src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java107
-rw-r--r--src/de/danoeh/antennapod/dialog/FeedItemDialog.java84
-rw-r--r--src/de/danoeh/antennapod/dialog/TimeDialog.java214
-rw-r--r--src/de/danoeh/antennapod/feed/EventDistributor.java8
-rw-r--r--src/de/danoeh/antennapod/feed/FeedMedia.java4
-rw-r--r--src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java3
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java134
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemlistFragment.java62
-rw-r--r--src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java43
-rw-r--r--src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java18
-rw-r--r--src/de/danoeh/antennapod/fragment/QueueFragment.java77
-rw-r--r--src/de/danoeh/antennapod/fragment/SearchFragment.java42
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java42
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java47
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java7
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java66
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/GpodnetService.java104
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java6
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java9
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java9
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java6
-rw-r--r--src/de/danoeh/antennapod/preferences/PlaybackPreferences.java7
-rw-r--r--src/de/danoeh/antennapod/preferences/UserPreferences.java1006
-rw-r--r--src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java7
-rw-r--r--src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java5
-rw-r--r--src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java5
-rw-r--r--src/de/danoeh/antennapod/receiver/PlayerWidget.java7
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadRequest.java23
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java120
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadStatus.java11
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackService.java59
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java25
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java15
-rw-r--r--src/de/danoeh/antennapod/spa/SPAUtil.java5
-rw-r--r--src/de/danoeh/antennapod/storage/DBTasks.java20
-rw-r--r--src/de/danoeh/antennapod/storage/DBWriter.java4
-rw-r--r--src/de/danoeh/antennapod/storage/DownloadRequester.java6
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java22
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/TypeGetter.java18
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java19
-rw-r--r--src/de/danoeh/antennapod/util/Converter.java21
-rw-r--r--src/de/danoeh/antennapod/util/DuckType.java4
-rw-r--r--src/de/danoeh/antennapod/util/URLChecker.java7
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java20
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java11
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java9
-rw-r--r--src/de/danoeh/antennapod/util/playback/Playable.java6
-rw-r--r--src/de/danoeh/antennapod/util/playback/PlaybackController.java22
-rw-r--r--src/de/danoeh/antennapod/util/playback/Timeline.java161
-rw-r--r--src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java78
-rw-r--r--src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java3
-rw-r--r--src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java2
-rw-r--r--src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java7
-rw-r--r--src/instrumentationTest/de/test/antennapod/util/ConverterTest.java35
-rw-r--r--src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java18
-rw-r--r--src/instrumentationTest/de/test/antennapod/util/playback/TimelineTest.java127
-rw-r--r--src/instrumentationTest/de/test/antennapod/util/syndication/FeedDiscovererTest.java109
m---------submodules/dslv0
129 files changed, 2837 insertions, 1431 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b06a9b5c7..65aa8ea59 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="38"
- android:versionName="0.9.9.1">
+ android:versionCode="39"
+ android:versionName="0.9.9.2">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
@@ -294,6 +294,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
+ <data android:scheme="itpc"/>
<data android:scheme="pcast"/>
<data android:scheme="feed"/>
</intent-filter>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1cce0c57a..3cdd715ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,15 @@
Change Log
==========
+Version 0.9.9.2
+---------------
+* Added support for feed discovery if a website URL is entered
+* Added support for 'next'/'previous' media keys
+* Improved sleep timer
+* Timestamps in shownotes can now be used to jump to a specific position
+* Automatic Flattring is now configurable
+* Several bugfixes and improvements
+
Version 0.9.9.1
---------------
* Several bugfixes and improvements
diff --git a/assets/LICENSE_BETTERPICKERS.txt b/assets/LICENSE_BETTERPICKERS.txt
new file mode 100644
index 000000000..80830ed73
--- /dev/null
+++ b/assets/LICENSE_BETTERPICKERS.txt
@@ -0,0 +1,13 @@
+Copyright 2013 Derek Brameyer
+
+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.
diff --git a/assets/LICENSE_JSOUP.txt b/assets/LICENSE_JSOUP.txt
new file mode 100644
index 000000000..f3ef71dbf
--- /dev/null
+++ b/assets/LICENSE_JSOUP.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2009, 2010, 2011, 2012, 2013 Jonathan Hedley <jonathan@hedley.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/assets/about.html b/assets/about.html
index 4bd4870c6..d572b7518 100644
--- a/assets/about.html
+++ b/assets/about.html
@@ -41,7 +41,7 @@
<div id="header" align="center">
<img src="logo.png" alt="Logo" width="100px" height="100px"/>
- <p>AntennaPod, Version 0.9.9.1</p>
+ <p>AntennaPod, Version 0.9.9.2</p>
<p>Copyright © 2014 Daniel Oeh</p>
@@ -60,5 +60,9 @@ licensed under the Apache 2.0 license <a href="LICENSE_FLATTR4J.txt">(View)</a>
licensed under the Apache 2.0 license <a href="LICENSE_DSLV.txt">(View)</a>
<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
+<h2>Better Pickers <a href="https://github.com/derekbrameyer/android-betterpickers">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_BETTERPICKERS.txt">(View)</a>
+<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
+licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
</body>
</html>
diff --git a/build.gradle b/build.gradle
index 7e1f7809b..140d483da 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,10 +3,10 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.11.+'
+ classpath 'com.android.tools.build:gradle:0.12.+'
}
}
-apply plugin: 'android'
+apply plugin: 'com.android.application'
repositories {
mavenCentral()
@@ -29,12 +29,16 @@ dependencies {
compile 'com.nineoldandroids:library:2.4.0'
compile project(':submodules:dslv:library')
- compile 'com.jayway.android.robotium:robotium-solo:5.1'
+ compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
+ compile ("com.doomonafireball.betterpickers:library:1.5.2") {
+ exclude group: 'com.android.support', module: 'support-v4'
+ }
+ compile 'org.jsoup:jsoup:1.7.3'
}
android {
compileSdkVersion 19
- buildToolsVersion "19.1.0"
+ buildToolsVersion "20.0"
defaultConfig {
minSdkVersion 10
@@ -99,6 +103,11 @@ android {
lintOptions {
abortOnError false
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
}
task wrapper(type: Wrapper) {
diff --git a/pom.xml b/pom.xml
index db1d0526d..658989cee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
<groupId>de.danoeh</groupId>
<artifactId>antennapod</artifactId>
<packaging>apk</packaging>
- <version>0.9.9.1</version>
+ <version>0.9.9.2</version>
<name>AntennaPod</name>
diff --git a/proguard.cfg b/proguard.cfg
index fd76cc73a..323e0b673 100644
--- a/proguard.cfg
+++ b/proguard.cfg
@@ -47,6 +47,9 @@
public static <fields>;
}
+-keep public class org.jsoup.** {
+ public *;
+}
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
diff --git a/res/layout/addfeed.xml b/res/layout/addfeed.xml
index 48c89f802..09502eb7b 100644
--- a/res/layout/addfeed.xml
+++ b/res/layout/addfeed.xml
@@ -29,7 +29,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvFeedurl"
android:layout_margin="8dp"
- android:hint="@string/feedurl_label"
+ android:hint="@string/etxtFeedurlHint"
android:inputType="textUri"/>
<Button
diff --git a/res/layout/autoflattr_preference_dialog.xml b/res/layout/autoflattr_preference_dialog.xml
new file mode 100644
index 000000000..fc2df30d7
--- /dev/null
+++ b/res/layout/autoflattr_preference_dialog.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <CheckBox
+ android:id="@+id/chkAutoFlattr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ android:text="@string/auto_flattr_enable"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small" />
+
+ <SeekBar
+ android:id="@+id/skbPercent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:max="100" />
+
+ <TextView
+ android:id="@+id/txtvStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:ellipsize="end"
+ android:lines="2"
+ android:text="@string/auto_flattr_after_percent"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/ellipsize_start_listitem.xml b/res/layout/ellipsize_start_listitem.xml
new file mode 100644
index 000000000..161e1aa37
--- /dev/null
+++ b/res/layout/ellipsize_start_listitem.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/text_size_small"
+ android:lines="1"
+ android:singleLine="true"
+ android:layout_margin="16dp"
+ android:ellipsize="start"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/gpodnetauth_activity.xml b/res/layout/gpodnetauth_activity.xml
index cd428fbdf..c096c20cf 100644
--- a/res/layout/gpodnetauth_activity.xml
+++ b/res/layout/gpodnetauth_activity.xml
@@ -6,5 +6,5 @@
<ViewFlipper
android:id="@+id/viewflipper"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="wrap_content"/>
</ScrollView> \ No newline at end of file
diff --git a/res/layout/queue_fragment.xml b/res/layout/queue_fragment.xml
index 373975c1c..742411761 100644
--- a/res/layout/queue_fragment.xml
+++ b/res/layout/queue_fragment.xml
@@ -16,8 +16,7 @@
dslv:drag_scroll_start="0.33"
dslv:float_alpha="0.6"
dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="true"
- dslv:remove_mode="flingRemove"
+ dslv:remove_enabled="false"
dslv:slide_shuffle_speed="0.3"
dslv:sort_enabled="true"
dslv:track_drag_sort="true"
@@ -40,17 +39,4 @@
android:indeterminateOnly="true"
android:visibility="gone"/>
- <LinearLayout
- android:id="@+id/undobar"
- style="@style/UndoBar">
-
- <TextView
- android:id="@+id/undobar_message"
- style="@style/UndoBarMessage"/>
-
- <Button
- android:id="@+id/undobar_button"
- style="@style/UndoBarButton"/>
- </LinearLayout>
-
</FrameLayout> \ No newline at end of file
diff --git a/res/layout/time_dialog.xml b/res/layout/time_dialog.xml
index 7e5d869e0..95cc9a5a7 100644
--- a/res/layout/time_dialog.xml
+++ b/res/layout/time_dialog.xml
@@ -20,7 +20,7 @@
android:inputType="number"
android:maxLength="2" >
- <requestFocus />
+
</EditText>
<Spinner
diff --git a/res/menu/queue_context.xml b/res/menu/queue_context.xml
index e24f35298..327600038 100644
--- a/res/menu/queue_context.xml
+++ b/res/menu/queue_context.xml
@@ -5,11 +5,16 @@
<item
android:id="@+id/move_to_top_item"
android:menuCategory="container"
- android:title="@string/move_to_top_label"/>
+ android:title="@string/move_to_top_label" />
+
+ <item
+ android:id="@+id/remove_from_queue_item"
+ android:menuCategory="container"
+ android:title="@string/remove_from_queue_label" />
<item
android:id="@+id/move_to_bottom_item"
android:menuCategory="container"
- android:title="@string/move_to_bottom_label"/>
+ android:title="@string/move_to_bottom_label" />
</menu> \ No newline at end of file
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 72c6e35b6..adb983e9e 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -164,6 +164,7 @@
<string name="pref_update_interval_hours_plural">saat</string>
<string name="pref_update_interval_hours_singular">saat</string>
<string name="pref_update_interval_hours_manual">Əl ilə</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Kanalları və ya epizodları axtar</string>
<string name="found_in_shownotes_label">Təsvirlərdə tapıldı</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 85dd36f91..a9f96fb31 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -199,7 +199,6 @@
<string name="pref_revokeAccess_title">Revoca l\'accés</string>
<string name="pref_revokeAccess_sum">Revoqueu el permís d\'accés d\'aquesta aplicació al vostre compte Flattr.</string>
<string name="pref_auto_flattr_title">Flattr automàtic</string>
- <string name="pref_auto_flattr_sum">Comparteix per Flattr els episodis la reproducció dels quals hagi arribat al 80%.</string>
<string name="user_interface_label">Interfície d\'usuari</string>
<string name="pref_set_theme_title">Selecció de tema</string>
<string name="pref_set_theme_sum">Canvieu l\'aparença d\'AntennaPod.</string>
@@ -224,6 +223,7 @@
<string name="pref_playback_speed_sum">Personalitzeu les velocitats disponibles per a una velocitat de reproducció d\'àudio variable</string>
<string name="pref_gpodnet_sethostname_title">Definex nom del servidor</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utilitza el servidor per defecte</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Cerca canals o episodis</string>
<string name="found_in_shownotes_label">Trobat a notes del programa</string>
diff --git a/res/values-cs-rCZ/strings.xml b/res/values-cs-rCZ/strings.xml
index b8820aee6..8792d1fc9 100644
--- a/res/values-cs-rCZ/strings.xml
+++ b/res/values-cs-rCZ/strings.xml
@@ -188,6 +188,7 @@
<string name="pref_playback_speed_sum">Přizpůsobení rychlosti je dostupné pro přehrávání zvuku různými rychlostmi</string>
<string name="pref_gpodnet_sethostname_title">Nastavit hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Použít přednastaveného hosta</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Hledat zdroje a epizody</string>
<string name="found_in_shownotes_label">Nalezeno v poznámkách k show</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9055ce4ed..5c41b6eb0 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -198,7 +198,6 @@
<string name="pref_revokeAccess_title">Tilbagekald adgang</string>
<string name="pref_revokeAccess_sum">Tilbagekald adgangen til din flattr konto fra denne app.</string>
<string name="pref_auto_flattr_title">Flattr\'er automatisk</string>
- <string name="pref_auto_flattr_sum">Flattr episoder der er 80 procent afspillet. </string>
<string name="user_interface_label">Brugerflade</string>
<string name="pref_set_theme_title">Vælg tema</string>
<string name="pref_set_theme_sum">Skift AntennaPods udseende.</string>
@@ -223,6 +222,7 @@
<string name="pref_playback_speed_sum">Tilpas tilgængelige hastigheder for variabelt afspilningshastigheds plugin</string>
<string name="pref_gpodnet_sethostname_title">Indstil værtsnavn</string>
<string name="pref_gpodnet_sethostname_use_default_host">Brug standard vært</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Søg efter feeds eller episoder</string>
<string name="found_in_shownotes_label">Funder i showets noter</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 04565af86..539470a5e 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -31,6 +31,7 @@
<string name="copy_url_label">URL kopieren</string>
<string name="share_url_label">URL teilen</string>
<string name="copied_url_msg">URL wurde in die Zwischenablage kopiert.</string>
+ <string name="go_to_position_label">Gehe zu dieser Position</string>
<!--Playback history-->
<string name="clear_history_label">Chronik löschen</string>
<!--Other-->
@@ -59,6 +60,7 @@
<string name="auto_download_label">Automatisch herunterladen</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Feed URL</string>
+ <string name="etxtFeedurlHint">URL des Feeds oder der Webseite</string>
<string name="txtvfeedurl_label">Podcast per URL hinzufügen</string>
<string name="podcastdirectories_label">Podcast in Verzeichnis finden</string>
<string name="podcastdirectories_descr">Bei gpodder.net kannst du nach neuen Podcasts nach Name, Kategorie oder Popularität suchen.</string>
@@ -133,6 +135,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">Puffert</string>
<string name="playbackservice_notification_title">Spiele Podcast ab</string>
+ <string name="unknown_media_key">AntennaPod - Unbekannte Medientaste: %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">Abspielliste leeren</string>
<string name="undo">Rückgängig</string>
@@ -146,6 +149,7 @@
<string name="return_home_label">Zur Hauptseite zurückkehren</string>
<string name="flattr_auth_success">Die Authentifizierung war erfolgreich! Du kannst nun in der Anwendung Flattr verwenden.</string>
<string name="no_flattr_token_title">Kein Flattr Token gefunden</string>
+ <string name="no_flattr_token_notification_msg">Dein Flattr Account scheint nicht mit AntennaPod verbunden zu sein. Tippe hier zum authentifizieren.</string>
<string name="no_flattr_token_msg">Dein Flattr Account scheint nicht mit AntennaPod verbunden zu sein. Du kannst entweder deinen Account mit AntennaPod verbinden, um direkt in der Anwendung Flattr zu verwenden, oder du kannst die Flattr-Seite der Sache im Netz besuchen.</string>
<string name="authenticate_now_label">Authentifizieren</string>
<string name="action_forbidden_title">Aktion verboten</string>
@@ -199,7 +203,7 @@
<string name="pref_revokeAccess_title">Zugriff entziehen</string>
<string name="pref_revokeAccess_sum">Entziehe dieser Anwendung die Zugriffserlaubnis für deinen Flattr Account.</string>
<string name="pref_auto_flattr_title">Automatisches Flattrn</string>
- <string name="pref_auto_flattr_sum">Flattr Episoden, die zu 80 Prozent gespielt wurden.</string>
+ <string name="pref_auto_flattr_sum">Automatisches Flattrn konfigurieren</string>
<string name="user_interface_label">Benutzeroberfläche</string>
<string name="pref_set_theme_title">Theme auswählen</string>
<string name="pref_set_theme_sum">Ändere das Aussehen von AntennaPod.</string>
@@ -222,8 +226,15 @@
<string name="pref_gpodnet_setlogin_information_sum">Ändere die Anmeldeinformationen deines gpodder.net profils</string>
<string name="pref_playback_speed_title">Wiedergabegeschwindigkeiten</string>
<string name="pref_playback_speed_sum">Lege die verfügbaren Werte für die Veränderung der Wiedergabeschwindigkeit fest</string>
+ <string name="pref_seek_delta_title">Spul-Zeit</string>
+ <string name="pref_seek_delta_sum">Spule so viele Sekunden vor oder zurück</string>
<string name="pref_gpodnet_sethostname_title">Hostname ändern</string>
<string name="pref_gpodnet_sethostname_use_default_host">Standard-Host verwenden</string>
+ <!--Auto-Flattr dialog-->
+ <string name="auto_flattr_enable">Automatisches Flattrn aktivieren</string>
+ <string name="auto_flattr_after_percent">Flattr eine Episode sobald %d Prozent gespielt worden sind</string>
+ <string name="auto_flattr_ater_beginning">Flattr Episode, sobald die Wiedergabe beginnt</string>
+ <string name="auto_flattr_ater_end">Flattr Episode, sobald die Wiedergabe endet</string>
<!--Search-->
<string name="search_hint">Suche nach Feeds oder Episoden</string>
<string name="found_in_shownotes_label">In Sendungsnotizen gefunden</string>
diff --git a/res/values-es-rES/strings.xml b/res/values-es-rES/strings.xml
index 77fc5dafc..cd4949530 100644
--- a/res/values-es-rES/strings.xml
+++ b/res/values-es-rES/strings.xml
@@ -149,6 +149,7 @@
<string name="pref_autodl_wifi_filter_title">Activar el filtro WiFi</string>
<string name="pref_autodl_wifi_filter_sum">Permitir la descarga automática sólo para las redes WiFi marcadas.</string>
<string name="pref_episode_cache_title">Caché de episodios</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Buscar canales o episodios</string>
<string name="found_in_shownotes_label">Encontrado en las notas del programa</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index cd432280f..1b87e6dbc 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -184,7 +184,6 @@
<string name="pref_revokeAccess_title">Revocar el acceso</string>
<string name="pref_revokeAccess_sum">Rescindir el permiso de acceso de esta aplicación a su cuenta de Flattr.</string>
<string name="pref_auto_flattr_title">Uso de Flattr automático</string>
- <string name="pref_auto_flattr_sum">Hacer Flattr al reproducir el 80 por ciento de cada episodio</string>
<string name="user_interface_label">Interfaz de usuario</string>
<string name="pref_set_theme_title">Elegir un tema</string>
<string name="pref_set_theme_sum">Cambiar la apariencia de AntennaPod.</string>
@@ -209,6 +208,7 @@
<string name="pref_playback_speed_sum">Personalice las velocidades disponibles para la reproducción de audio a velocidad variable</string>
<string name="pref_gpodnet_sethostname_title">Definir nombre de equipo</string>
<string name="pref_gpodnet_sethostname_use_default_host">Usar nombre de equipo por defecto</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Buscar canales o episodios</string>
<string name="found_in_shownotes_label">Encontrado en las notas del programa</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index d87c0a1b4..76ecd8340 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -199,7 +199,6 @@
<string name="pref_revokeAccess_title">Révoquer l\'accès</string>
<string name="pref_revokeAccess_sum">Révoquer la permission d\'accès à votre compte Flattr depuis cette application.</string>
<string name="pref_auto_flattr_title">Flattr automatique</string>
- <string name="pref_auto_flattr_sum">Flattrer les épisodes dont 80 pour-cents ont été joués.</string>
<string name="user_interface_label">Interface utilisateur</string>
<string name="pref_set_theme_title">Choisir un thème</string>
<string name="pref_set_theme_sum">Modifier l\'apparence d\'AntennaPod.</string>
@@ -224,6 +223,7 @@
<string name="pref_playback_speed_sum">Modifier la liste des vitesses disponibles pour la lecture audio</string>
<string name="pref_gpodnet_sethostname_title">Choisir un nom de domaine</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utiliser le nom de domaine par défaut</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Chercher des flux ou épisodes</string>
<string name="found_in_shownotes_label">Trouvé dans les notes</string>
diff --git a/res/values-hi-rIN/strings.xml b/res/values-hi-rIN/strings.xml
index 818f3dfa9..43590f62a 100644
--- a/res/values-hi-rIN/strings.xml
+++ b/res/values-hi-rIN/strings.xml
@@ -197,6 +197,7 @@
<string name="pref_playback_speed_sum">चर गति ऑडियो प्लेबैक के लिए उपलब्ध गति बनाइए</string>
<string name="pref_gpodnet_sethostname_title">होस्टनाम सेट</string>
<string name="pref_gpodnet_sethostname_use_default_host">डिफ़ॉल्ट होस्ट का प्रयोग करें</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">फ़ीड या एपिसोड के लिए खोज</string>
<string name="found_in_shownotes_label">Shownotes में मिला</string>
diff --git a/res/values-it-rIT/strings.xml b/res/values-it-rIT/strings.xml
index 05e492ce0..9bc81c269 100644
--- a/res/values-it-rIT/strings.xml
+++ b/res/values-it-rIT/strings.xml
@@ -190,6 +190,7 @@
<string name="pref_playback_speed_sum">Personalizza le velocità disponibili per la riproduzione audio a velocità variabile</string>
<string name="pref_gpodnet_sethostname_title">Imposta l\'hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Usa l\'host di default</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Ricerca per Feed o Episodi</string>
<string name="found_in_shownotes_label">Trovato nelle note dell\'episodio</string>
diff --git a/res/values-iw-rIL/strings.xml b/res/values-iw-rIL/strings.xml
index cbd21aeea..27f4b969d 100644
--- a/res/values-iw-rIL/strings.xml
+++ b/res/values-iw-rIL/strings.xml
@@ -180,7 +180,6 @@
<string name="pref_revokeAccess_title">בטל גישה</string>
<string name="pref_revokeAccess_sum">בטל הרשאת גישה לחשבון flattr ליישום זה.</string>
<string name="pref_auto_flattr_title">תרומות Flattr אוטומטיות</string>
- <string name="pref_auto_flattr_sum">תרום לפרקים ש80% מתוכם נוגן </string>
<string name="user_interface_label">ממשק משתמש</string>
<string name="pref_set_theme_title">בחר ערכת נושא</string>
<string name="pref_set_theme_sum">שנה את מראה אנטנה-פוד</string>
@@ -205,6 +204,7 @@
<string name="pref_playback_speed_sum">התאמת המהיריות הזמינות לניגון במהירות משתנה</string>
<string name="pref_gpodnet_sethostname_title">הגדר שם שרת</string>
<string name="pref_gpodnet_sethostname_use_default_host">השתמש בשרת ברירת מידל</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">חפש הזנות או פרקים</string>
<string name="found_in_shownotes_label">נמצא בהערות פרק</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 88774e168..4d783b83a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -180,7 +180,6 @@
<string name="pref_revokeAccess_title">접근 철회</string>
<string name="pref_revokeAccess_sum">이 앱이 Flattr 계정에 접근할 권한을 철회합니다.</string>
<string name="pref_auto_flattr_title">자동 Flattr</string>
- <string name="pref_auto_flattr_sum">80% 재생한 에피소드 Flattr하기.</string>
<string name="user_interface_label">사용자 인터페이스</string>
<string name="pref_set_theme_title">테마 선택</string>
<string name="pref_set_theme_sum">안테나팟의 겉모양을 바꿉니다.</string>
@@ -205,6 +204,7 @@
<string name="pref_playback_speed_sum">여러가지 오디오 재생 속도 직접 설정</string>
<string name="pref_gpodnet_sethostname_title">호스트 이름 설정</string>
<string name="pref_gpodnet_sethostname_use_default_host">기본 호스트 사용</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">피드나 에피소드 검색</string>
<string name="found_in_shownotes_label">프로그램 메모에서 발견</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index dfcf40128..a0c852059 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -180,7 +180,6 @@
<string name="pref_revokeAccess_title">Toegang intrekken</string>
<string name="pref_revokeAccess_sum">Trek de toegang van deze app in tot je Flattr account.</string>
<string name="pref_auto_flattr_title">Automatische Flattr</string>
- <string name="pref_auto_flattr_sum">Afleveringen automatisch Flattr nadat 80 procent van de episode gespeeld is</string>
<string name="user_interface_label">User Interface</string>
<string name="pref_set_theme_title">Kies theme</string>
<string name="pref_set_theme_sum">Verander het uiterlijk van AntennaPod.</string>
@@ -205,6 +204,7 @@
<string name="pref_playback_speed_sum">Pas de beschikbare snelheden aan voor de variabele audio afspeelsnelheid</string>
<string name="pref_gpodnet_sethostname_title">Definieer hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Gebruik standaard host</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Feeds of afleveringen zoeken</string>
<string name="found_in_shownotes_label">Gevonden in de shownotes</string>
diff --git a/res/values-pl-rPL/strings.xml b/res/values-pl-rPL/strings.xml
index e1ae1d349..fc56ab6bf 100644
--- a/res/values-pl-rPL/strings.xml
+++ b/res/values-pl-rPL/strings.xml
@@ -3,19 +3,29 @@
<!--Activitiy and fragment titles-->
<string name="app_name">AntennaPod</string>
<string name="feeds_label">Kanały</string>
+ <string name="add_feed_label">Dodaj podcast</string>
<string name="podcasts_label">PODCASTY</string>
<string name="episodes_label">ODCINKI</string>
+ <string name="new_episodes_label">Nowe odcinki</string>
+ <string name="all_episodes_label">Wszystkie odcinki</string>
<string name="new_label">Nowy</string>
<string name="waiting_list_label">Lista oczekujących</string>
<string name="settings_label">Ustawienia</string>
<string name="add_new_feed_label">Dodaj podcast</string>
<string name="downloads_label">Pobrane</string>
+ <string name="downloads_running_label">W toku</string>
+ <string name="downloads_completed_label">Ukończone</string>
+ <string name="downloads_log_label">Dziennik</string>
<string name="cancel_download_label">Anuluj pobieranie</string>
<string name="playback_history_label">Historia odtwarzania</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">gpodder.net login</string>
<!--New episodes fragment-->
+ <string name="recently_published_episodes_label">Ostatnio opublikowane</string>
+ <string name="episode_filter_label">Pokaż tylko nowe odcinki</string>
<!--Main activity-->
+ <string name="drawer_open">Otwórz menu</string>
+ <string name="drawer_close">Zamknij menu</string>
<!--Webview actions-->
<string name="open_in_browser_label">Otwórz w przeglądarce</string>
<string name="copy_url_label">Kopiuj adres</string>
@@ -29,12 +39,13 @@
<string name="author_label">Autor</string>
<string name="language_label">Język</string>
<string name="podcast_settings_label">Ustawienia</string>
+ <string name="cover_label">Obraz</string>
<string name="error_label">Błąd</string>
<string name="error_msg_prefix">Wystąpił błąd:</string>
<string name="refresh_label">Odśwież</string>
<string name="external_storage_error_msg">Brak zewnętrznej pamięci. Sprawdź czy jest ona podłączona żeby aplikacja mogła pracować poprawnie.</string>
<string name="chapters_label">Rozdziały</string>
- <string name="shownotes_label">Pokaż notatki</string>
+ <string name="shownotes_label">Opis odcinka</string>
<string name="description_label">Opis</string>
<string name="most_recent_prefix">Najnowszy odcinek:\u0020</string>
<string name="episodes_suffix">:\u0020odcinków</string>
@@ -49,9 +60,14 @@
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Adres kanału</string>
<string name="txtvfeedurl_label">Dodaj podcast przez adres</string>
+ <string name="podcastdirectories_label">Znajdź podcast w folderze</string>
+ <string name="podcastdirectories_descr">Możesz wyszukiwać nowe podcasty ze względu na nazwę, kategorię lub popularność na gpodder.net</string>
+ <string name="browse_gpoddernet_label">Przeglądaj gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Oznacz wszystkie jako przeczytane</string>
+ <string name="mark_all_read_msg">Wszystkie odcinki zaznaczone jako przeczytane</string>
<string name="show_info_label">Pokaż informacje</string>
+ <string name="remove_feed_label">Usuń podcast</string>
<string name="share_link_label">Udostępnij stronę</string>
<string name="share_source_label">Udostępnij kanał</string>
<string name="feed_delete_confirmation_msg">Potwierdź chęć usunięcia tego kanału wraz ze WSZYSTKIMI odcinkami, które zostały pobrane.</string>
@@ -62,6 +78,7 @@
<string name="pause_label">Pauza</string>
<string name="stream_label">Strumień</string>
<string name="remove_label">Usuń</string>
+ <string name="remove_episode_lable">Usuń odcinek</string>
<string name="mark_read_label">Oznacz jako przeczytane</string>
<string name="mark_unread_label">Oznacz jako nieprzeczytane</string>
<string name="add_to_queue_label">Dodaj do kolejki</string>
@@ -72,10 +89,12 @@
<string name="download_all">Pobierz wszystkie</string>
<string name="skip_episode_label">Pomiń odcinek</string>
<!--Download messages and labels-->
+ <string name="download_successful">Operacja zakończona sukcesem</string>
+ <string name="download_failed">Operacja nie powiodła się</string>
<string name="download_pending">Pobieranie w toku</string>
<string name="download_running">Pobieram</string>
<string name="download_error_device_not_found">Nie znaleziono urządzenia docelowego</string>
- <string name="download_error_insufficient_space">Nie wystarczająca ilość pamięci</string>
+ <string name="download_error_insufficient_space">Niewystarczająca ilość pamięci</string>
<string name="download_error_file_error">Błąd pliku</string>
<string name="download_error_http_data_error">Błąd danych HTTP</string>
<string name="download_error_error_unknown">Nieznany błąd</string>
@@ -83,6 +102,7 @@
<string name="download_error_unsupported_type">Nieobsługiwany typ kanału</string>
<string name="download_error_connection_error">Błąd połączenia</string>
<string name="download_error_unknown_host">Nieznany host</string>
+ <string name="download_error_unauthorized">Błąd autoryzacji</string>
<string name="cancel_all_downloads_label">Anuluj wszystkie pobierania</string>
<string name="download_cancelled_msg">Pobieranie anulowane</string>
<string name="download_report_title">Pobieranie ukończone</string>
@@ -91,6 +111,7 @@
<string name="download_error_request_error">Błąd żądania</string>
<string name="download_error_db_access">Błąd dostępu do bazy danych</string>
<string name="downloads_left">:\u0020pobrań pozostało</string>
+ <string name="downloads_processing">Przetwarzanie pobranych</string>
<string name="download_notification_title">Pobieranie danych podcastu</string>
<string name="download_report_content">%1$d pobierania poprawne, %2$d nieudane</string>
<string name="download_log_title_unknown">Nieznany tytuł</string>
@@ -98,6 +119,8 @@
<string name="download_type_media">Plik multimedialny</string>
<string name="download_type_image">Obraz</string>
<string name="download_request_error_dialog_message_prefix">Wystąpił błąd przy próbie pobierania:\u0020</string>
+ <string name="authentication_notification_title">Wymagana autoryzacja</string>
+ <string name="authentication_notification_msg">Żądany zasób wymaga podania nazwy użytkownika oraz hasła</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Błąd!</string>
<string name="player_stopped_msg">Żadne media nie odtwarzane </string>
@@ -118,7 +141,7 @@
<string name="move_to_bottom_label">Przesuń na dół</string>
<!--Flattr-->
<string name="flattr_auth_label">Logowanie do Flattr</string>
- <string name="flattr_auth_explanation">Naciśnij przycisk poniżej by zacząć proces autoryzacji. Zostaniesz przekierowany na stronę logowanie do flattr w przeglądarce i zostaniesz poproszony o przyznanie zezwolenia AntennaPod-owi na flattr-owanie. Po daniu zezwolenia powrócisz do tej strony automatycznie.</string>
+ <string name="flattr_auth_explanation">Naciśnij przycisk poniżej by zacząć proces autoryzacji. Zostaniesz przekierowany na stronę logowania do flattr w przeglądarce i zostaniesz poproszony o przyznanie zezwolenia AntennaPod-owi na flattr-owanie. Po daniu zezwolenia powrócisz do tej strony automatycznie.</string>
<string name="authenticate_label">Autoryzacja</string>
<string name="return_home_label">Wróć do ekranu głównego</string>
<string name="flattr_auth_success">Autoryzacja się powiodła. Możesz teraz używać flattr w aplikacji.</string>
@@ -130,6 +153,17 @@
<string name="access_revoked_title">Anulowano dostęp</string>
<string name="access_revoked_info">Odwołałeś dostęp AntennaPod do swojego konta. W celu zakończenia procesu musisz usunąć aplikację z listy aplikacji dozwolonych na koncie Flattr.</string>
<!--Flattr-->
+ <string name="flattr_click_success">Poprawnie z-flattr-owano</string>
+ <string name="flattr_click_success_count">Z-flattr-owano %d elementów</string>
+ <string name="flattr_click_success_queue">Z-flattr-owano: %s</string>
+ <string name="flattr_click_failure_count">Flattr-owanie %d elementów nie powiodło się</string>
+ <string name="flattr_click_failure">Flattr-owanie zakończone niepowodzeniem: %s</string>
+ <string name="flattr_click_enqueued">Elementy zostaną z-flattr-owane później</string>
+ <string name="flattring_thing">Flattr-owanie %s</string>
+ <string name="flattring_label">Flattr-uję</string>
+ <string name="flattrd_label">AntennaPod z-flattr-owała</string>
+ <string name="flattrd_failed_label">Flattr-owanie AntennaPod nie powiodło się</string>
+ <string name="flattr_retrieving_status">Wyszukiwanie z-flattr-owanych elementów</string>
<!--Variable Speed-->
<string name="download_plugin_label">Pobierz wtyczkę</string>
<string name="no_playback_plugin_title">Wtyczka nie zainstalowana</string>
@@ -189,6 +223,7 @@
<string name="pref_playback_speed_sum">Dostosuj prędkości dostępne dla odtwarzania audio o zmiennej prędkości</string>
<string name="pref_gpodnet_sethostname_title">Ustaw nazwę hosta</string>
<string name="pref_gpodnet_sethostname_use_default_host">Użyj domyślnego hosta</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Szukaj kanałów lub odcinków</string>
<string name="found_in_shownotes_label">Znaleziono w notatkach</string>
@@ -220,6 +255,9 @@
<string name="sleep_timer_label">Wyłącznik czasowy</string>
<string name="time_left_label">Pozostały czas:\u0020</string>
<string name="time_dialog_invalid_input">Błąd wpisu, czas musi być liczbą całkowitą</string>
+ <string name="time_unit_seconds">sekundy</string>
+ <string name="time_unit_minutes">minuty</string>
+ <string name="time_unit_hours">godziny</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIE</string>
<string name="gpodnet_toplist_header">TOP PODCASTY</string>
@@ -268,10 +306,14 @@
<string name="downloading_label">Pobieranie...</string>
<!--Content descriptions for image buttons-->
<string name="show_chapters_label">Pokaż rozdziały</string>
+ <string name="show_shownotes_label">Pokaż opis odcinka</string>
+ <string name="show_cover_label">Pokaż obraz</string>
<string name="rewind_label">Cofnij</string>
<string name="fast_forward_label">Przewiń</string>
<string name="media_type_audio_label">Audio</string>
<string name="media_type_video_label">Wideo</string>
+ <string name="navigate_upwards_label">Przesuń w górę</string>
+ <string name="butAction_label">Więcej akcji</string>
<string name="status_playing_label">Odcinek jest odtwarzany</string>
<string name="status_downloading_label">Odcinek jest pobierany</string>
<string name="status_downloaded_label">Odcinek pobrany</string>
@@ -279,6 +321,10 @@
<string name="in_queue_label">Odcinek jest w kolejce</string>
<string name="new_episodes_count_label">Liczba nowych odcinków</string>
<string name="in_progress_episodes_count_label">Liczba odcinków, których zacząłeś słuchać</string>
+ <string name="drag_handle_content_description">Przeciągnij aby zmienić pozycję elementu</string>
<!--Feed information screen-->
+ <string name="authentication_label">Autoryzacja</string>
+ <string name="authentication_descr">Zmień swoją nazwę użytkownika oraz hasło dla tego podcastu i jego odcinków</string>
<!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Importowanie subskrybcji z jednozadaniowych aplikacji</string>
</resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index d028fd43c..62fd9c046 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -189,6 +189,7 @@
<string name="pref_playback_speed_sum">Personalize as velocidades variáveis de reprodução de áudio.</string>
<string name="pref_gpodnet_sethostname_title">Configurar hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Usar host padrão</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Procurar por Feeds ou Episódios</string>
<string name="found_in_shownotes_label">Encontrado nas notas do podcast</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 68c1a67cd..1a49ee1d5 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -199,7 +199,6 @@
<string name="pref_revokeAccess_title">Revogar acesso</string>
<string name="pref_revokeAccess_sum">Revogar permissões de acesso da aplicação à sua conta flattr.</string>
<string name="pref_auto_flattr_title">Flattr automático</string>
- <string name="pref_auto_flattr_sum">Flattr de episódios com 80 porcento de reprodução.</string>
<string name="user_interface_label">Interface</string>
<string name="pref_set_theme_title">Tema</string>
<string name="pref_set_theme_sum">Mudar o aspeto do AntennaPod.</string>
@@ -224,6 +223,7 @@
<string name="pref_playback_speed_sum">Personalize as velocidades de reprodução disponíveis.</string>
<string name="pref_gpodnet_sethostname_title">Definir nome de servidor</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utilizar pré-definição</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Procurar fontes ou episódios</string>
<string name="found_in_shownotes_label">Encontrado nas notas</string>
diff --git a/res/values-ro-rRO/strings.xml b/res/values-ro-rRO/strings.xml
index 91be96b6b..a6e782f74 100644
--- a/res/values-ro-rRO/strings.xml
+++ b/res/values-ro-rRO/strings.xml
@@ -174,6 +174,7 @@
<string name="pref_gpodnet_authenticate_title">Autentificare</string>
<string name="pref_playback_speed_title">Viteze de ascutare</string>
<string name="pref_playback_speed_sum">Modifică vitezele disponibile pentru viteza de ascultare.</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Caută feeduri sau episoade</string>
<string name="found_in_shownotes_label">Găsit în notițe</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index f54283e5f..c5c642da0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -186,7 +186,6 @@
<string name="pref_revokeAccess_title">Отозвать доступ</string>
<string name="pref_revokeAccess_sum">Отменить доступ этого приложения к вашему аккаунту Flattr.</string>
<string name="pref_auto_flattr_title">Автоматически поддерживать через Flattr</string>
- <string name="pref_auto_flattr_sum">Поддерживать через Flattr эпизоды, прослушанные на 80%</string>
<string name="user_interface_label">Интерфейс</string>
<string name="pref_set_theme_title">Выбор темы</string>
<string name="pref_set_theme_sum">Изменить тему оформления AntennaPod</string>
@@ -211,6 +210,7 @@
<string name="pref_playback_speed_sum">Настроить скорости воспроизведения</string>
<string name="pref_gpodnet_sethostname_title">Задать имя узла</string>
<string name="pref_gpodnet_sethostname_use_default_host">Использовать узел по умолчанию</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Поиск каналов или выпусков</string>
<string name="found_in_shownotes_label">Найдено в описании выпуска</string>
diff --git a/res/values-sv-rSE/strings.xml b/res/values-sv-rSE/strings.xml
index d942000d1..beda2187e 100644
--- a/res/values-sv-rSE/strings.xml
+++ b/res/values-sv-rSE/strings.xml
@@ -199,7 +199,6 @@
<string name="pref_revokeAccess_title">Återkalla åtkomst</string>
<string name="pref_revokeAccess_sum">Återkalla behörigheten till ditt Flattr-konto för denna app.</string>
<string name="pref_auto_flattr_title">Automatisk Flattring</string>
- <string name="pref_auto_flattr_sum">Flattra episoder som har spelats minst 80%.</string>
<string name="user_interface_label">Användargränssnitt</string>
<string name="pref_set_theme_title">Välj tema</string>
<string name="pref_set_theme_sum">Ändra utseendet på AntennaPod.</string>
@@ -224,6 +223,7 @@
<string name="pref_playback_speed_sum">Anpassa de tillgängliga hastigheterna för variabel uppspelningshastighet.</string>
<string name="pref_gpodnet_sethostname_title">Sätt värdnamn</string>
<string name="pref_gpodnet_sethostname_use_default_host">Använd standardvärden</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Sök efter flöden eller avsnitt</string>
<string name="found_in_shownotes_label">Hittad i shownotes</string>
diff --git a/res/values-uk-rUA/strings.xml b/res/values-uk-rUA/strings.xml
index ff9ea728f..6653e6614 100644
--- a/res/values-uk-rUA/strings.xml
+++ b/res/values-uk-rUA/strings.xml
@@ -198,7 +198,6 @@
<string name="pref_revokeAccess_title">Відкликати доступ</string>
<string name="pref_revokeAccess_sum">Відкликати дозвіл на доступ до вашого flattr з цього додатку</string>
<string name="pref_auto_flattr_title">Automatic Flattr</string>
- <string name="pref_auto_flattr_sum">Flattr episodes of which 80% have been played.</string>
<string name="user_interface_label">Зовнішній вид</string>
<string name="pref_set_theme_title">Обрати тему</string>
<string name="pref_set_theme_sum">Змінити появу AntennaPod</string>
@@ -223,6 +222,7 @@
<string name="pref_playback_speed_sum">Налаштування швідкості доступно для змінної швидкості програвання</string>
<string name="pref_gpodnet_sethostname_title">Встановити ім\'я хоста</string>
<string name="pref_gpodnet_sethostname_use_default_host">Використати хост по замовчанню</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">Пошук каналів та епізодів</string>
<string name="found_in_shownotes_label">Знайдено у примітках</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index d58c76833..63320b851 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -210,6 +210,7 @@
<string name="pref_playback_speed_sum">自定义音频播放速度</string>
<string name="pref_gpodnet_sethostname_title">设置主机名</string>
<string name="pref_gpodnet_sethostname_use_default_host">使用默认主机</string>
+ <!--Auto-Flattr dialog-->
<!--Search-->
<string name="search_hint">搜索订阅或者曲目</string>
<string name="found_in_shownotes_label">笔记中查找</string>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 69ea13e52..f09c76080 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <string-array name="seek_delta_values">
+ <item>5</item>
+ <item>10</item>
+ <item>15</item>
+ <item>20</item>
+ <item>30</item>
+ <item>45</item>
+ <item>60</item>
+ </string-array>
+
<string-array name="update_intervall_options">
<item>Manual</item>
<item>1 hour</item>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index c1d80d375..90e405fde 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -12,6 +12,7 @@
<item name="open_in_browser_item" type="id"/>
<item name="copy_url_item" type="id"/>
<item name="share_url_item" type="id"/>
+ <item name="go_to_position_item" type="id"/>
<item name="organize_queue_item" type="id"/>
<item name="drag_handle" type="id"/>
<item name="skip_episode_item" type="id"/>
@@ -21,5 +22,6 @@
<item name="imageloader_key" type="id"/>
<item name="notification_gpodnet_sync_error" type="id"/>
<item name="notification_gpodnet_sync_autherror" type="id"/>
-
+ <item name="undobar_button" type="id"/>
+ <item name="undobar_message" type="id"/>
</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8f81955e2..b5cc4ee86 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,7 @@
<string name="copy_url_label">Copy URL</string>
<string name="share_url_label">Share URL</string>
<string name="copied_url_msg">Copied URL to clipboard.</string>
+ <string name="go_to_position_label">Go to this position</string>
<!-- Playback history -->
<string name="clear_history_label">Clear history</string>
@@ -69,6 +70,7 @@
<!-- 'Add Feed' Activity labels -->
<string name="feedurl_label">Feed URL</string>
+ <string name="etxtFeedurlHint">URL of feed or website</string>
<string name="txtvfeedurl_label">Add Podcast by URL</string>
<string name="podcastdirectories_label">Find podcast in directory</string>
<string name="podcastdirectories_descr">You can search for new podcasts by name, category or popularity in the gpodder.net directory.</string>
@@ -147,6 +149,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">Buffering</string>
<string name="playbackservice_notification_title">Playing podcast</string>
+ <string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string>
<!-- Queue operations -->
<string name="clear_queue_label">Clear queue</string>
@@ -162,6 +165,7 @@
<string name="return_home_label">Return to home</string>
<string name="flattr_auth_success">Authentication was successful! You can now flattr things within the app.</string>
<string name="no_flattr_token_title">No Flattr token found</string>
+ <string name="no_flattr_token_notification_msg">Your flattr account does not seem to be connected to AntennaPod. Tap here to authenticate.</string>
<string name="no_flattr_token_msg">Your flattr account does not seem to be connected to AntennaPod. You can either connect your account to AntennaPod to flattr things within the app or you can visit the website of the thing to flattr it there.</string>
<string name="authenticate_now_label">Authenticate</string>
<string name="action_forbidden_title">Action forbidden</string>
@@ -219,7 +223,7 @@
<string name="pref_revokeAccess_title">Revoke access</string>
<string name="pref_revokeAccess_sum">Revoke the access permission to your flattr account for this app.</string>
<string name="pref_auto_flattr_title">Automatic Flattr</string>
- <string name="pref_auto_flattr_sum">Flattr episodes of which 80% have been played.</string>
+ <string name="pref_auto_flattr_sum">Configure automatic flattring</string>
<string name="user_interface_label">User Interface</string>
<string name="pref_set_theme_title">Select theme</string>
<string name="pref_set_theme_sum">Change the appearance of AntennaPod.</string>
@@ -242,9 +246,16 @@
<string name="pref_gpodnet_setlogin_information_sum">Change the login information for your gpodder.net account.</string>
<string name="pref_playback_speed_title">Playback Speeds</string>
<string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string>
+ <string name="pref_seek_delta_title">Seek time</string>
+ <string name="pref_seek_delta_sum">Seek this many seconds when rewinding or fast-forwarding</string>
<string name="pref_gpodnet_sethostname_title">Set hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Use default host</string>
-
+
+ <!-- Auto-Flattr dialog -->
+ <string name="auto_flattr_enable">Enable automatic flattring</string>
+ <string name="auto_flattr_after_percent">Flattr episode as soon as %d percent have been played</string>
+ <string name="auto_flattr_ater_beginning">Flattr episode when playback starts</string>
+ <string name="auto_flattr_ater_end">Flattr episode when playback ends</string>
<!-- Search -->
<string name="search_hint">Search for Feeds or Episodes</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7477712b2..e42072afa 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -145,4 +145,30 @@
<item name="android:textColor">@color/new_indicator_green</item>
<item name="android:text">@string/new_label</item>
</style>
+
+ <style name="AntennaPodBetterPickerThemeLight">
+ <item name="bpDialogBackground">@drawable/dialog_full_holo_light</item>
+ <item name="bpTitleColor">@color/dialog_text_color_holo_light</item>
+ <item name="bpTextColor">@color/dialog_text_color_holo_light</item>
+ <item name="bpDeleteIcon">@drawable/ic_backspace_light</item>
+ <item name="bpCheckIcon">@drawable/ic_check_light</item>
+ <item name="bpKeyBackground">@drawable/borderless_button</item>
+ <item name="bpButtonBackground">@drawable/borderless_button</item>
+ <item name="bpTitleDividerColor">@color/default_keyboard_indicator_color_dark</item>
+ <item name="bpDividerColor">@color/default_divider_color_light</item>
+ <item name="bpKeyboardIndicatorColor">@color/selection_background_color_light</item>
+ </style>
+
+ <style name="AntennaPodBetterPickerThemeDark">
+ <item name="bpDialogBackground">@drawable/dialog_full_holo_dark</item>
+ <item name="bpTitleColor">@color/dialog_text_color_holo_dark</item>
+ <item name="bpTextColor">@color/dialog_text_color_holo_dark</item>
+ <item name="bpDeleteIcon">@drawable/ic_backspace_dark</item>
+ <item name="bpCheckIcon">@drawable/ic_check_dark</item>
+ <item name="bpKeyBackground">@drawable/borderless_button_dark</item>
+ <item name="bpButtonBackground">@drawable/borderless_button_dark</item>
+ <item name="bpTitleDividerColor">@color/default_keyboard_indicator_color_dark</item>
+ <item name="bpDividerColor">@color/default_divider_color_dark</item>
+ <item name="bpKeyboardIndicatorColor">@color/selection_background_color_dark</item>
+ </style>
</resources>
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 8e0b886de..470e1a1fe 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -34,6 +34,15 @@
android:key="prefPauseForFocusLoss"
android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
android:title="@string/pref_pausePlaybackForFocusLoss_title" />
+
+ <ListPreference
+ android:defaultValue="30"
+ android:entries="@array/seek_delta_values"
+ android:entryValues="@array/seek_delta_values"
+ android:key="prefSeekDeltaSecs"
+ android:summary="@string/pref_seek_delta_sum"
+ android:title="@string/pref_seek_delta_title" />
+
</PreferenceCategory>
<PreferenceCategory android:title="@string/network_pref">
<ListPreference
@@ -83,10 +92,8 @@
<intent android:action=".activities.FlattrAuthActivity"/>
</PreferenceScreen>
- <CheckBoxPreference
- android:defaultValue="false"
- android:enabled="false"
- android:key="pref_auto_flattr"
+ <Preference
+ android:key="prefAutoFlattrPrefs"
android:summary="@string/pref_auto_flattr_sum"
android:title="@string/pref_auto_flattr_title" />
<Preference
diff --git a/src/com/aocate/media/MediaPlayer.java b/src/com/aocate/media/MediaPlayer.java
index 995dbbc51..04ecd58a9 100644
--- a/src/com/aocate/media/MediaPlayer.java
+++ b/src/com/aocate/media/MediaPlayer.java
@@ -32,6 +32,8 @@ import android.os.Message;
import android.os.Handler.Callback;
import android.util.Log;
+import de.danoeh.antennapod.BuildConfig;
+
public class MediaPlayer {
public interface OnBufferingUpdateListener {
public abstract void onBufferingUpdate(MediaPlayer arg0, int percent);
@@ -309,7 +311,7 @@ public class MediaPlayer {
Log.d(MP_TAG, "this.mpi is not a ServiceBackedMediaPlayer, but we couldn't use it anyway 321");
return false;
} else {
- assert (this.mpi instanceof ServiceBackedMediaPlayer);
+ if (BuildConfig.DEBUG && !(this.mpi instanceof ServiceBackedMediaPlayer)) throw new AssertionError();
if (this.useService && isPrestoLibraryInstalled()) {
// We should be using the service, and we are. Great!
Log.d(MP_TAG, "We could be using a ServiceBackedMediaPlayer and we are 327");
diff --git a/src/de/danoeh/antennapod/AppConfig.java b/src/de/danoeh/antennapod/AppConfig.java
index cac946f84..0e12a350f 100644
--- a/src/de/danoeh/antennapod/AppConfig.java
+++ b/src/de/danoeh/antennapod/AppConfig.java
@@ -2,6 +2,6 @@ package de.danoeh.antennapod;
public final class AppConfig {
/** Should be used when setting User-Agent header for HTTP-requests. */
- public final static String USER_AGENT = "AntennaPod/0.9.9.1";
+ public final static String USER_AGENT = "AntennaPod/0.9.9.2";
}
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 090c3f1f5..6373ff240 100644
--- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -12,32 +12,48 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
-import android.view.Window;
-import android.widget.*;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import de.danoeh.antennapod.feed.*;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.MediaType;
+import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.fragment.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
+import de.danoeh.antennapod.util.playback.PlaybackController;
/**
* Activity for playing audio files.
*/
-public class AudioplayerActivity extends MediaplayerActivity {
+public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback,
+ NavDrawerActivity {
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
@@ -123,7 +139,6 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowTitleEnabled(false);
detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS];
@@ -215,8 +230,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
- if (getIntent().getAction() != null
- && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
+ if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent();
if (BuildConfig.DEBUG)
Log.d(TAG, "Received VIEW intent: "
@@ -293,7 +307,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
case POS_DESCR:
if (descriptionFragment == null) {
descriptionFragment = ItemDescriptionFragment
- .newInstance(media, true);
+ .newInstance(media, true, true);
}
currentlyShownFragment = descriptionFragment;
break;
@@ -427,6 +441,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
};
typedArray.recycle();
drawerToggle.setDrawerIndicatorEnabled(false);
+ drawerLayout.setDrawerListener(drawerToggle);
navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter);
@@ -603,6 +618,34 @@ public class AudioplayerActivity extends MediaplayerActivity {
clearStatusMsg();
}
+ @Override
+ public PlaybackController getPlaybackController() {
+ return controller;
+ }
+
+ @Override
+ public boolean isDrawerOpen() {
+ return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (!MenuItemUtils.isActivityDrawerOpen(this)) {
+ return super.onCreateOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (!MenuItemUtils.isActivityDrawerOpen(this)) {
+ return super.onPrepareOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
+
public interface AudioplayerContentFragment {
public void onDataSetChanged(Playable media);
}
@@ -615,7 +658,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToggle.onOptionsItemSelected(item)) {
+ if (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) {
return true;
} else {
return super.onOptionsItemSelected(item);
diff --git a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
index 597189885..e89f8d05c 100644
--- a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
@@ -5,19 +5,26 @@ import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
+import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.asynctask.ImageDiskCache;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.examples.HtmlToPlainText;
+import org.jsoup.nodes.Document;
import java.util.ArrayList;
import java.util.Date;
@@ -25,9 +32,11 @@ import java.util.List;
import java.util.Map;
/**
- * Created by daniel on 24.08.13.
+ * Default implementation of OnlineFeedViewActivity. Shows the downloaded feed's items with their descriptions,
+ * a subscribe button and a spinner for choosing alternate feed URLs.
*/
public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
+ private static final String TAG = "DefaultOnlineFeedViewActivity";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds;
@@ -64,6 +73,24 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
}
@Override
+ protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
+ super.beforeShowFeedInformation(feed, alternateFeedUrls);
+
+ // remove HTML tags from descriptions
+
+ if (BuildConfig.DEBUG) Log.d(TAG, "Removing HTML from shownotes");
+ if (feed.getItems() != null) {
+ HtmlToPlainText formatter = new HtmlToPlainText();
+ for (FeedItem item : feed.getItems()) {
+ if (item.getDescription() != null) {
+ Document description = Jsoup.parse(item.getDescription());
+ item.setDescription(StringUtils.trim(formatter.getPlainText(description)));
+ }
+ }
+ }
+ }
+
+ @Override
protected void showFeedInformation(final Feed feed, final Map<String, String> alternateFeedUrls) {
super.showFeedInformation(feed, alternateFeedUrls);
setContentView(R.layout.listview_activity);
@@ -74,7 +101,7 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
ListView listView = (ListView) findViewById(R.id.listview);
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View header = inflater.inflate(R.layout.onlinefeedview_header, null);
+ View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
listView.addHeaderView(header);
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
@@ -131,7 +158,7 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
for (String url : alternateFeedUrls.keySet()) {
alternateUrlsTitleList.add(alternateFeedUrls.get(url));
}
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spAlternateUrls.setAdapter(adapter);
spAlternateUrls.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
diff --git a/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
index 4b8420e45..c5f25d813 100644
--- a/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -9,6 +9,9 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -58,7 +61,8 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
butCancel = (Button) findViewById(R.id.butCancel);
txtvDescription = (TextView) findViewById(R.id.txtvDescription);
- if (!getIntent().hasExtra(ARG_DOWNLOAD_REQUEST)) throw new IllegalArgumentException("Download request missing");
+ Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
+
request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java
index 257bea82d..b7014dab2 100644
--- a/src/de/danoeh/antennapod/activity/MainActivity.java
+++ b/src/de/danoeh/antennapod/activity/MainActivity.java
@@ -19,6 +19,9 @@ import android.util.Log;
import android.view.*;
import android.widget.AdapterView;
import android.widget.ListView;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
@@ -28,13 +31,14 @@ import de.danoeh.antennapod.fragment.*;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.StorageUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
/**
* The activity that is shown when the user launches the app.
*/
-public class MainActivity extends ActionBarActivity {
+public class MainActivity extends ActionBarActivity implements NavDrawerActivity{
private static final String TAG = "MainActivity";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
| EventDistributor.DOWNLOAD_QUEUED
@@ -61,7 +65,7 @@ public class MainActivity extends ActionBarActivity {
private ListView navList;
private NavListAdapter navAdapter;
- private ActionBarDrawerToggle drawerToogle;
+ private ActionBarDrawerToggle drawerToggle;
private CharSequence drawerTitle;
private CharSequence currentTitle;
@@ -71,7 +75,7 @@ public class MainActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
@@ -82,7 +86,7 @@ public class MainActivity extends ActionBarActivity {
navList = (ListView) findViewById(R.id.nav_list);
TypedArray typedArray = obtainStyledAttributes(new int[]{R.attr.nav_drawer_toggle});
- drawerToogle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) {
+ drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
@@ -101,7 +105,7 @@ public class MainActivity extends ActionBarActivity {
};
typedArray.recycle();
- drawerLayout.setDrawerListener(drawerToogle);
+ drawerLayout.setDrawerListener(drawerToggle);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
@@ -147,6 +151,10 @@ public class MainActivity extends ActionBarActivity {
return getSupportActionBar();
}
+ public boolean isDrawerOpen() {
+ return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
+ }
+
public List<Feed> getFeeds() {
return (navDrawerData != null) ? navDrawerData.feeds : null;
}
@@ -219,7 +227,7 @@ public class MainActivity extends ActionBarActivity {
}
public void loadChildFragment(Fragment fragment) {
- if (fragment == null) throw new IllegalArgumentException("fragment = null");
+ Validate.notNull(fragment);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.replace(R.id.main_view, fragment, "main")
@@ -244,7 +252,7 @@ public class MainActivity extends ActionBarActivity {
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- drawerToogle.syncState();
+ drawerToggle.syncState();
if (savedInstanceState != null) {
currentTitle = savedInstanceState.getString("title");
if (!drawerLayout.isDrawerOpen(navList)) {
@@ -257,7 +265,7 @@ public class MainActivity extends ActionBarActivity {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- drawerToogle.onConfigurationChanged(newConfig);
+ drawerToggle.onConfigurationChanged(newConfig);
}
@Override
@@ -296,7 +304,7 @@ public class MainActivity extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToogle.onOptionsItemSelected(item)) {
+ if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
@@ -311,7 +319,6 @@ public class MainActivity extends ActionBarActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
-
return true;
}
diff --git a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
index fc70f4c05..13e7b8a82 100644
--- a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -3,19 +3,25 @@ package de.danoeh.antennapod.activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.Window;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import com.doomonafireball.betterpickers.hmspicker.HmsPickerBuilder;
+import com.doomonafireball.betterpickers.hmspicker.HmsPickerDialogFragment;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.dialog.TimeDialog;
@@ -30,6 +36,7 @@ import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.util.playback.MediaPlayerError;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.PlaybackController;
+import org.shredzone.flattr4j.model.User;
/**
* Provides general features which are both needed for playing audio and video
@@ -164,6 +171,10 @@ public abstract class MediaplayerActivity extends ActionBarActivity
protected void onCreate(Bundle savedInstanceState) {
chooseTheme();
super.onCreate(savedInstanceState);
+
+ // subclasses might use this feature
+ supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
if (BuildConfig.DEBUG)
Log.d(TAG, "Creating Activity");
StorageUtils.checkStorageAvailability(this);
@@ -312,16 +323,34 @@ public abstract class MediaplayerActivity extends ActionBarActivity
break;
case R.id.set_sleeptimer_item:
if (controller.serviceAvailable()) {
- TimeDialog td = new TimeDialog(this,
- R.string.set_sleeptimer_label,
- R.string.set_sleeptimer_label) {
-
- @Override
- public void onTimeEntered(long millis) {
- controller.setSleepTimer(millis);
- }
- };
- td.show();
+ int pickerStyle = (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Light) ?
+ R.style.AntennaPodBetterPickerThemeLight : R.style.AntennaPodBetterPickerThemeDark;
+ if (Build.VERSION.SDK_INT > 10) { // TODO remove this as soon as dialog is shown correctly on 2.3
+ HmsPickerBuilder hpb = new HmsPickerBuilder()
+ .setStyleResId(pickerStyle)
+ .setFragmentManager(getSupportFragmentManager());
+
+ hpb.addHmsPickerDialogHandler(new HmsPickerDialogFragment.HmsPickerDialogHandler() {
+ @Override
+ public void onDialogHmsSet(int ref, int hours, int minutes, int seconds) {
+ if (controller != null && controller.serviceAvailable()) {
+ controller.setSleepTimer((hours * 3600 + minutes * 60 + seconds) * 1000);
+ }
+ }
+ });
+ hpb.show();
+ } else {
+ TimeDialog td = new TimeDialog(this,
+ R.string.set_sleeptimer_label,
+ R.string.set_sleeptimer_label) {
+
+ @Override
+ public void onTimeEntered(long millis) {
+ controller.setSleepTimer(millis);
+ }
+ };
+ td.show();
+ }
break;
}
diff --git a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index e397ff2ca..2c6d75cd8 100644
--- a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -9,6 +9,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
+import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
@@ -29,13 +30,16 @@ import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.FileNameGenerator;
import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.util.URLChecker;
+import de.danoeh.antennapod.util.syndication.FeedDiscoverer;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Map;
/**
@@ -127,6 +131,13 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
}
+ private void resetIntent(String url, String title) {
+ Intent intent = new Intent();
+ intent.putExtra(ARG_FEEDURL, url);
+ intent.putExtra(ARG_TITLE, title);
+ setIntent(intent);
+ }
+
private void onDownloadCompleted(final Downloader downloader) {
runOnUiThread(new Runnable() {
@@ -244,8 +255,15 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
e.printStackTrace();
reasonDetailed = e.getMessage();
} catch (UnsupportedFeedtypeException e) {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
+ if (BuildConfig.DEBUG) Log.d(TAG, "Unsupported feed type detected");
+ if (StringUtils.equalsIgnoreCase("html", e.getRootElement())) {
+ if (showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url())) {
+ return;
+ }
+ } else {
+ e.printStackTrace();
+ reasonDetailed = e.getMessage();
+ }
} finally {
boolean rc = new File(feed.getFile_url()).delete();
if (BuildConfig.DEBUG)
@@ -253,6 +271,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
if (successful) {
+ beforeShowFeedInformation(feed, alternateFeedUrls);
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -285,7 +304,16 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
/**
- * Called when feed parsed successfully
+ * Called after the feed has been downloaded and parsed and before showFeedInformation is called.
+ * This method is executed on a background thread
+ */
+ protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
+
+ }
+
+ /**
+ * Called when feed parsed successfully.
+ * This method is executed on the GUI thread.
*/
protected void showFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
@@ -316,9 +344,67 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
finish();
}
});
+ builder.show();
}
}
+ private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) {
+ FeedDiscoverer fd = new FeedDiscoverer();
+ final Map<String, String> urlsMap;
+ try {
+ urlsMap = fd.findLinks(feedFile, baseUrl);
+ if (urlsMap == null || urlsMap.isEmpty()) {
+ return false;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (isPaused || isFinishing()) {
+ return;
+ }
+
+ final List<String> titles = new ArrayList<String>();
+ final List<String> urls = new ArrayList<String>();
+
+ urls.addAll(urlsMap.keySet());
+ for (String url : urls) {
+ titles.add(urlsMap.get(url));
+ }
+
+ final ArrayAdapter<String> adapter = new ArrayAdapter<String>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
+ DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String selectedUrl = urls.get(which);
+ dialog.dismiss();
+ resetIntent(selectedUrl, titles.get(which));
+ startFeedDownload(selectedUrl, null, null);
+ }
+ };
+
+ AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
+ .setTitle(R.string.feeds_label)
+ .setCancelable(true)
+ .setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ })
+ .setAdapter(adapter, onClickListener);
+ ab.show();
+ }
+ });
+
+
+ return true;
+ }
+
private class FeedViewAuthenticationDialog extends AuthenticationDialog {
private String feedUrl;
@@ -339,5 +425,4 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
startFeedDownload(feedUrl, username, password);
}
}
-
}
diff --git a/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
index 5f2ea0401..e09941abf 100644
--- a/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
+++ b/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
@@ -103,11 +103,11 @@ public class OpmlFeedChooserActivity extends ActionBarActivity {
super.onCreateOptionsMenu(menu);
MenuItemCompat.setShowAsAction(menu.add(Menu.NONE, R.id.select_all_item, Menu.NONE,
R.string.select_all_label),
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
MenuItemCompat.setShowAsAction(menu.add(Menu.NONE, R.id.deselect_all_item, Menu.NONE,
R.string.deselect_all_label),
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
return true;
}
diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
index 77ec579ed..a62ad3ae9 100644
--- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -15,7 +15,6 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceScreen;
-import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -25,6 +24,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.preferences.GpodnetPreferences;
@@ -47,7 +47,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
private static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
- private static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
+ private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
private static final String PREF_OPML_EXPORT = "prefOpmlExport";
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
@@ -69,7 +69,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
super.onCreate(savedInstanceState);
if (android.os.Build.VERSION.SDK_INT >= 11) {
- ActionBar ab = getActionBar();
+ @SuppressLint("AppCompatMethod") ActionBar ab = getActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
@@ -247,6 +247,26 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
return true;
}
});
+
+ findPreference(PREF_AUTO_FLATTR_PREFS).setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(PreferenceActivity.this,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
+
+ }
+
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(PreferenceActivity.this, autoFlattrEnabled, autoFlattrValue);
+ checkItemVisibility();
+ }
+ });
+ return true;
+ }
+ });
buildUpdateIntervalPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
@@ -314,7 +334,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
findPreference(PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
findPreference(PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
- findPreference(PREF_AUTO_FLATTR).setEnabled(hasFlattrToken);
+ findPreference(PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
.setEnabled(UserPreferences.isEnableAutodownload());
diff --git a/src/de/danoeh/antennapod/activity/StorageErrorActivity.java b/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
index 2cd56ba8b..d8a137eb9 100644
--- a/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -7,6 +7,9 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -54,7 +57,7 @@ public class StorageErrorActivity extends ActionBarActivity {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
if (intent.getBooleanExtra("read-only", true)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Media was mounted; Finishing activity");
diff --git a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
index c45a3d162..46fa98c49 100644
--- a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -6,14 +6,19 @@ import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.support.v4.view.WindowCompat;
import android.util.Log;
import android.util.Pair;
-import android.view.*;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.MediaType;
@@ -48,7 +53,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= 11) {
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
@@ -126,7 +131,8 @@ public class VideoplayerActivity extends MediaplayerActivity {
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
@@ -304,6 +310,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE);
}
+ @SuppressLint("NewApi")
private void showVideoControls() {
videoOverlay.setVisibility(View.VISIBLE);
butPlay.setVisibility(View.VISIBLE);
@@ -318,6 +325,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
+ @SuppressLint("NewApi")
private void hideVideoControls() {
final Animation animation = AnimationUtils.loadAnimation(this,
R.anim.fade_out);
diff --git a/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 05048f079..cb6dc41cf 100644
--- a/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ b/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
@@ -67,9 +67,9 @@ public class GpodnetAuthenticationActivity extends ActionBarActivity {
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
views = new View[]{
- inflater.inflate(R.layout.gpodnetauth_credentials, null),
- inflater.inflate(R.layout.gpodnetauth_device, null),
- inflater.inflate(R.layout.gpodnetauth_finish, null)
+ inflater.inflate(R.layout.gpodnetauth_credentials, viewFlipper, false),
+ inflater.inflate(R.layout.gpodnetauth_device, viewFlipper, false),
+ inflater.inflate(R.layout.gpodnetauth_finish, viewFlipper, false)
};
for (View view : views) {
viewFlipper.addView(view);
diff --git a/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index 17c61a86c..1de071a73 100644
--- a/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -4,6 +4,9 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.view.View;
import android.widget.ImageButton;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
@@ -20,11 +23,12 @@ public class ActionButtonUtils {
private final Context context;
public ActionButtonUtils(Context context) {
- if (context == null) throw new IllegalArgumentException("context = null");
+ Validate.notNull(context);
+
this.context = context;
drawables = context.obtainStyledAttributes(new int[]{
- R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.navigation_chapters});
- labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label};
+ R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.navigation_chapters, R.attr.navigation_accept});
+ labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label, R.string.mark_read_label};
}
/**
@@ -32,7 +36,8 @@ public class ActionButtonUtils {
* action button so that it matches the state of the FeedItem.
*/
public void configureActionButton(ImageButton butSecondary, FeedItem item) {
- if (butSecondary == null || item == null) throw new IllegalArgumentException("butSecondary or item was null");
+ Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
+
final FeedMedia media = item.getMedia();
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
@@ -61,7 +66,13 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[0]));
}
} else {
- butSecondary.setVisibility(View.INVISIBLE);
+ if (item.isRead()) {
+ butSecondary.setVisibility(View.INVISIBLE);
+ } else {
+ butSecondary.setVisibility(View.VISIBLE);
+ butSecondary.setImageDrawable(drawables.getDrawable(4));
+ butSecondary.setContentDescription(context.getString(labels[3]));
+ }
}
}
}
diff --git a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
index 72ad6774f..c12de6ebd 100644
--- a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -51,7 +51,7 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.simplechapter_item, null);
+ convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
diff --git a/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 3acd587af..0c4cbe685 100644
--- a/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -2,11 +2,15 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.widget.Toast;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DBTasks;
+import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -19,31 +23,35 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
private final Context context;
public DefaultActionButtonCallback(Context context) {
- if (context == null) throw new IllegalArgumentException("context = null");
+ Validate.notNull(context);
this.context = context;
}
@Override
public void onActionButtonPressed(final FeedItem item) {
- final FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
- boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
- if (!isDownloading && !media.isDownloaded()) {
- try {
- DBTasks.downloadFeedItems(context, item);
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+
+ if (item.hasMedia()) {
+ final FeedMedia media = item.getMedia();
+ boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
+ if (!isDownloading && !media.isDownloaded()) {
+ try {
+ DBTasks.downloadFeedItems(context, item);
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ } else if (isDownloading) {
+ DownloadRequester.getInstance().cancelDownload(context, media);
+ Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
+ } else { // media is downloaded
+ DBTasks.playMedia(context, media, true, true, false);
+ }
+ } else {
+ if (!item.isRead()) {
+ DBWriter.markItemRead(context, item, true, true);
}
- } else if (isDownloading) {
- DownloadRequester.getInstance().cancelDownload(context, media);
- Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
- } else { // media is downloaded
- DBTasks.playMedia(context, media, true, true, false);
}
}
}
diff --git a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index e97d69494..2cc216227 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -34,7 +34,7 @@ public class DownloadLogAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.downloadlog_item, null);
+ convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
diff --git a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 0bf770df2..33b11774f 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -54,7 +54,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
diff --git a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index fa2e5a0a7..658af9e4e 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -56,7 +56,7 @@ public class DownloadlistAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.downloadlist_item, null);
+ convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.message = (TextView) convertView
.findViewById(R.id.txtvMessage);
diff --git a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
index 9a7b607aa..5e857c131 100644
--- a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
@@ -72,7 +72,7 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.external_itemlist_item,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.feedTitle = (TextView) convertView
.findViewById(R.id.txtvFeedname);
@@ -225,7 +225,7 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.feeditemlist_header, null);
+ convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
TextView headerTitle = (TextView) convertView
.findViewById(0);
ImageButton actionButton = (ImageButton) convertView
diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index c4a16d4db..357b5f8b4 100644
--- a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -67,7 +67,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.feeditemlist_item, null);
+ convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
holder.title = (TextView) convertView
.findViewById(R.id.txtvItemname);
holder.lenSize = (TextView) convertView
diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 5fb204b26..c2c2285ac 100644
--- a/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -12,7 +12,7 @@ import de.danoeh.antennapod.feed.FeedItem;
import java.util.List;
/**
- * Created by daniel on 24.08.13.
+ * List adapter for showing a list of FeedItems with their title and description.
*/
public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
@@ -31,7 +31,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.itemdescription_listitem, null);
+ convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
index 536bf80e3..9676372fb 100644
--- a/src/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -110,7 +110,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_listitem, null);
+ convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
@@ -154,7 +154,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_section_item, null);
+ convertView = inflater.inflate(R.layout.nav_section_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
convertView.setTag(holder);
@@ -179,7 +179,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_feedlistitem, null);
+ convertView = inflater.inflate(R.layout.nav_feedlistitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
index 555a334f6..07fd3e6b1 100644
--- a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
@@ -62,7 +62,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.new_episodes_listitem,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
index ecce1b473..f671ba5c6 100644
--- a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -57,7 +57,7 @@ public class QueueListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.queue_listitem,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.butSecondary = (ImageButton) convertView
.findViewById(R.id.butSecondaryAction);
diff --git a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index 5c6af3943..ecfbb4660 100644
--- a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -55,7 +55,7 @@ public class SearchlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.searchlist_item, null);
+ convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.cover = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
diff --git a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index 795b17917..f20232a6f 100644
--- a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -38,7 +38,7 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, null);
+ convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
index 1c5003ab3..21ae5291e 100644
--- a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
+++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
@@ -5,6 +5,9 @@ import android.content.*;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.service.download.Downloader;
@@ -45,9 +48,9 @@ public class DownloadObserver {
* @throws java.lang.IllegalArgumentException if one of the arguments is null.
*/
public DownloadObserver(Activity activity, Handler handler, Callback callback) {
- if (activity == null) throw new IllegalArgumentException("activity = null");
- if (handler == null) throw new IllegalArgumentException("handler = null");
- if (callback == null) throw new IllegalArgumentException("callback = null");
+ Validate.notNull(activity);
+ Validate.notNull(handler);
+ Validate.notNull(callback);
this.activity = activity;
this.handler = handler;
@@ -166,7 +169,7 @@ public class DownloadObserver {
}
public void setActivity(Activity activity) {
- if (activity == null) throw new IllegalArgumentException("activity = null");
+ Validate.notNull(activity);
this.activity = activity;
}
diff --git a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
index e9aa79ac1..9210ac1d1 100644
--- a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
+++ b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
@@ -1,300 +1,236 @@
package de.danoeh.antennapod.asynctask;
-import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
+
+import org.apache.commons.lang3.Validate;
+import org.shredzone.flattr4j.exception.FlattrException;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.FlattrAuthActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.NetworkUtils;
import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Performs a click action in a background thread.
+ * <p/>
+ * When started, the flattr click worker will try to flattr every item that is in the flattr queue. If no network
+ * connection is available it will shut down immediately. The FlattrClickWorker can also be given one additional
+ * FlattrThing which will be flattrd immediately.
+ * <p/>
+ * The FlattrClickWorker will display a toast notification for every item that has been flattrd. If the FlattrClickWorker failed
+ * to flattr something, a notification will be displayed.
*/
-
-public class FlattrClickWorker extends AsyncTask<Void, String, Void> {
+public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorker.ExitCode> {
protected static final String TAG = "FlattrClickWorker";
- protected Context context;
-
- private final int NOTIFICATION_ID = 4;
- protected String errorMsg;
- protected int exitCode;
- protected ArrayList<String> flattrd;
- protected ArrayList<String> flattr_failed;
+ private static final int NOTIFICATION_ID = 4;
+ private final Context context;
- protected NotificationCompat.Builder notificationCompatBuilder;
- private Notification.BigTextStyle notificationBuilder;
- protected NotificationManager notificationManager;
+ public static enum ExitCode {EXIT_NORMAL, NO_TOKEN, NO_NETWORK, NO_THINGS}
- protected ProgressDialog progDialog;
+ private volatile int countFailed = 0;
+ private volatile int countSuccess = 0;
- protected final static int EXIT_DEFAULT = 0;
- protected final static int NO_TOKEN = 1;
- protected final static int ENQUEUED = 2;
- protected final static int NO_THINGS = 3;
-
- public final static int ENQUEUE_ONLY = 1;
- public final static int FLATTR_TOAST = 2;
- public static final int FLATTR_NOTIFICATION = 3;
-
- private int run_mode = FLATTR_NOTIFICATION;
-
- private FlattrThing extra_flattr_thing; // additional urls to flattr that do *not* originate from the queue
+ private volatile FlattrThing extraFlattrThing;
/**
- * @param context
- * @param run_mode can be one of ENQUEUE_ONLY, FLATTR_TOAST and FLATTR_NOTIFICATION
+ * Only relevant if just one thing is flattrd
*/
- public FlattrClickWorker(Context context, int run_mode) {
- this(context);
- this.run_mode = run_mode;
- }
+ private volatile FlattrException exception;
+ /**
+ * Creates a new FlattrClickWorker which will only flattr all things in the queue.
+ * <p/>
+ * The FlattrClickWorker has to be started by calling executeAsync().
+ *
+ * @param context A context for accessing the database and posting notifications. Must not be null.
+ */
public FlattrClickWorker(Context context) {
- super();
- this.context = context;
- exitCode = EXIT_DEFAULT;
-
- flattrd = new ArrayList<String>();
- flattr_failed = new ArrayList<String>();
-
- errorMsg = "";
+ Validate.notNull(context);
+ this.context = context.getApplicationContext();
}
- /* only used in PreferencesActivity for flattring antennapod itself,
- * can't really enqueue this thing
- */
- public FlattrClickWorker(Context context, FlattrThing thing) {
+ /**
+ * Creates a new FlattrClickWorker which will flattr all things in the queue and one additional
+ * FlattrThing.
+ * <p/>
+ * The FlattrClickWorker has to be started by calling executeAsync().
+ *
+ * @param context A context for accessing the database and posting notifications. Must not be null.
+ * @param extraFlattrThing The additional thing to flattr
+ */
+ public FlattrClickWorker(Context context, FlattrThing extraFlattrThing) {
this(context);
- extra_flattr_thing = thing;
- run_mode = FLATTR_TOAST;
- Log.d(TAG, "Going to flattr special thing that is not in the queue: " + thing.getTitle());
- }
-
- protected void onNoAccessToken() {
- Log.w(TAG, "No access token was available");
- }
-
- protected void onFlattrError() {
- FlattrUtils.showErrorDialog(context, errorMsg);
+ this.extraFlattrThing = extraFlattrThing;
}
- protected void onFlattred() {
- String notificationTitle = context.getString(R.string.flattrd_label);
- String notificationText = "", notificationSubText = "", notificationBigText = "";
-
- // text for successfully flattred items
- if (flattrd.size() == 1)
- notificationText = String.format(context.getString(R.string.flattr_click_success));
- else if (flattrd.size() > 1) // flattred pending items from queue
- notificationText = String.format(context.getString(R.string.flattr_click_success_count, flattrd.size()));
- if (flattrd.size() > 0) {
- String acc = "";
- for (String s : flattrd)
- acc += s + '\n';
- acc = acc.substring(0, acc.length() - 2);
+ @Override
+ protected ExitCode doInBackground(Void... params) {
- notificationBigText = String.format(context.getString(R.string.flattr_click_success_queue), acc);
+ if (!FlattrUtils.hasToken()) {
+ return ExitCode.NO_TOKEN;
}
- // add text for failures
- if (flattr_failed.size() > 0) {
- notificationTitle = context.getString(R.string.flattrd_failed_label);
- notificationText = String.format(context.getString(R.string.flattr_click_failure_count), flattr_failed.size())
- + " " + notificationText;
-
- notificationSubText = flattr_failed.get(0);
+ if (!NetworkUtils.networkAvailable(context)) {
+ return ExitCode.NO_NETWORK;
+ }
- String acc = "";
- for (String s : flattr_failed)
- acc += s + '\n';
- acc = acc.substring(0, acc.length() - 2);
+ final List<FlattrThing> flattrQueue = DBReader.getFlattrQueue(context);
+ if (extraFlattrThing != null) {
+ flattrQueue.add(extraFlattrThing);
+ } else if (flattrQueue.size() == 1) {
+ // if only one item is flattrd, the report can specifically mentioned that this item has failed
+ extraFlattrThing = flattrQueue.get(0);
+ }
- notificationBigText = String.format(context.getString(R.string.flattr_click_failure), acc)
- + "\n" + notificationBigText;
+ if (flattrQueue.isEmpty()) {
+ return ExitCode.NO_THINGS;
}
- Log.d(TAG, "Going to post notification: " + notificationBigText);
-
- notificationManager.cancel(NOTIFICATION_ID);
-
- if (run_mode == FLATTR_NOTIFICATION || flattr_failed.size() > 0) {
- PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder = new Notification.BigTextStyle(
- new Notification.Builder(context)
- .setOngoing(false)
- .setContentTitle(notificationTitle)
- .setContentText(notificationText)
- .setContentIntent(contentIntent)
- .setSubText(notificationSubText)
- .setSmallIcon(R.drawable.stat_notify_sync))
- .bigText(notificationText + "\n" + notificationBigText);
- notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
- } else {
- notificationCompatBuilder = new NotificationCompat.Builder(context) // need new notificationBuilder and cancel/renotify to get rid of progress bar
- .setContentTitle(notificationTitle)
- .setContentText(notificationText)
- .setContentIntent(contentIntent)
- .setSubText(notificationBigText)
- .setTicker(notificationTitle)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setOngoing(false);
- notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ List<Future> dbFutures = new LinkedList<Future>();
+ for (FlattrThing thing : flattrQueue) {
+ if (BuildConfig.DEBUG) Log.d(TAG, "Processing " + thing.getTitle());
+
+ try {
+ thing.getFlattrStatus().setUnflattred(); // pop from queue to prevent unflattrable things from getting stuck in flattr queue infinitely
+ FlattrUtils.clickUrl(context, thing.getPaymentLink());
+ thing.getFlattrStatus().setFlattred();
+ publishProgress(R.string.flattr_click_success);
+ countSuccess++;
+
+ } catch (FlattrException e) {
+ e.printStackTrace();
+ countFailed++;
+ if (countFailed == 1) {
+ exception = e;
+ }
}
- } else if (run_mode == FLATTR_TOAST) {
- Toast.makeText(context.getApplicationContext(),
- notificationText,
- Toast.LENGTH_LONG)
- .show();
- }
- }
- protected void onEnqueue() {
- Toast.makeText(context.getApplicationContext(),
- R.string.flattr_click_enqueued,
- Toast.LENGTH_LONG)
- .show();
- }
+ Future<?> f = DBWriter.setFlattredStatus(context, thing, false);
+ if (f != null) {
+ dbFutures.add(f);
+ }
+ }
- protected void onSetupNotification() {
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder = new Notification.BigTextStyle(
- new Notification.Builder(context)
- .setContentTitle(context.getString(R.string.flattring_label))
- .setAutoCancel(true)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setProgress(0, 0, true)
- .setOngoing(true));
- } else {
- notificationCompatBuilder = new NotificationCompat.Builder(context)
- .setContentTitle(context.getString(R.string.flattring_label))
- .setAutoCancel(true)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setProgress(0, 0, true)
- .setOngoing(true);
+ for (Future f : dbFutures) {
+ try {
+ f.get();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
}
- notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ return ExitCode.EXIT_NORMAL;
}
@Override
- protected void onPostExecute(Void result) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Exit code was " + exitCode);
-
+ protected void onPostExecute(ExitCode exitCode) {
+ super.onPostExecute(exitCode);
switch (exitCode) {
- case NO_TOKEN:
- notificationManager.cancel(NOTIFICATION_ID);
- onNoAccessToken();
+ case EXIT_NORMAL:
+ if (countFailed > 0) {
+ postFlattrFailedNotification();
+ }
break;
- case ENQUEUED:
- onEnqueue();
+ case NO_NETWORK:
+ postToastNotification(R.string.flattr_click_enqueued);
break;
- case EXIT_DEFAULT:
- onFlattred();
+ case NO_TOKEN:
+ postNoTokenNotification();
break;
- case NO_THINGS: // FlattrClickWorker called automatically somewhere to empty flattr queue
- notificationManager.cancel(NOTIFICATION_ID);
+ case NO_THINGS: // nothing to notify here
break;
}
}
@Override
- protected void onPreExecute() {
- onSetupNotification();
+ protected void onProgressUpdate(Integer... values) {
+ super.onProgressUpdate(values);
+ postToastNotification(values[0]);
}
- private static boolean haveInternetAccess(Context context) {
- ConnectivityManager cm =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-
- NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- return (networkInfo != null && networkInfo.isConnectedOrConnecting());
+ private void postToastNotification(int msg) {
+ Toast.makeText(context, context.getString(msg), Toast.LENGTH_LONG).show();
}
- @Override
- protected Void doInBackground(Void... params) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Starting background work");
-
- exitCode = EXIT_DEFAULT;
-
- if (!FlattrUtils.hasToken()) {
- exitCode = NO_TOKEN;
- } else if (DBReader.getFlattrQueueEmpty(context) && extra_flattr_thing == null) {
- exitCode = NO_THINGS;
- } else if (!haveInternetAccess(context) || run_mode == ENQUEUE_ONLY) {
- exitCode = ENQUEUED;
- } else {
- List<FlattrThing> flattrList = DBReader.getFlattrQueue(context);
- Log.d(TAG, "flattrQueue processing list with " + flattrList.size() + " items.");
-
- if (extra_flattr_thing != null)
- flattrList.add(extra_flattr_thing);
-
- flattrd.ensureCapacity(flattrList.size());
-
- for (FlattrThing thing : flattrList) {
- try {
- Log.d(TAG, "flattrQueue processing " + thing.getTitle() + " " + thing.getPaymentLink());
- publishProgress(String.format(context.getString(R.string.flattring_thing), thing.getTitle()));
-
- thing.getFlattrStatus().setUnflattred(); // pop from queue to prevent unflattrable things from getting stuck in flattr queue infinitely
-
- FlattrUtils.clickUrl(context, thing.getPaymentLink());
- flattrd.add(thing.getTitle());
-
- thing.getFlattrStatus().setFlattred();
- } catch (Exception e) {
- Log.d(TAG, "flattrQueue processing exception at item " + thing.getTitle() + " " + e.getMessage());
- flattr_failed.ensureCapacity(flattrList.size());
- flattr_failed.add(thing.getTitle() + ": " + e.getMessage());
- }
- Log.d(TAG, "flattrQueue processing - going to write thing back to db with flattr_status " + Long.toString(thing.getFlattrStatus().toLong()));
- DBWriter.setFlattredStatus(context, thing, false);
- }
+ private void postNoTokenNotification() {
+ PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, FlattrAuthActivity.class), 0);
+
+ Notification notification = new NotificationCompat.Builder(context)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.no_flattr_token_notification_msg)))
+ .setContentIntent(contentIntent)
+ .setContentTitle(context.getString(R.string.no_flattr_token_title))
+ .setTicker(context.getString(R.string.no_flattr_token_title))
+ .setSmallIcon(R.drawable.stat_notify_sync_error)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notification);
+ }
+ private void postFlattrFailedNotification() {
+ if (countFailed == 0) {
+ return;
}
- return null;
- }
-
- @Override
- protected void onProgressUpdate(String... names) {
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder.setBigContentTitle(names[0]);
- notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ String title;
+ String subtext;
+
+ if (countFailed == 1) {
+ title = context.getString(R.string.flattrd_failed_label);
+ String exceptionMsg = (exception.getMessage() != null) ? exception.getMessage() : "";
+ subtext = context.getString(R.string.flattr_click_failure, extraFlattrThing.getTitle())
+ + "\n" + exceptionMsg;
} else {
- notificationCompatBuilder.setContentText(names[0]);
- notificationCompatBuilder.setContentIntent(contentIntent);
- notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ title = context.getString(R.string.flattrd_label);
+ subtext = context.getString(R.string.flattr_click_success_count, countSuccess) + "\n"
+ + context.getString(R.string.flattr_click_failure_count, countFailed);
}
+
+ Notification notification = new NotificationCompat.Builder(context)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(subtext))
+ .setContentIntent(contentIntent)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setSmallIcon(R.drawable.stat_notify_sync_error)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notification);
}
- @SuppressLint("NewApi")
+
+ /**
+ * Starts the FlattrClickWorker as an AsyncTask.
+ */
+ @TargetApi(11)
public void executeAsync() {
- FlattrUtils.hasToken();
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
+ executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
execute();
}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
index 1d069daa5..77609f28b 100644
--- a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
+++ b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
@@ -11,6 +11,7 @@ import de.danoeh.antennapod.service.download.DownloadRequest;
import de.danoeh.antennapod.service.download.HttpDownloader;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import java.io.*;
import java.util.ArrayList;
@@ -48,9 +49,8 @@ public class ImageDiskCache {
* Return an instance of an ImageDiskCache that stores images in the specified folder.
*/
public static synchronized ImageDiskCache getInstance(String path, long maxCacheSize) {
- if (path == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(path);
+
if (cacheSingletons.containsKey(path)) {
return cacheSingletons.get(path);
}
@@ -358,9 +358,7 @@ public class ImageDiskCache {
private final long size;
public DiskCacheObject(String fileUrl, long size) {
- if (fileUrl == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(fileUrl);
this.fileUrl = fileUrl;
this.timestamp = System.currentTimeMillis();
this.size = size;
diff --git a/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
new file mode 100644
index 000000000..d1ed795dc
--- /dev/null
+++ b/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.apache.commons.lang3.Validate;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.preferences.UserPreferences;
+
+/**
+ * Creates a new AlertDialog that displays preferences for auto-flattring to the user.
+ */
+public class AutoFlattrPreferenceDialog {
+
+ private AutoFlattrPreferenceDialog() {
+ }
+
+ public static void newAutoFlattrPreferenceDialog(final Activity activity, final AutoFlattrPreferenceDialogInterface callback) {
+ Validate.notNull(activity);
+ Validate.notNull(callback);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+
+ @SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null);
+ final CheckBox chkAutoFlattr = (CheckBox) view.findViewById(R.id.chkAutoFlattr);
+ final SeekBar skbPercent = (SeekBar) view.findViewById(R.id.skbPercent);
+ final TextView txtvStatus = (TextView) view.findViewById(R.id.txtvStatus);
+
+ chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr());
+ skbPercent.setEnabled(chkAutoFlattr.isChecked());
+ txtvStatus.setEnabled(chkAutoFlattr.isChecked());
+
+ final int initialValue = (int) (UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100.0f);
+ setStatusMsgText(activity, txtvStatus, initialValue);
+ skbPercent.setProgress(initialValue);
+
+ chkAutoFlattr.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ skbPercent.setEnabled(chkAutoFlattr.isChecked());
+ txtvStatus.setEnabled(chkAutoFlattr.isChecked());
+ }
+ });
+
+ skbPercent.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ setStatusMsgText(activity, txtvStatus, progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ builder.setTitle(R.string.pref_auto_flattr_title)
+ .setView(view)
+ .setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ float progDouble = ((float) skbPercent.getProgress()) / 100.0f;
+ callback.onConfirmed(chkAutoFlattr.isChecked(), progDouble);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.cancel_label, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ callback.onCancelled();
+ dialog.dismiss();
+ }
+ })
+ .setCancelable(false).show();
+ }
+
+ private static void setStatusMsgText(Context context, TextView txtvStatus, int progress) {
+ if (progress == 0) {
+ txtvStatus.setText(R.string.auto_flattr_ater_beginning);
+ } else if (progress == 100) {
+ txtvStatus.setText(R.string.auto_flattr_ater_end);
+ } else {
+ txtvStatus.setText(context.getString(R.string.auto_flattr_after_percent, progress));
+ }
+ }
+
+ public static interface AutoFlattrPreferenceDialogInterface {
+ public void onCancelled();
+
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue);
+ }
+
+
+}
diff --git a/src/de/danoeh/antennapod/dialog/FeedItemDialog.java b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
index 4a130af6e..7384463de 100644
--- a/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
+++ b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.dialog;
+import android.annotation.TargetApi;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -21,6 +22,14 @@ import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
@@ -34,11 +43,6 @@ import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler;
-import org.apache.commons.lang3.StringEscapeUtils;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
/**
* Shows information about a specific FeedItem and provides actions like playing, downloading, etc.
@@ -58,7 +62,7 @@ public class FeedItemDialog extends Dialog {
private PopupMenu popupMenu;
public static FeedItemDialog newInstance(Context context, FeedItemDialogSavedInstance savedInstance) {
- if (savedInstance == null) throw new IllegalArgumentException("savedInstance = null");
+ Validate.notNull(savedInstance);
FeedItemDialog dialog = newInstance(context, savedInstance.item, savedInstance.queueAccess);
if (savedInstance.isShowing) {
dialog.show();
@@ -76,8 +80,8 @@ public class FeedItemDialog extends Dialog {
public FeedItemDialog(Context context, int theme, FeedItem item, QueueAccess queue) {
super(context, theme);
- if (item == null) throw new IllegalArgumentException("item = null");
- if (queue == null) throw new IllegalArgumentException("queue = null");
+ Validate.notNull(item);
+ Validate.notNull(queue);
this.item = item;
this.queue = queue;
}
@@ -95,6 +99,7 @@ public class FeedItemDialog extends Dialog {
&& UserPreferences.getTheme() != R.style.Theme_AntennaPod_Dark;
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -147,12 +152,9 @@ public class FeedItemDialog extends Dialog {
@Override
public void onClick(View v) {
- FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
actionButtonCallback.onActionButtonPressed(item);
- if (media.isDownloaded()) {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
dismiss();
}
@@ -166,16 +168,17 @@ public class FeedItemDialog extends Dialog {
{
@Override
public void onClick(View v) {
- FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
-
- if (!media.isDownloaded()) {
- DBTasks.playMedia(getContext(), media, true, true, true);
- dismiss();
- } else {
- DBWriter.deleteFeedMediaOfItem(getContext(), media.getId());
+ if (item.hasMedia()) {
+ FeedMedia media = item.getMedia();
+ if (!media.isDownloaded()) {
+ DBTasks.playMedia(getContext(), media, true, true, true);
+ dismiss();
+ } else {
+ DBWriter.deleteFeedMediaOfItem(getContext(), media.getId());
+ }
+ } else if (item.getLink() != null) {
+ Uri uri = Uri.parse(item.getLink());
+ getContext().startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
}
}
@@ -186,7 +189,13 @@ public class FeedItemDialog extends Dialog {
public void onClick(View v) {
popupMenu.getMenu().clear();
popupMenu.inflate(R.menu.feeditem_dialog);
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
popupMenu.show();
}
}
@@ -228,9 +237,26 @@ public class FeedItemDialog extends Dialog {
}
FeedMedia media = item.getMedia();
if (media == null) {
- header.setVisibility(View.GONE);
+ TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
+ R.attr.location_web_site});
+
+ if (!item.isRead()) {
+ butAction1.setImageDrawable(drawables.getDrawable(0));
+ butAction1.setContentDescription(getContext().getString(R.string.mark_read_label));
+ butAction1.setVisibility(View.VISIBLE);
+ } else {
+ butAction1.setVisibility(View.INVISIBLE);
+ }
+
+ if (item.getLink() != null) {
+ butAction2.setImageDrawable(drawables.getDrawable(1));
+ butAction2.setContentDescription(getContext().getString(R.string.visit_website_label));
+ } else {
+ butAction2.setEnabled(false);
+ }
+
+ drawables.recycle();
} else {
- header.setVisibility(View.VISIBLE);
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.av_play,
R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
@@ -348,7 +374,7 @@ public class FeedItemDialog extends Dialog {
public void setItem(FeedItem item) {
- if (item == null) throw new IllegalArgumentException("item = null");
+ Validate.notNull(item);
this.item = item;
}
@@ -369,7 +395,7 @@ public class FeedItemDialog extends Dialog {
}
public void setQueue(QueueAccess queue) {
- if (queue == null) throw new IllegalArgumentException("queue = null");
+ Validate.notNull(queue);
this.queue = queue;
}
@@ -387,7 +413,7 @@ public class FeedItemDialog extends Dialog {
/**
* Used to save the FeedItemDialog's state across configuration changes
- * */
+ */
public static class FeedItemDialogSavedInstance {
final FeedItem item;
final QueueAccess queueAccess;
diff --git a/src/de/danoeh/antennapod/dialog/TimeDialog.java b/src/de/danoeh/antennapod/dialog/TimeDialog.java
index cb3ebf0ab..bbd514640 100644
--- a/src/de/danoeh/antennapod/dialog/TimeDialog.java
+++ b/src/de/danoeh/antennapod/dialog/TimeDialog.java
@@ -8,6 +8,7 @@ import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.Window;
+import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
@@ -15,114 +16,123 @@ import de.danoeh.antennapod.R;
import java.util.concurrent.TimeUnit;
public abstract class TimeDialog extends Dialog {
- private static final String TAG = "TimeDialog";
+ private static final String TAG = "TimeDialog";
- private static final int DEFAULT_SPINNER_POSITION = 1;
+ private static final int DEFAULT_SPINNER_POSITION = 1;
- private Context context;
+ private Context context;
- private EditText etxtTime;
- private Spinner spTimeUnit;
- private Button butConfirm;
- private Button butCancel;
+ private EditText etxtTime;
+ private Spinner spTimeUnit;
+ private Button butConfirm;
+ private Button butCancel;
- private TimeUnit[] units = { TimeUnit.SECONDS, TimeUnit.MINUTES,
- TimeUnit.HOURS };
+ private TimeUnit[] units = {TimeUnit.SECONDS, TimeUnit.MINUTES,
+ TimeUnit.HOURS};
- public TimeDialog(Context context, int titleTextId, int leftButtonTextId) {
- super(context);
- this.context = context;
- }
+ public TimeDialog(Context context, int titleTextId, int leftButtonTextId) {
+ super(context);
+ this.context = context;
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds),
- context.getString(R.string.time_unit_minutes),
- context.getString(R.string.time_unit_hours)};
-
- setContentView(R.layout.time_dialog);
- etxtTime = (EditText) findViewById(R.id.etxtTime);
- spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
-
- butConfirm.setText(R.string.set_sleeptimer_label);
- butCancel.setText(R.string.cancel_label);
- setTitle(R.string.set_sleeptimer_label);
- ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
- this.getContext(), android.R.layout.simple_spinner_item,
- spinnerContent);
- spinnerAdapter
- .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spTimeUnit.setAdapter(spinnerAdapter);
- spTimeUnit.setSelection(DEFAULT_SPINNER_POSITION);
- butCancel.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
- butConfirm.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- try {
- long input = readTimeMillis();
- onTimeEntered(input);
- dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context,
- R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- }
- });
- etxtTime.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
-
- }
- });
- checkInputLength(etxtTime.getText().length());
-
- }
-
- private void checkInputLength(int length) {
- if (length > 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is larger than 0, enabling confirm button");
- butConfirm.setEnabled(true);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- butConfirm.setEnabled(false);
- }
- }
-
- public abstract void onTimeEntered(long millis);
-
- private long readTimeMillis() {
- TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
- long value = Long.valueOf(etxtTime.getText().toString());
- return selectedUnit.toMillis(value);
- }
+ context.getString(R.string.time_unit_minutes),
+ context.getString(R.string.time_unit_hours)};
+
+ setContentView(R.layout.time_dialog);
+ etxtTime = (EditText) findViewById(R.id.etxtTime);
+ spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
+ butConfirm = (Button) findViewById(R.id.butConfirm);
+ butCancel = (Button) findViewById(R.id.butCancel);
+
+ butConfirm.setText(R.string.set_sleeptimer_label);
+ butCancel.setText(R.string.cancel_label);
+ setTitle(R.string.set_sleeptimer_label);
+ ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
+ this.getContext(), android.R.layout.simple_spinner_item,
+ spinnerContent);
+ spinnerAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spTimeUnit.setAdapter(spinnerAdapter);
+ spTimeUnit.setSelection(DEFAULT_SPINNER_POSITION);
+ butCancel.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ butConfirm.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ try {
+ long input = readTimeMillis();
+ onTimeEntered(input);
+ dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context,
+ R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ }
+ });
+ etxtTime.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ checkInputLength(s.length());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+
+ }
+ });
+ checkInputLength(etxtTime.getText().length());
+ etxtTime.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }, 100);
+
+
+
+ }
+
+ private void checkInputLength(int length) {
+ if (length > 0) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Length is larger than 0, enabling confirm button");
+ butConfirm.setEnabled(true);
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Length is smaller than 0, disabling confirm button");
+ butConfirm.setEnabled(false);
+ }
+ }
+
+ public abstract void onTimeEntered(long millis);
+
+ private long readTimeMillis() {
+ TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
+ long value = Long.valueOf(etxtTime.getText().toString());
+ return selectedUnit.toMillis(value);
+ }
}
diff --git a/src/de/danoeh/antennapod/feed/EventDistributor.java b/src/de/danoeh/antennapod/feed/EventDistributor.java
index 85a9b5727..5fb72048e 100644
--- a/src/de/danoeh/antennapod/feed/EventDistributor.java
+++ b/src/de/danoeh/antennapod/feed/EventDistributor.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.feed;
import android.os.Handler;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import java.util.AbstractQueue;
@@ -90,10 +93,7 @@ public class EventDistributor extends Observable {
@Override
public void addObserver(Observer observer) {
super.addObserver(observer);
- if (!(observer instanceof EventListener)) {
- throw new IllegalArgumentException(
- "Observer must be instance of EventListener");
- }
+ Validate.isInstanceOf(EventListener.class, observer);
}
public void sendDownloadQueuedBroadcast() {
diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java
index 1f8e7f8f8..dc941cb48 100644
--- a/src/de/danoeh/antennapod/feed/FeedMedia.java
+++ b/src/de/danoeh/antennapod/feed/FeedMedia.java
@@ -401,7 +401,9 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public String getImageLoaderCacheKey() {
String out;
- if (item.hasItemImageDownloaded()) {
+ if (item == null) {
+ return null;
+ } else if (item.hasItemImageDownloaded()) {
out = item.getImageLoaderCacheKey();
} else {
out = new Playable.DefaultPlayableImageLoader(this)
diff --git a/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index ed304ad37..082fe93fc 100644
--- a/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -68,6 +68,7 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = null;
viewCreated = false;
feedItemDialog = null;
+ stopItemLoader();
}
@Override
@@ -103,6 +104,7 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
}
+ setListShown(true);
listAdapter.notifyDataSetChanged();
if (feedItemDialog != null) {
boolean res = feedItemDialog.updateContent(queue, items);
@@ -171,7 +173,6 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
protected void onPostExecute(Object[] results) {
super.onPostExecute(results);
- setListShown(true);
if (results != null) {
items = (List<FeedItem>) results[0];
queue = (QueueAccess) results[1];
diff --git a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index bf6974982..04c7fbf8e 100644
--- a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -2,8 +2,11 @@ package de.danoeh.antennapod.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.*;
-import android.content.res.TypedArray;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -11,26 +14,33 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.*;
+import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.util.ShareUtils;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.playback.Playable;
-import org.apache.commons.lang3.StringEscapeUtils;
-
-import java.util.concurrent.Callable;
+import de.danoeh.antennapod.util.playback.PlaybackController;
+import de.danoeh.antennapod.util.playback.Timeline;
-/** Displays the description of a Playable object in a Webview. */
+/**
+ * Displays the description of a Playable object in a Webview.
+ */
public class ItemDescriptionFragment extends Fragment {
private static final String TAG = "ItemDescriptionFragment";
@@ -43,6 +53,7 @@ public class ItemDescriptionFragment extends Fragment {
private static final String ARG_FEEDITEM_ID = "arg.feeditem";
private static final String ARG_SAVE_STATE = "arg.saveState";
+ private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes";
private WebView webvDescription;
@@ -63,21 +74,29 @@ public class ItemDescriptionFragment extends Fragment {
*/
private boolean saveState;
+ /**
+ * True if Fragment should highlight timecodes (e.g. time codes in the HH:MM:SS format).
+ */
+ private boolean highlightTimecodes;
+
public static ItemDescriptionFragment newInstance(Playable media,
- boolean saveState) {
+ boolean saveState,
+ boolean highlightTimecodes) {
ItemDescriptionFragment f = new ItemDescriptionFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_PLAYABLE, media);
args.putBoolean(ARG_SAVE_STATE, saveState);
+ args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
f.setArguments(args);
return f;
}
- public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState) {
+ public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState, boolean highlightTimecodes) {
ItemDescriptionFragment f = new ItemDescriptionFragment();
Bundle args = new Bundle();
args.putLong(ARG_FEEDITEM_ID, item.getId());
args.putBoolean(ARG_SAVE_STATE, saveState);
+ args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
f.setArguments(args);
return f;
}
@@ -106,12 +125,16 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return false;
+ if (Timeline.isTimecodeLink(url)) {
+ onTimecodeLinkSelected(url);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ e.printStackTrace();
+ return true;
+ }
}
return true;
}
@@ -178,6 +201,7 @@ public class ItemDescriptionFragment extends Fragment {
Log.d(TAG, "Creating fragment");
Bundle args = getArguments();
saveState = args.getBoolean(ARG_SAVE_STATE, false);
+ highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
}
@@ -229,21 +253,6 @@ public class ItemDescriptionFragment extends Fragment {
}
}
- /**
- * Return the CSS style of the Webview.
- *
- * @param textColor the default color to use for the text in the webview. This
- * value is inserted directly into the CSS String.
- */
- private String applyWebviewStyle(String textColor, String data) {
- final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> @font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
- final int pageMargin = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
- .getDisplayMetrics());
- return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
- pageMargin, pageMargin, pageMargin, data);
- }
-
private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
@@ -254,7 +263,8 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG,
"Link of webview was long-pressed. Extra: "
- + r.getExtra());
+ + r.getExtra()
+ );
selectedURL = r.getExtra();
webvDescription.showContextMenu();
return true;
@@ -295,6 +305,13 @@ public class ItemDescriptionFragment extends Fragment {
R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
break;
+ case R.id.go_to_position_item:
+ if (Timeline.isTimecodeLink(selectedURL)) {
+ onTimecodeLinkSelected(selectedURL);
+ } else {
+ Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: " + selectedURL);
+ }
+ break;
default:
handled = false;
break;
@@ -311,13 +328,19 @@ public class ItemDescriptionFragment extends Fragment {
ContextMenuInfo menuInfo) {
if (selectedURL != null) {
super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
- menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
- R.string.copy_url_label);
- menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
- R.string.share_url_label);
- menu.setHeaderTitle(selectedURL);
+ if (Timeline.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)));
+ } else {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
}
}
@@ -364,22 +387,10 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading Webview");
try {
- Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes();
- final String shownotes = shownotesLoadTask.call();
-
- data = StringEscapeUtils.unescapeHtml4(shownotes);
Activity activity = getActivity();
if (activity != null) {
- TypedArray res = activity
- .getTheme()
- .obtainStyledAttributes(
- new int[]{android.R.attr.textColorPrimary});
- int colorResource = res.getColor(0, 0);
- String colorString = String.format("#%06X",
- 0xFFFFFF & colorResource);
- Log.i(TAG, "text color: " + colorString);
- res.recycle();
- data = applyWebviewStyle(colorString, data);
+ Timeline timeline = new Timeline(activity, shownotesProvider);
+ data = timeline.processShownotes(highlightTimecodes);
} else {
cancel(true);
}
@@ -409,7 +420,8 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG,
"Saving scroll position: "
- + webvDescription.getScrollY());
+ + webvDescription.getScrollY()
+ );
editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
.toString());
@@ -447,4 +459,18 @@ public class ItemDescriptionFragment extends Fragment {
}
return false;
}
+
+ private void onTimecodeLinkSelected(String link) {
+ int time = Timeline.getTimecodeLinkTime(link);
+ if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) {
+ PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController();
+ if (pc != null) {
+ pc.seekTo(time);
+ }
+ }
+ }
+
+ public interface ItemDescriptionFragmentCallback {
+ public PlaybackController getPlaybackController();
+ }
}
diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
index 5d0b1bec8..d37f17b6d 100644
--- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -12,11 +12,20 @@ import android.support.v4.app.ListFragment;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
+import org.apache.commons.lang3.Validate;
+
+import java.util.List;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.FeedInfoActivity;
@@ -41,8 +50,7 @@ import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Displays a list of FeedItems.
@@ -97,7 +105,7 @@ public class ItemlistFragment extends ListFragment {
setHasOptionsMenu(true);
Bundle args = getArguments();
- if (args == null) throw new IllegalArgumentException("args invalid");
+ Validate.notNull(args);
feedID = args.getLong(ARGUMENT_FEED_ID);
}
@@ -155,31 +163,36 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- if (itemsLoaded) {
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
+
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ if (itemsLoaded) {
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
+ }
+ return true;
}
- return true;
- }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
+ }
}
@Override
@@ -322,10 +335,11 @@ public class ItemlistFragment extends ListFragment {
Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
return;
}
+ ListView lv = getListView();
LayoutInflater inflater = (LayoutInflater)
getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View header = inflater.inflate(R.layout.feeditemlist_header, null);
- getListView().addHeaderView(header);
+ View header = inflater.inflate(R.layout.feeditemlist_header, lv, false);
+ lv.addHeaderView(header);
TextView txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
diff --git a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index a0861779c..fe995256b 100644
--- a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -34,6 +34,7 @@ import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -138,31 +139,35 @@ public class NewEpisodesFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.new_episodes, menu);
-
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ inflater.inflate(R.menu.new_episodes, menu);
+
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty());
- menu.findItem(R.id.episode_filter_item).setChecked(showOnlyNewEpisodes);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty());
+ menu.findItem(R.id.episode_filter_item).setChecked(showOnlyNewEpisodes);
+ }
}
@Override
diff --git a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index bab5143d5..470186180 100644
--- a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -25,6 +25,8 @@ import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -129,17 +131,21 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[] {R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty());
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty());
+ }
}
@Override
diff --git a/src/de/danoeh/antennapod/fragment/QueueFragment.java b/src/de/danoeh/antennapod/fragment/QueueFragment.java
index e3f0bb19d..2f322f75b 100644
--- a/src/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/src/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -5,15 +5,25 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.SearchView;
import android.util.Log;
-import android.view.*;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.TextView;
+
import com.mobeta.android.dslv.DragSortListView;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
@@ -27,12 +37,8 @@ import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.QueueAccess;
-import de.danoeh.antennapod.util.UndoBarController;
-import de.danoeh.antennapod.util.gui.FeedItemUndoToken;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Shows all items in the queue
@@ -47,7 +53,6 @@ public class QueueFragment extends Fragment {
private QueueListAdapter listAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
- private UndoBarController undoBarController;
private List<FeedItem> queue;
private List<Downloader> downloaderList;
@@ -111,7 +116,6 @@ public class QueueFragment extends Fragment {
private void resetViewState() {
unregisterForContextMenu(listView);
listAdapter = null;
- undoBarController = null;
activity.set(null);
viewsCreated = false;
blockDownloadObserverUpdate = false;
@@ -133,22 +137,24 @@ public class QueueFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -185,6 +191,9 @@ public class QueueFragment extends Fragment {
case R.id.move_to_bottom_item:
DBWriter.moveQueueItemToBottom(getActivity(), selectedItem.getId(), true);
return true;
+ case R.id.remove_from_queue_item:
+ DBWriter.removeQueueItem(getActivity(), selectedItem.getId(), false);
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -212,19 +221,6 @@ public class QueueFragment extends Fragment {
}
});
- undoBarController = new UndoBarController(root.findViewById(R.id.undobar), new UndoBarController.UndoListener() {
- @Override
- public void onUndo(Parcelable token) {
- // Perform the undo
- FeedItemUndoToken undoToken = (FeedItemUndoToken) token;
- if (token != null) {
- long itemId = undoToken.getFeedItemId();
- int position = undoToken.getPosition();
- DBWriter.addQueueItemAt(getActivity(), itemId, position, false);
- }
- }
- });
-
listView.setDragSortListener(new DragSortListView.DragSortListener() {
@Override
public void drag(int from, int to) {
@@ -245,13 +241,6 @@ public class QueueFragment extends Fragment {
@Override
public void remove(int which) {
- stopItemLoader();
- FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
- DBWriter.removeQueueItem(getActivity(), item.getId(), true);
- undoBarController.showUndoBar(false,
- getString(R.string.removed_from_queue), new FeedItemUndoToken(item,
- which)
- );
}
});
diff --git a/src/de/danoeh/antennapod/fragment/SearchFragment.java b/src/de/danoeh/antennapod/fragment/SearchFragment.java
index f89e44717..b3ade4d70 100644
--- a/src/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/src/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -20,6 +20,8 @@ import de.danoeh.antennapod.feed.*;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.FeedSearcher;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
@@ -131,26 +133,28 @@ public class SearchFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
- MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- final SearchView sv = new SearchView(getActivity());
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setQuery(getArguments().getString(ARG_QUERY), false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- getArguments().putString(ARG_QUERY, s);
- itemsLoaded = false;
- startSearchTask();
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
+ MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ final SearchView sv = new SearchView(getActivity());
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQuery(getArguments().getString(ARG_QUERY), false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ getArguments().putString(ARG_QUERY, s);
+ itemsLoaded = false;
+ startSearchTask();
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- MenuItemCompat.setActionView(item, sv);
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setActionView(item, sv);
+ }
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 837df0594..1b4616207 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -21,6 +21,7 @@ import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
@@ -44,22 +45,24 @@ public abstract class PodcastListFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final android.support.v7.widget.SearchView sv = new android.support.v7.widget.SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final android.support.v7.widget.SearchView sv = new android.support.v7.widget.SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -123,7 +126,7 @@ public abstract class PodcastListFragment extends Fragment {
protected void onPostExecute(List<GpodnetPodcast> gpodnetPodcasts) {
super.onPostExecute(gpodnetPodcasts);
final Context context = getActivity();
- if (context != null && gpodnetPodcasts != null) {
+ if (context != null && gpodnetPodcasts != null && gpodnetPodcasts.size() > 0) {
PodcastListAdapter listAdapter = new PodcastListAdapter(context, 0, gpodnetPodcasts);
gridView.setAdapter(listAdapter);
listAdapter.notifyDataSetChanged();
@@ -132,13 +135,18 @@ public abstract class PodcastListFragment extends Fragment {
gridView.setVisibility(View.VISIBLE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
+ } else if (context != null && gpodnetPodcasts != null) {
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(getString(R.string.search_status_no_results));
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setVisibility(View.GONE);
} else if (context != null) {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
txtvError.setText(getString(R.string.error_msg_prefix) + exception.getMessage());
txtvError.setVisibility(View.VISIBLE);
butRetry.setVisibility(View.VISIBLE);
-
}
}
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
index 79d0c5d6f..801024787 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
@@ -4,15 +4,17 @@ import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
+
+import org.apache.commons.lang3.Validate;
+
+import java.util.List;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Performs a search on the gpodder.net directory and displays the results.
@@ -44,22 +46,24 @@ public class SearchListFragment extends PodcastListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setQuery(query, false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- changeQuery(s);
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setQuery(query, false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ changeQuery(s);
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -68,9 +72,8 @@ public class SearchListFragment extends PodcastListFragment {
}
public void changeQuery(String query) {
- if (query == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(query);
+
this.query = query;
loadData();
}
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
index f016290bf..204dda992 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.os.Bundle;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
@@ -21,7 +24,7 @@ public class TagFragment extends PodcastListFragment {
private GpodnetTag tag;
public static TagFragment newInstance(String tagName) {
- if (tagName == null) throw new IllegalArgumentException("tagName = null");
+ Validate.notNull(tagName);
TagFragment fragment = new TagFragment();
Bundle args = new Bundle();
args.putString("tag", tagName);
@@ -34,7 +37,7 @@ public class TagFragment extends PodcastListFragment {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
- if (args == null || args.getString("tag") == null) throw new IllegalArgumentException("args invalid");
+ Validate.isTrue(args != null && args.getString("tag") != null, "args invalid");
tag = new GpodnetTag(args.getString("tag"));
((MainActivity) getActivity()).getMainActivtyActionBar().setTitle(tag.getName());
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index 880726e50..2289862aa 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -11,16 +11,17 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetTag;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.ArrayList;
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
public class TagListFragment extends ListFragment {
private static final String TAG = "TagListFragment";
@@ -35,22 +36,24 @@ public class TagListFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -66,11 +69,26 @@ public class TagListFragment extends ListFragment {
}
});
- loadData();
+ startLoadTask();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ cancelLoadTask();
+ }
+
+ private AsyncTask<Void, Void, List<GpodnetTag>> loadTask;
+
+ private void cancelLoadTask() {
+ if (loadTask != null && !loadTask.isCancelled()) {
+ loadTask.cancel(true);
+ }
}
- private void loadData() {
- AsyncTask<Void, Void, List<GpodnetTag>> task = new AsyncTask<Void, Void, List<GpodnetTag>>() {
+ private void startLoadTask() {
+ cancelLoadTask();
+ loadTask = new AsyncTask<Void, Void, List<GpodnetTag>>() {
private Exception exception;
@Override
@@ -115,9 +133,9 @@ public class TagListFragment extends ListFragment {
}
};
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
- task.execute();
+ loadTask.execute();
}
}
}
diff --git a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
index a0c5b534c..038b2a367 100644
--- a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
+++ b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.gpoddernet;
-import de.danoeh.antennapod.gpoddernet.model.*;
-import de.danoeh.antennapod.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.service.download.AntennapodHttpClient;
+import org.apache.commons.lang3.Validate;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@@ -32,6 +30,14 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetDevice;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetSubscriptionChange;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetTag;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetUploadChangesResponse;
+import de.danoeh.antennapod.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.service.download.AntennapodHttpClient;
+
/**
* Communicates with the gpodder.net service.
*/
@@ -89,10 +95,8 @@ public class GpodnetService {
*/
public List<GpodnetPodcast> getPodcastsForTag(GpodnetTag tag, int count)
throws GpodnetServiceException {
- if (tag == null) {
- throw new IllegalArgumentException(
- "Tag and title of tag must not be null");
- }
+ Validate.notNull(tag);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/tag/%s/%d.json", tag.getName(), count), null);
@@ -120,9 +124,8 @@ public class GpodnetService {
*/
public List<GpodnetPodcast> getPodcastToplist(int count)
throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
+ Validate.isTrue(count >= 1 && count <= 100, "Count must be in range 1..100");
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/toplist/%d.json", count), null);
@@ -155,9 +158,8 @@ public class GpodnetService {
* @throws GpodnetServiceAuthenticationException If there is an authentication error.
*/
public List<GpodnetPodcast> getSuggestions(int count) throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
+ Validate.isTrue(count >= 1 && count <= 100, "Count must be in range 1..100");
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/suggestions/%d.json", count), null);
@@ -220,9 +222,8 @@ public class GpodnetService {
*/
public List<GpodnetDevice> getDevices(String username)
throws GpodnetServiceException {
- if (username == null) {
- throw new IllegalArgumentException("Username must not be null");
- }
+ Validate.notNull(username);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/devices/%s.json", username), null);
@@ -255,10 +256,9 @@ public class GpodnetService {
public void configureDevice(String username, String deviceId,
String caption, GpodnetDevice.DeviceType type)
throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/devices/%s/%s.json", username, deviceId), null);
@@ -303,10 +303,9 @@ public class GpodnetService {
*/
public String getSubscriptionsOfDevice(String username, String deviceId)
throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/subscriptions/%s/%s.opml", username, deviceId), null);
@@ -332,9 +331,8 @@ public class GpodnetService {
*/
public String getSubscriptionsOfUser(String username)
throws GpodnetServiceException {
- if (username == null) {
- throw new IllegalArgumentException("Username must not be null");
- }
+ Validate.notNull(username);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/subscriptions/%s.opml", username), null);
@@ -406,10 +404,11 @@ public class GpodnetService {
*/
public GpodnetUploadChangesResponse uploadChanges(String username, String deviceId, Collection<String> added,
Collection<String> removed) throws GpodnetServiceException {
- if (username == null || deviceId == null || added == null || removed == null) {
- throw new IllegalArgumentException(
- "Username, device ID, added and removed must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+ Validate.notNull(added);
+ Validate.notNull(removed);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/subscriptions/%s/%s.json", username, deviceId), null);
@@ -453,10 +452,9 @@ public class GpodnetService {
*/
public GpodnetSubscriptionChange getSubscriptionChanges(String username,
String deviceId, long timestamp) throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
String params = String.format("since=%d", timestamp);
String path = String.format("/api/2/subscriptions/%s/%s.json",
username, deviceId);
@@ -486,10 +484,9 @@ public class GpodnetService {
*/
public void authenticate(String username, String password)
throws GpodnetServiceException {
- if (username == null || password == null) {
- throw new IllegalArgumentException(
- "Username and password must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(password);
+
URI uri;
try {
uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
@@ -517,9 +514,8 @@ public class GpodnetService {
private String executeRequest(HttpRequestBase request)
throws GpodnetServiceException {
- if (request == null) {
- throw new IllegalArgumentException("request must not be null");
- }
+ Validate.notNull(request);
+
String responseString = null;
HttpResponse response = null;
try {
@@ -586,9 +582,8 @@ public class GpodnetService {
private String getStringFromEntity(HttpEntity entity)
throws GpodnetServiceException {
- if (entity == null) {
- throw new IllegalArgumentException("entity must not be null");
- }
+ Validate.notNull(entity);
+
ByteArrayOutputStream outputStream;
int contentLength = (int) entity.getContentLength();
if (contentLength > 0) {
@@ -613,9 +608,7 @@ public class GpodnetService {
private void checkStatusCode(HttpResponse response)
throws GpodnetServiceException {
- if (response == null) {
- throw new IllegalArgumentException("response must not be null");
- }
+ Validate.notNull(response);
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode != HttpStatus.SC_OK) {
if (responseCode == HttpStatus.SC_UNAUTHORIZED) {
@@ -629,9 +622,8 @@ public class GpodnetService {
private List<GpodnetPodcast> readPodcastListFromJSONArray(JSONArray array)
throws JSONException {
- if (array == null) {
- throw new IllegalArgumentException("array must not be null");
- }
+ Validate.notNull(array);
+
List<GpodnetPodcast> result = new ArrayList<GpodnetPodcast>(
array.length());
for (int i = 0; i < array.length(); i++) {
@@ -685,9 +677,8 @@ public class GpodnetService {
private List<GpodnetDevice> readDeviceListFromJSONArray(JSONArray array)
throws JSONException {
- if (array == null) {
- throw new IllegalArgumentException("array must not be null");
- }
+ Validate.notNull(array);
+
List<GpodnetDevice> result = new ArrayList<GpodnetDevice>(
array.length());
for (int i = 0; i < array.length(); i++) {
@@ -707,9 +698,8 @@ public class GpodnetService {
private GpodnetSubscriptionChange readSubscriptionChangesFromJSONObject(
JSONObject object) throws JSONException {
- if (object == null) {
- throw new IllegalArgumentException("object must not be null");
- }
+ Validate.notNull(object);
+
List<String> added = new LinkedList<String>();
JSONArray jsonAdded = object.getJSONArray("add");
for (int i = 0; i < jsonAdded.length(); i++) {
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
index ae7199fcc..86a2171fa 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
public class GpodnetDevice {
private String id;
@@ -9,9 +11,7 @@ public class GpodnetDevice {
public GpodnetDevice(String id, String caption, String type,
int subscriptions) {
- if (id == null) {
- throw new IllegalArgumentException("ID must not be null");
- }
+ Validate.notNull(id);
this.id = id;
this.caption = caption;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
index aa01b66e2..b002035c9 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
public class GpodnetPodcast {
private String url;
private String title;
@@ -11,10 +13,9 @@ public class GpodnetPodcast {
public GpodnetPodcast(String url, String title, String description,
int subscribers, String logoUrl, String website, String mygpoLink) {
- if (url == null || title == null || description == null) {
- throw new IllegalArgumentException(
- "URL, title and description must not be null");
- }
+ Validate.notNull(url);
+ Validate.notNull(title);
+ Validate.notNull(description);
this.url = url;
this.title = title;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
index dccb53e5d..a4617118d 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
import java.util.List;
public class GpodnetSubscriptionChange {
@@ -9,10 +11,9 @@ public class GpodnetSubscriptionChange {
public GpodnetSubscriptionChange(List<String> added, List<String> removed,
long timestamp) {
- if (added == null || removed == null) {
- throw new IllegalArgumentException(
- "added and remove must not be null");
- }
+ Validate.notNull(added);
+ Validate.notNull(removed);
+
this.added = added;
this.removed = removed;
this.timestamp = timestamp;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
index e8a36a554..80b84095e 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
import java.util.Comparator;
public class GpodnetTag {
@@ -8,9 +10,7 @@ public class GpodnetTag {
private int usage;
public GpodnetTag(String name, int usage) {
- if (name == null) {
- throw new IllegalArgumentException("Name must not be null");
- }
+ Validate.notNull(name);
this.name = name;
this.usage = usage;
diff --git a/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java b/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
index 80e0768dd..1d1ab052f 100644
--- a/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
+++ b/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
@@ -4,6 +4,9 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
/**
@@ -66,8 +69,8 @@ public class PlaybackPreferences implements
public static void createInstance(Context context) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Creating new instance of UserPreferences");
- if (context == null)
- throw new IllegalArgumentException("Context must not be null");
+ Validate.notNull(context);
+
instance = new PlaybackPreferences(context);
PreferenceManager.getDefaultSharedPreferences(context)
diff --git a/src/de/danoeh/antennapod/preferences/UserPreferences.java b/src/de/danoeh/antennapod/preferences/UserPreferences.java
index 31250bcd9..2020ddfae 100644
--- a/src/de/danoeh/antennapod/preferences/UserPreferences.java
+++ b/src/de/danoeh/antennapod/preferences/UserPreferences.java
@@ -7,11 +7,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
-import de.danoeh.antennapod.receiver.FeedUpdateReceiver;
+
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import org.json.JSONArray;
import org.json.JSONException;
@@ -21,6 +19,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.receiver.FeedUpdateReceiver;
+
/**
* Provides access to preferences set by the user in the settings screen. A
* private instance of this class must first be instantiated via
@@ -28,347 +31,372 @@ import java.util.concurrent.TimeUnit;
* when called.
*/
public class UserPreferences implements
- SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "UserPreferences";
-
- public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
- public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
- public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
- public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
- public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
- public static final String PREF_DISPLAY_ONLY_EPISODES = "prefDisplayOnlyEpisodes";
- public static final String PREF_AUTO_DELETE = "prefAutoDelete";
- public static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
- public static final String PREF_THEME = "prefTheme";
- public static final String PREF_DATA_FOLDER = "prefDataFolder";
- public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
- public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
- private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
- public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
- private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
- private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
- public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
+ SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = "UserPreferences";
+
+ public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
+ public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
+ public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
+ public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
+ public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
+ public static final String PREF_DISPLAY_ONLY_EPISODES = "prefDisplayOnlyEpisodes";
+ public static final String PREF_AUTO_DELETE = "prefAutoDelete";
+ public static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
+ public static final String PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD = "prefAutoFlattrPlayedDurationThreshold";
+ public static final String PREF_THEME = "prefTheme";
+ public static final String PREF_DATA_FOLDER = "prefDataFolder";
+ public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
+ public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
+ private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
+ public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
+ private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
+ private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
+ public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
+ private static final String PREF_SEEK_DELTA_SECS = "prefSeekDeltaSecs";
// TODO: Make this value configurable
- private static final double PLAYED_DURATION_AUTOFLATTR_THRESHOLD = 0.8;
-
- private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
-
- private static UserPreferences instance;
- private final Context context;
-
- // Preferences
- private boolean pauseOnHeadsetDisconnect;
- private boolean followQueue;
- private boolean downloadMediaOnWifiOnly;
- private long updateInterval;
- private boolean allowMobileUpdate;
- private boolean displayOnlyEpisodes;
- private boolean autoDelete;
- private boolean autoFlattr;
- private int theme;
- private boolean enableAutodownload;
- private boolean enableAutodownloadWifiFilter;
- private String[] autodownloadSelectedNetworks;
- private int episodeCacheSize;
- private String playbackSpeed;
- private String[] playbackSpeedArray;
- private boolean pauseForFocusLoss;
- private boolean isFreshInstall;
-
- private UserPreferences(Context context) {
- this.context = context;
- loadPreferences();
- }
-
- /**
- * Sets up the UserPreferences class.
- *
- * @throws IllegalArgumentException
- * if context is null
- * */
- public static void createInstance(Context context) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating new instance of UserPreferences");
- if (context == null)
- throw new IllegalArgumentException("Context must not be null");
- instance = new UserPreferences(context);
-
- createImportDirectory();
- createNoMediaFile();
- PreferenceManager.getDefaultSharedPreferences(context)
- .registerOnSharedPreferenceChangeListener(instance);
-
- }
-
- private void loadPreferences() {
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(context);
- EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
- R.integer.episode_cache_size_unlimited);
- pauseOnHeadsetDisconnect = sp.getBoolean(
- PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
- downloadMediaOnWifiOnly = sp.getBoolean(
- PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
- updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL,
- "0"));
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
- displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- theme = readThemeValue(sp.getString(PREF_THEME, "0"));
- enableAutodownloadWifiFilter = sp.getBoolean(
- PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
- PREF_EPISODE_CACHE_SIZE, "20"));
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
- PREF_PLAYBACK_SPEED_ARRAY, null));
- pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
- }
-
- private int readThemeValue(String valueFromPrefs) {
- switch (Integer.parseInt(valueFromPrefs)) {
- case 0:
- return R.style.Theme_AntennaPod_Light;
- case 1:
- return R.style.Theme_AntennaPod_Dark;
- default:
- return R.style.Theme_AntennaPod_Light;
- }
- }
-
- private long readUpdateInterval(String valueFromPrefs) {
- int hours = Integer.parseInt(valueFromPrefs);
- return TimeUnit.HOURS.toMillis(hours);
- }
-
- private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
- if (valueFromPrefs.equals(context
- .getString(R.string.pref_episode_cache_unlimited))) {
- return EPISODE_CACHE_SIZE_UNLIMITED;
- } else {
- return Integer.valueOf(valueFromPrefs);
- }
- }
-
- private String[] readPlaybackSpeedArray(String valueFromPrefs) {
- String[] selectedSpeeds = null;
- // If this preference hasn't been set yet, return the default options
- if (valueFromPrefs == null) {
- String[] allSpeeds = context.getResources().getStringArray(
- R.array.playback_speed_values);
- List<String> speedList = new LinkedList<String>();
- for (String speedStr : allSpeeds) {
- float speed = Float.parseFloat(speedStr);
- if (speed < 2.0001 && speed * 10 % 1 == 0) {
- speedList.add(speedStr);
- }
- }
- selectedSpeeds = speedList.toArray(new String[speedList.size()]);
- } else {
- try {
- JSONArray jsonArray = new JSONArray(valueFromPrefs);
- selectedSpeeds = new String[jsonArray.length()];
- for (int i = 0; i < jsonArray.length(); i++) {
- selectedSpeeds[i] = jsonArray.getString(i);
- }
- } catch (JSONException e) {
- Log.e(TAG,
- "Got JSON error when trying to get speeds from JSONArray");
- e.printStackTrace();
- }
- }
- return selectedSpeeds;
- }
-
- private static void instanceAvailable() {
- if (instance == null) {
- throw new IllegalStateException(
- "UserPreferences was used before being set up");
- }
- }
-
- public static boolean isPauseOnHeadsetDisconnect() {
- instanceAvailable();
- return instance.pauseOnHeadsetDisconnect;
- }
-
- public static boolean isFollowQueue() {
- instanceAvailable();
- return instance.followQueue;
- }
-
- public static boolean isDownloadMediaOnWifiOnly() {
- instanceAvailable();
- return instance.downloadMediaOnWifiOnly;
- }
-
- public static long getUpdateInterval() {
- instanceAvailable();
- return instance.updateInterval;
- }
-
- public static boolean isAllowMobileUpdate() {
- instanceAvailable();
- return instance.allowMobileUpdate;
- }
-
- public static boolean isDisplayOnlyEpisodes() {
- instanceAvailable();
- //return instance.displayOnlyEpisodes;
+ private static final float PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT = 0.8f;
+
+ private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
+
+ private static UserPreferences instance;
+ private final Context context;
+
+ // Preferences
+ private boolean pauseOnHeadsetDisconnect;
+ private boolean followQueue;
+ private boolean downloadMediaOnWifiOnly;
+ private long updateInterval;
+ private boolean allowMobileUpdate;
+ private boolean displayOnlyEpisodes;
+ private boolean autoDelete;
+ private boolean autoFlattr;
+ private float autoFlattrPlayedDurationThreshold;
+ private int theme;
+ private boolean enableAutodownload;
+ private boolean enableAutodownloadWifiFilter;
+ private String[] autodownloadSelectedNetworks;
+ private int episodeCacheSize;
+ private String playbackSpeed;
+ private String[] playbackSpeedArray;
+ private boolean pauseForFocusLoss;
+ private int seekDeltaSecs;
+ private boolean isFreshInstall;
+
+ private UserPreferences(Context context) {
+ this.context = context;
+ loadPreferences();
+ }
+
+ /**
+ * Sets up the UserPreferences class.
+ *
+ * @throws IllegalArgumentException if context is null
+ */
+ public static void createInstance(Context context) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Creating new instance of UserPreferences");
+ Validate.notNull(context);
+
+ instance = new UserPreferences(context);
+
+ createImportDirectory();
+ createNoMediaFile();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .registerOnSharedPreferenceChangeListener(instance);
+
+ }
+
+ private void loadPreferences() {
+ SharedPreferences sp = PreferenceManager
+ .getDefaultSharedPreferences(context);
+ EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
+ R.integer.episode_cache_size_unlimited);
+ pauseOnHeadsetDisconnect = sp.getBoolean(
+ PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
+ followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
+ downloadMediaOnWifiOnly = sp.getBoolean(
+ PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
+ updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL,
+ "0"));
+ allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
+ displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
+ autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
+ autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
+ autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
+ PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
+ theme = readThemeValue(sp.getString(PREF_THEME, "0"));
+ enableAutodownloadWifiFilter = sp.getBoolean(
+ PREF_ENABLE_AUTODL_WIFI_FILTER, false);
+ autodownloadSelectedNetworks = StringUtils.split(
+ sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
+ episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
+ PREF_EPISODE_CACHE_SIZE, "20"));
+ enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
+ playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
+ playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
+ PREF_PLAYBACK_SPEED_ARRAY, null));
+ pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
+ seekDeltaSecs = Integer.valueOf(sp.getString(PREF_SEEK_DELTA_SECS, "30"));
+ }
+
+ private int readThemeValue(String valueFromPrefs) {
+ switch (Integer.parseInt(valueFromPrefs)) {
+ case 0:
+ return R.style.Theme_AntennaPod_Light;
+ case 1:
+ return R.style.Theme_AntennaPod_Dark;
+ default:
+ return R.style.Theme_AntennaPod_Light;
+ }
+ }
+
+ private long readUpdateInterval(String valueFromPrefs) {
+ int hours = Integer.parseInt(valueFromPrefs);
+ return TimeUnit.HOURS.toMillis(hours);
+ }
+
+ private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
+ if (valueFromPrefs.equals(context
+ .getString(R.string.pref_episode_cache_unlimited))) {
+ return EPISODE_CACHE_SIZE_UNLIMITED;
+ } else {
+ return Integer.valueOf(valueFromPrefs);
+ }
+ }
+
+ private String[] readPlaybackSpeedArray(String valueFromPrefs) {
+ String[] selectedSpeeds = null;
+ // If this preference hasn't been set yet, return the default options
+ if (valueFromPrefs == null) {
+ String[] allSpeeds = context.getResources().getStringArray(
+ R.array.playback_speed_values);
+ List<String> speedList = new LinkedList<String>();
+ for (String speedStr : allSpeeds) {
+ float speed = Float.parseFloat(speedStr);
+ if (speed < 2.0001 && speed * 10 % 1 == 0) {
+ speedList.add(speedStr);
+ }
+ }
+ selectedSpeeds = speedList.toArray(new String[speedList.size()]);
+ } else {
+ try {
+ JSONArray jsonArray = new JSONArray(valueFromPrefs);
+ selectedSpeeds = new String[jsonArray.length()];
+ for (int i = 0; i < jsonArray.length(); i++) {
+ selectedSpeeds[i] = jsonArray.getString(i);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG,
+ "Got JSON error when trying to get speeds from JSONArray");
+ e.printStackTrace();
+ }
+ }
+ return selectedSpeeds;
+ }
+
+ private static void instanceAvailable() {
+ if (instance == null) {
+ throw new IllegalStateException(
+ "UserPreferences was used before being set up");
+ }
+ }
+
+ public static boolean isPauseOnHeadsetDisconnect() {
+ instanceAvailable();
+ return instance.pauseOnHeadsetDisconnect;
+ }
+
+ public static boolean isFollowQueue() {
+ instanceAvailable();
+ return instance.followQueue;
+ }
+
+ public static boolean isDownloadMediaOnWifiOnly() {
+ instanceAvailable();
+ return instance.downloadMediaOnWifiOnly;
+ }
+
+ public static long getUpdateInterval() {
+ instanceAvailable();
+ return instance.updateInterval;
+ }
+
+ public static boolean isAllowMobileUpdate() {
+ instanceAvailable();
+ return instance.allowMobileUpdate;
+ }
+
+ public static boolean isDisplayOnlyEpisodes() {
+ instanceAvailable();
+ //return instance.displayOnlyEpisodes;
return false;
- }
-
- public static boolean isAutoDelete() {
- instanceAvailable();
- return instance.autoDelete;
- }
-
- public static boolean isAutoFlattr() {
- instanceAvailable();
- return instance.autoFlattr;
- }
-
- public static int getTheme() {
- instanceAvailable();
- return instance.theme;
- }
-
- public static boolean isEnableAutodownloadWifiFilter() {
- instanceAvailable();
- return instance.enableAutodownloadWifiFilter;
- }
-
- public static String[] getAutodownloadSelectedNetworks() {
- instanceAvailable();
- return instance.autodownloadSelectedNetworks;
- }
-
- public static int getEpisodeCacheSizeUnlimited() {
- return EPISODE_CACHE_SIZE_UNLIMITED;
- }
-
- public static String getPlaybackSpeed() {
- instanceAvailable();
- return instance.playbackSpeed;
- }
-
- public static String[] getPlaybackSpeedArray() {
- instanceAvailable();
- return instance.playbackSpeedArray;
- }
-
- /**
- * Returns the capacity of the episode cache. This method will return the
- * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
- * 'unlimited'.
- */
- public static int getEpisodeCacheSize() {
- instanceAvailable();
- return instance.episodeCacheSize;
- }
-
- public static boolean isEnableAutodownload() {
- instanceAvailable();
- return instance.enableAutodownload;
- }
-
- public static boolean shouldPauseForFocusLoss() {
- instanceAvailable();
- return instance.pauseForFocusLoss;
- }
-
- public static boolean isFreshInstall() {
- instanceAvailable();
- return instance.isFreshInstall;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Registered change of user preferences. Key: " + key);
-
- if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
- downloadMediaOnWifiOnly = sp.getBoolean(
- PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
-
- } else if (key.equals(PREF_MOBILE_UPDATE)) {
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
-
- } else if (key.equals(PREF_FOLLOW_QUEUE)) {
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
-
- } else if (key.equals(PREF_UPDATE_INTERVAL)) {
- updateInterval = readUpdateInterval(sp.getString(
- PREF_UPDATE_INTERVAL, "0"));
- restartUpdateAlarm(updateInterval);
-
- } else if (key.equals(PREF_AUTO_DELETE)) {
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
-
- } else if (key.equals(PREF_AUTO_FLATTR)) {
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- } else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
- displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
- false);
- } else if (key.equals(PREF_THEME)) {
- theme = readThemeValue(sp.getString(PREF_THEME, ""));
- } else if (key.equals(PREF_ENABLE_AUTODL_WIFI_FILTER)) {
- enableAutodownloadWifiFilter = sp.getBoolean(
- PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- } else if (key.equals(PREF_AUTODL_SELECTED_NETWORKS)) {
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
- } else if (key.equals(PREF_EPISODE_CACHE_SIZE)) {
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
- PREF_EPISODE_CACHE_SIZE, "20"));
- } else if (key.equals(PREF_ENABLE_AUTODL)) {
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- } else if (key.equals(PREF_PLAYBACK_SPEED)) {
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- } else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
- PREF_PLAYBACK_SPEED_ARRAY, null));
- } else if (key.equals(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS)) {
- pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
- } else if (key.equals(PREF_PAUSE_ON_HEADSET_DISCONNECT)) {
+ }
+
+ public static boolean isAutoDelete() {
+ instanceAvailable();
+ return instance.autoDelete;
+ }
+
+ public static boolean isAutoFlattr() {
+ instanceAvailable();
+ return instance.autoFlattr;
+ }
+
+ /**
+ * Returns the time after which an episode should be auto-flattr'd in percent of the episode's
+ * duration.
+ */
+ public static float getAutoFlattrPlayedDurationThreshold() {
+ instanceAvailable();
+ return instance.autoFlattrPlayedDurationThreshold;
+ }
+
+ public static int getTheme() {
+ instanceAvailable();
+ return instance.theme;
+ }
+
+ public static boolean isEnableAutodownloadWifiFilter() {
+ instanceAvailable();
+ return instance.enableAutodownloadWifiFilter;
+ }
+
+ public static String[] getAutodownloadSelectedNetworks() {
+ instanceAvailable();
+ return instance.autodownloadSelectedNetworks;
+ }
+
+ public static int getEpisodeCacheSizeUnlimited() {
+ return EPISODE_CACHE_SIZE_UNLIMITED;
+ }
+
+ public static String getPlaybackSpeed() {
+ instanceAvailable();
+ return instance.playbackSpeed;
+ }
+
+ public static String[] getPlaybackSpeedArray() {
+ instanceAvailable();
+ return instance.playbackSpeedArray;
+ }
+
+ public static int getSeekDeltaMs() {
+ instanceAvailable();
+ return 1000 * instance.seekDeltaSecs;
+ }
+
+ /**
+ * Returns the capacity of the episode cache. This method will return the
+ * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
+ * 'unlimited'.
+ */
+ public static int getEpisodeCacheSize() {
+ instanceAvailable();
+ return instance.episodeCacheSize;
+ }
+
+ public static boolean isEnableAutodownload() {
+ instanceAvailable();
+ return instance.enableAutodownload;
+ }
+
+ public static boolean shouldPauseForFocusLoss() {
+ instanceAvailable();
+ return instance.pauseForFocusLoss;
+ }
+
+ public static boolean isFreshInstall() {
+ instanceAvailable();
+ return instance.isFreshInstall;
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Registered change of user preferences. Key: " + key);
+
+ if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
+ downloadMediaOnWifiOnly = sp.getBoolean(
+ PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
+
+ } else if (key.equals(PREF_MOBILE_UPDATE)) {
+ allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
+
+ } else if (key.equals(PREF_FOLLOW_QUEUE)) {
+ followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
+
+ } else if (key.equals(PREF_UPDATE_INTERVAL)) {
+ updateInterval = readUpdateInterval(sp.getString(
+ PREF_UPDATE_INTERVAL, "0"));
+ restartUpdateAlarm(updateInterval);
+
+ } else if (key.equals(PREF_AUTO_DELETE)) {
+ autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
+
+ } else if (key.equals(PREF_AUTO_FLATTR)) {
+ autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
+ } else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
+ displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
+ false);
+ } else if (key.equals(PREF_THEME)) {
+ theme = readThemeValue(sp.getString(PREF_THEME, ""));
+ } else if (key.equals(PREF_ENABLE_AUTODL_WIFI_FILTER)) {
+ enableAutodownloadWifiFilter = sp.getBoolean(
+ PREF_ENABLE_AUTODL_WIFI_FILTER, false);
+ } else if (key.equals(PREF_AUTODL_SELECTED_NETWORKS)) {
+ autodownloadSelectedNetworks = StringUtils.split(
+ sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
+ } else if (key.equals(PREF_EPISODE_CACHE_SIZE)) {
+ episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
+ PREF_EPISODE_CACHE_SIZE, "20"));
+ } else if (key.equals(PREF_ENABLE_AUTODL)) {
+ enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
+ } else if (key.equals(PREF_PLAYBACK_SPEED)) {
+ playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
+ } else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
+ playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
+ PREF_PLAYBACK_SPEED_ARRAY, null));
+ } else if (key.equals(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS)) {
+ pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
+ } else if (key.equals(PREF_SEEK_DELTA_SECS)) {
+ seekDeltaSecs = Integer.valueOf(sp.getString(PREF_SEEK_DELTA_SECS, "30"));
+ } else if (key.equals(PREF_PAUSE_ON_HEADSET_DISCONNECT)) {
pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
+ } else if (key.equals(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD)) {
+ autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
+ PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
}
- }
-
- public static void setPlaybackSpeed(String speed) {
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED, speed).apply();
- }
-
- public static void setPlaybackSpeedArray(String[] speeds) {
- JSONArray jsonArray = new JSONArray();
- for (String speed : speeds) {
- jsonArray.put(speed);
- }
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
- .apply();
- }
-
- public static void setAutodownloadSelectedNetworks(Context context,
- String[] value) {
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext())
- .edit();
- editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
- StringUtils.join(value, ','));
- editor.commit();
- }
+ }
+
+ public static void setPlaybackSpeed(String speed) {
+ PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
+ .putString(PREF_PLAYBACK_SPEED, speed).apply();
+ }
+
+ public static void setPlaybackSpeedArray(String[] speeds) {
+ JSONArray jsonArray = new JSONArray();
+ for (String speed : speeds) {
+ jsonArray.put(speed);
+ }
+ PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
+ .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
+ .apply();
+ }
+
+ public static void setAutodownloadSelectedNetworks(Context context,
+ String[] value) {
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(context.getApplicationContext())
+ .edit();
+ editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
+ StringUtils.join(value, ','));
+ editor.commit();
+ }
/**
- * Sets the update interval value. Should only be used for testing purposes!
- * */
+ * Sets the update interval value. Should only be used for testing purposes!
+ */
public static void setUpdateInterval(Context context, long newValue) {
instanceAvailable();
SharedPreferences.Editor editor = PreferenceManager
@@ -380,154 +408,170 @@ public class UserPreferences implements
instance.updateInterval = newValue;
}
- /**
- * Return the folder where the app stores all of its data. This method will
- * return the standard data folder if none has been set by the user.
- *
- * @param type
- * The name of the folder inside the data folder. May be null
- * when accessing the root of the data folder.
- * @return The data folder that has been requested or null if the folder
- * could not be created.
- */
- public static File getDataFolder(Context context, String type) {
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext());
- String strDir = prefs.getString(PREF_DATA_FOLDER, null);
- if (strDir == null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Using default data folder");
- return context.getExternalFilesDir(type);
- } else {
- File dataDir = new File(strDir);
- if (!dataDir.exists()) {
- if (!dataDir.mkdir()) {
- Log.w(TAG, "Could not create data folder");
- return null;
- }
- }
-
- if (type == null) {
- return dataDir;
- } else {
- // handle path separators
- String[] dirs = type.split("/");
- for (int i = 0; i < dirs.length; i++) {
- if (dirs.length > 0) {
- if (i < dirs.length - 1) {
- dataDir = getDataFolder(context, dirs[i]);
- if (dataDir == null) {
- return null;
- }
- }
- type = dirs[i];
- }
- }
- File typeDir = new File(dataDir, type);
- if (!typeDir.exists()) {
- if (dataDir.canWrite()) {
- if (!typeDir.mkdir()) {
- Log.e(TAG, "Could not create data folder named "
- + type);
- return null;
- }
- }
- }
- return typeDir;
- }
-
- }
- }
-
- public static void setDataFolder(String dir) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Result from DirectoryChooser: " + dir);
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(instance.context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PREF_DATA_FOLDER, dir);
- editor.commit();
- createImportDirectory();
- }
-
- /** Create a .nomedia file to prevent scanning by the media scanner. */
- private static void createNoMediaFile() {
- File f = new File(instance.context.getExternalFilesDir(null),
- ".nomedia");
- if (!f.exists()) {
- try {
- f.createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Could not create .nomedia file");
- e.printStackTrace();
- }
- if (BuildConfig.DEBUG)
- Log.d(TAG, ".nomedia file created");
- }
- }
-
- /**
- * Creates the import directory if it doesn't exist and if storage is
- * available
- */
- private static void createImportDirectory() {
- File importDir = getDataFolder(instance.context,
- OpmlImportFromPathActivity.IMPORT_DIR);
- if (importDir != null) {
- if (importDir.exists()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Import directory already exists");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating import directory");
- importDir.mkdir();
- }
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not access external storage.");
- }
- }
-
- /**
- * Updates alarm registered with the AlarmManager service or deactivates it.
- *
- * @param millis
- * new value to register with AlarmManager. If millis is 0, the
- * alarm is deactivated.
- * */
- public static void restartUpdateAlarm(long millis) {
- instanceAvailable();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restarting update alarm. New value: " + millis);
- AlarmManager alarmManager = (AlarmManager) instance.context
- .getSystemService(Context.ALARM_SERVICE);
- PendingIntent updateIntent = PendingIntent.getBroadcast(
- instance.context, 0, new Intent(
- FeedUpdateReceiver.ACTION_REFRESH_FEEDS), 0);
- alarmManager.cancel(updateIntent);
- if (millis != 0) {
- alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, millis, millis,
- updateIntent);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Changed alarm to new interval");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Automatic update was deactivated");
- }
- }
-
/**
- * Reads episode cache size as it is saved in the episode_cache_size_values array.
+ * Change the auto-flattr settings
+ *
+ * @param context For accessing the shared preferences
+ * @param enabled Whether automatic flattring should be enabled at all
+ * @param autoFlattrThreshold The percentage of playback time after which an episode should be
+ * flattrd. Must be a value between 0 and 1 (inclusive)
* */
- public static int readEpisodeCacheSize(String valueFromPrefs) {
+ public static void setAutoFlattrSettings(Context context, boolean enabled, float autoFlattrThreshold) {
instanceAvailable();
- return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
+ Validate.inclusiveBetween(0.0, 1.0, autoFlattrThreshold);
+ PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
+ .edit()
+ .putBoolean(PREF_AUTO_FLATTR, enabled)
+ .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold)
+ .commit();
+ instance.autoFlattr = enabled;
+ instance.autoFlattrPlayedDurationThreshold = autoFlattrThreshold;
+ }
+
+ /**
+ * Return the folder where the app stores all of its data. This method will
+ * return the standard data folder if none has been set by the user.
+ *
+ * @param type The name of the folder inside the data folder. May be null
+ * when accessing the root of the data folder.
+ * @return The data folder that has been requested or null if the folder
+ * could not be created.
+ */
+ public static File getDataFolder(Context context, String type) {
+ instanceAvailable();
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(context.getApplicationContext());
+ String strDir = prefs.getString(PREF_DATA_FOLDER, null);
+ if (strDir == null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Using default data folder");
+ return context.getExternalFilesDir(type);
+ } else {
+ File dataDir = new File(strDir);
+ if (!dataDir.exists()) {
+ if (!dataDir.mkdir()) {
+ Log.w(TAG, "Could not create data folder");
+ return null;
+ }
+ }
+
+ if (type == null) {
+ return dataDir;
+ } else {
+ // handle path separators
+ String[] dirs = type.split("/");
+ for (int i = 0; i < dirs.length; i++) {
+ if (dirs.length > 0) {
+ if (i < dirs.length - 1) {
+ dataDir = getDataFolder(context, dirs[i]);
+ if (dataDir == null) {
+ return null;
+ }
+ }
+ type = dirs[i];
+ }
+ }
+ File typeDir = new File(dataDir, type);
+ if (!typeDir.exists()) {
+ if (dataDir.canWrite()) {
+ if (!typeDir.mkdir()) {
+ Log.e(TAG, "Could not create data folder named "
+ + type);
+ return null;
+ }
+ }
+ }
+ return typeDir;
+ }
+
+ }
+ }
+
+ public static void setDataFolder(String dir) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Result from DirectoryChooser: " + dir);
+ instanceAvailable();
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(instance.context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(PREF_DATA_FOLDER, dir);
+ editor.commit();
+ createImportDirectory();
}
- public static double getPlayedDurationAutoflattrThreshold() {
+ /**
+ * Create a .nomedia file to prevent scanning by the media scanner.
+ */
+ private static void createNoMediaFile() {
+ File f = new File(instance.context.getExternalFilesDir(null),
+ ".nomedia");
+ if (!f.exists()) {
+ try {
+ f.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not create .nomedia file");
+ e.printStackTrace();
+ }
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, ".nomedia file created");
+ }
+ }
+
+ /**
+ * Creates the import directory if it doesn't exist and if storage is
+ * available
+ */
+ private static void createImportDirectory() {
+ File importDir = getDataFolder(instance.context,
+ OpmlImportFromPathActivity.IMPORT_DIR);
+ if (importDir != null) {
+ if (importDir.exists()) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Import directory already exists");
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Creating import directory");
+ importDir.mkdir();
+ }
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Could not access external storage.");
+ }
+ }
+
+ /**
+ * Updates alarm registered with the AlarmManager service or deactivates it.
+ *
+ * @param millis new value to register with AlarmManager. If millis is 0, the
+ * alarm is deactivated.
+ */
+ public static void restartUpdateAlarm(long millis) {
instanceAvailable();
- return PLAYED_DURATION_AUTOFLATTR_THRESHOLD;
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Restarting update alarm. New value: " + millis);
+ AlarmManager alarmManager = (AlarmManager) instance.context
+ .getSystemService(Context.ALARM_SERVICE);
+ PendingIntent updateIntent = PendingIntent.getBroadcast(
+ instance.context, 0, new Intent(
+ FeedUpdateReceiver.ACTION_REFRESH_FEEDS), 0
+ );
+ alarmManager.cancel(updateIntent);
+ if (millis != 0) {
+ alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, millis, millis,
+ updateIntent);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Changed alarm to new interval");
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Automatic update was deactivated");
+ }
+ }
+
+ /**
+ * Reads episode cache size as it is saved in the episode_cache_size_values array.
+ */
+ public static int readEpisodeCacheSize(String valueFromPrefs) {
+ instanceAvailable();
+ return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
}
}
diff --git a/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java b/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
index 7b8879f21..a0539e276 100644
--- a/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
@@ -4,6 +4,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -15,10 +18,10 @@ public class AlarmUpdateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
- if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Resetting update alarm after reboot");
- } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
+ } else if (StringUtils.equals(intent.getAction(), Intent.ACTION_PACKAGE_REPLACED)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Resetting update alarm after app upgrade");
}
diff --git a/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index 31c053386..4dcf0b6aa 100644
--- a/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -6,6 +6,9 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -16,7 +19,7 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
- if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
diff --git a/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java b/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
index a7482fbf4..3c283a30b 100644
--- a/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
@@ -6,6 +6,9 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBTasks;
@@ -17,7 +20,7 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_REFRESH_FEEDS)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_REFRESH_FEEDS)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
boolean mobileUpdate = UserPreferences.isAllowMobileUpdate();
diff --git a/src/de/danoeh/antennapod/receiver/PlayerWidget.java b/src/de/danoeh/antennapod/receiver/PlayerWidget.java
index 5748ced3b..9f8892181 100644
--- a/src/de/danoeh/antennapod/receiver/PlayerWidget.java
+++ b/src/de/danoeh/antennapod/receiver/PlayerWidget.java
@@ -5,6 +5,9 @@ import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.service.playback.PlayerWidgetService;
@@ -15,9 +18,9 @@ public class PlayerWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(FORCE_WIDGET_UPDATE)) {
+ if (StringUtils.equals(intent.getAction(), FORCE_WIDGET_UPDATE)) {
startUpdate(context);
- } else if (intent.getAction().equals(STOP_WIDGET_UPDATE)) {
+ } else if (StringUtils.equals(intent.getAction(), STOP_WIDGET_UPDATE)) {
stopUpdate(context);
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadRequest.java b/src/de/danoeh/antennapod/service/download/DownloadRequest.java
index 7ff9bc01c..e803d30d4 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadRequest.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadRequest.java
@@ -3,6 +3,8 @@ package de.danoeh.antennapod.service.download;
import android.os.Parcel;
import android.os.Parcelable;
+import org.apache.commons.lang3.Validate;
+
public class DownloadRequest implements Parcelable {
private final String destination;
@@ -21,15 +23,9 @@ public class DownloadRequest implements Parcelable {
public DownloadRequest(String destination, String source, String title,
long feedfileId, int feedfileType, String username, String password, boolean deleteOnFailure) {
- if (destination == null) {
- throw new IllegalArgumentException("Destination must not be null");
- }
- if (source == null) {
- throw new IllegalArgumentException("Source must not be null");
- }
- if (title == null) {
- throw new IllegalArgumentException("Title must not be null");
- }
+ Validate.notNull(destination);
+ Validate.notNull(source);
+ Validate.notNull(title);
this.destination = destination;
this.source = source;
@@ -110,11 +106,14 @@ public class DownloadRequest implements Parcelable {
if (size != that.size) return false;
if (soFar != that.soFar) return false;
if (statusMsg != that.statusMsg) return false;
- if (destination != null ? !destination.equals(that.destination) : that.destination != null) return false;
- if (password != null ? !password.equals(that.password) : that.password != null) return false;
+ if (destination != null ? !destination.equals(that.destination) : that.destination != null)
+ return false;
+ if (password != null ? !password.equals(that.password) : that.password != null)
+ return false;
if (source != null ? !source.equals(that.source) : that.source != null) return false;
if (title != null ? !title.equals(that.title) : that.title != null) return false;
- if (username != null ? !username.equals(that.username) : that.username != null) return false;
+ if (username != null ? !username.equals(that.username) : that.username != null)
+ return false;
return true;
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index 4f60ef8d6..63be91b57 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -19,30 +19,63 @@ import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.webkit.URLUtil;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.http.HttpStatus;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.parsers.ParserConfigurationException;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DownloadAuthenticationActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.NavListAdapter;
-import de.danoeh.antennapod.feed.*;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedImage;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.feed.FeedPreferences;
import de.danoeh.antennapod.fragment.DownloadsFragment;
-import de.danoeh.antennapod.storage.*;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBTasks;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.storage.DownloadRequestException;
+import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.syndication.handler.FeedHandler;
import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.InvalidFeedException;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.http.HttpStatus;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages the download of feedfiles in the app. Downloads can be enqueued viathe startService intent.
@@ -82,9 +115,14 @@ public class DownloadService extends Service {
public static final String EXTRA_REQUEST = "request";
/**
- * Stores DownloadStatus objects of completed downloads for creating a report at the end of the lifecylce.
+ * Stores new media files that will be queued for auto-download if possible.
+ */
+ private List<Long> newMediaFiles;
+
+ /**
+ * Contains all completed downloads that have not been included in the report yet.
*/
- private List<DownloadStatus> completedDownloads;
+ private List<DownloadStatus> reportQueue;
private ExecutorService syncExecutor;
private CompletionService<Downloader> downloadExecutor;
@@ -206,7 +244,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Service started");
isRunning = true;
handler = new Handler();
- completedDownloads = Collections.synchronizedList(new ArrayList<DownloadStatus>());
+ newMediaFiles = Collections.synchronizedList(new ArrayList<Long>());
+ reportQueue = Collections.synchronizedList(new ArrayList<DownloadStatus>());
downloads = new ArrayList<Downloader>();
numberOfDownloads = new AtomicInteger(0);
@@ -285,7 +324,10 @@ public class DownloadService extends Service {
cancelNotificationUpdater();
unregisterReceiver(cancelDownloadReceiver);
- DBTasks.autodownloadUndownloadedItems(getApplicationContext());
+ if (!newMediaFiles.isEmpty()) {
+ DBTasks.autodownloadUndownloadedItems(getApplicationContext(),
+ ArrayUtils.toPrimitive(newMediaFiles.toArray(new Long[newMediaFiles.size()])));
+ }
}
@SuppressLint("NewApi")
@@ -394,12 +436,10 @@ public class DownloadService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_CANCEL_DOWNLOAD)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_DOWNLOAD)) {
String url = intent.getStringExtra(EXTRA_DOWNLOAD_URL);
- if (url == null) {
- throw new IllegalArgumentException(
- "ACTION_CANCEL_DOWNLOAD intent needs download url extra");
- }
+ Validate.notNull(url, "ACTION_CANCEL_DOWNLOAD intent needs download url extra");
+
if (BuildConfig.DEBUG)
Log.d(TAG, "Cancelling download with url " + url);
Downloader d = getDownloader(url);
@@ -409,7 +449,7 @@ public class DownloadService extends Service {
Log.e(TAG, "Could not cancel download with url " + url);
}
- } else if (intent.getAction().equals(ACTION_CANCEL_ALL_DOWNLOADS)) {
+ } else if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
for (Downloader d : downloads) {
d.cancel();
if (BuildConfig.DEBUG)
@@ -482,7 +522,7 @@ public class DownloadService extends Service {
* @param status the download that is going to be saved
*/
private void saveDownloadStatus(DownloadStatus status) {
- completedDownloads.add(status);
+ reportQueue.add(status);
DBWriter.addDownloadStatus(this, status);
}
@@ -505,7 +545,7 @@ public class DownloadService extends Service {
// a download report is created if at least one download has failed
// (excluding failed image downloads)
- for (DownloadStatus status : completedDownloads) {
+ for (DownloadStatus status : reportQueue) {
if (status.isSuccessful()) {
successfulDownloads++;
} else if (!status.isCancelled()) {
@@ -552,7 +592,7 @@ public class DownloadService extends Service {
if (BuildConfig.DEBUG)
Log.d(TAG, "No report is created");
}
- completedDownloads.clear();
+ reportQueue.clear();
}
/**
@@ -698,7 +738,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Waiting for " + (startTime + WAIT_TIMEOUT - currentTime) + " ms");
sleep(startTime + WAIT_TIMEOUT - currentTime);
} catch (InterruptedException e) {
- if (BuildConfig.DEBUG) Log.d(TAG, "interrupted while waiting for more downloads");
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "interrupted while waiting for more downloads");
tasks += pollCompletedDownloads();
} finally {
currentTime = System.currentTimeMillis();
@@ -794,6 +835,14 @@ public class DownloadService extends Service {
);
}
}
+
+ // queue new media files for automatic download
+ for (FeedItem item : savedFeed.getItems()) {
+ if (!item.isRead() && item.hasMedia() && !item.getMedia().isDownloaded()) {
+ newMediaFiles.add(item.getMedia().getId());
+ }
+ }
+
numberOfDownloads.decrementAndGet();
}
@@ -1033,12 +1082,9 @@ public class DownloadService extends Service {
private DownloadStatus status;
public ImageHandlerThread(DownloadStatus status, DownloadRequest request) {
- if (status == null) {
- throw new IllegalArgumentException("Status must not be null");
- }
- if (request == null) {
- throw new IllegalArgumentException("Request must not be null");
- }
+ Validate.notNull(status);
+ Validate.notNull(request);
+
this.status = status;
this.request = request;
}
@@ -1070,12 +1116,8 @@ public class DownloadService extends Service {
private DownloadStatus status;
public MediaHandlerThread(DownloadStatus status, DownloadRequest request) {
- if (status == null) {
- throw new IllegalArgumentException("Status must not be null");
- }
- if (request == null) {
- throw new IllegalArgumentException("Request must not be null");
- }
+ Validate.notNull(status);
+ Validate.notNull(request);
this.status = status;
this.request = request;
diff --git a/src/de/danoeh/antennapod/service/download/DownloadStatus.java b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
index b240cddbc..1d76770bb 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadStatus.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.service.download;
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.util.DownloadError;
@@ -59,9 +61,8 @@ public class DownloadStatus {
public DownloadStatus(DownloadRequest request, DownloadError reason,
boolean successful, boolean cancelled, String reasonDetailed) {
- if (request == null) {
- throw new IllegalArgumentException("request must not be null");
- }
+ Validate.notNull(request);
+
this.title = request.getTitle();
this.feedfileId = request.getFeedfileId();
this.feedfileType = request.getFeedfileType();
@@ -75,9 +76,7 @@ public class DownloadStatus {
/** Constructor for creating new completed downloads. */
public DownloadStatus(FeedFile feedfile, String title, DownloadError reason,
boolean successful, String reasonDetailed) {
- if (feedfile == null) {
- throw new IllegalArgumentException("feedfile must not be null");
- }
+ Validate.notNull(feedfile);
this.title = title;
this.done = true;
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
index b7ff62129..163a57ed2 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
@@ -22,6 +22,10 @@ import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
+import android.widget.Toast;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
@@ -39,9 +43,9 @@ import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.Playable;
-import de.danoeh.antennapod.util.playback.PlaybackController;
import java.util.List;
@@ -282,7 +286,8 @@ public class PlaybackService extends Service {
if (BuildConfig.DEBUG)
Log.d(TAG, "Handling keycode: " + keycode);
- final PlayerStatus status = mediaPlayer.getPSMPInfo().playerStatus;
+ final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
+ final PlayerStatus status = info.playerStatus;
switch (keycode) {
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -310,14 +315,20 @@ public class PlaybackService extends Service {
mediaPlayer.pause(true, true);
}
break;
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
- mediaPlayer.seekDelta(PlaybackController.DEFAULT_SEEK_DELTA);
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ mediaPlayer.seekDelta(UserPreferences.getSeekDeltaMs());
break;
- }
- case KeyEvent.KEYCODE_MEDIA_REWIND: {
- mediaPlayer.seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ mediaPlayer.seekDelta(-UserPreferences.getSeekDeltaMs());
+ break;
+ default:
+ if (info.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
+ String message = String.format(getResources().getString(R.string.unknown_media_key), keycode);
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
break;
- }
}
}
@@ -502,6 +513,11 @@ public class PlaybackService extends Service {
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
}
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
+
+ // auto-flattr if enabled
+ if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) {
+ DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item);
+ }
}
// Load next episode if previous episode was in the queue and if there
@@ -752,14 +768,13 @@ public class PlaybackService extends Service {
FeedItem item = m.getItem();
m.setPlayedDuration(m.getPlayedDuration() + ((int)(deltaPlayedDuration * playbackSpeed)));
// Auto flattr
- if (FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred() &&
- (m.getPlayedDuration() > UserPreferences.getPlayedDurationAutoflattrThreshold() * duration)) {
+ if (isAutoFlattrable(m) &&
+ (m.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "saveCurrentPosition: performing auto flattr since played duration " + Integer.toString(m.getPlayedDuration())
- + " is " + UserPreferences.getPlayedDurationAutoflattrThreshold() * 100 + "% of file duration " + Integer.toString(duration));
- item.getFlattrStatus().setFlattrQueue();
- DBWriter.setFeedItemFlattrStatus(PodcastApp.getInstance(), item, false);
+ + " is " + UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100 + "% of file duration " + Integer.toString(duration));
+ DBTasks.flattrItemIfLoggedIn(this, item);
}
}
playable.saveCurrentPosition(PreferenceManager
@@ -889,8 +904,7 @@ public class PlaybackService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
if (state != -1) {
if (BuildConfig.DEBUG)
@@ -932,8 +946,7 @@ public class PlaybackService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
stopSelf();
}
}
@@ -943,8 +956,7 @@ public class PlaybackService extends Service {
private BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_SKIP_CURRENT_EPISODE)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
mediaPlayer.endPlayback();
@@ -1045,4 +1057,13 @@ public class PlaybackService extends Service {
return mediaPlayer.getVideoSize();
}
+ private boolean isAutoFlattrable(Playable p) {
+ if (p != null && p instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) p;
+ FeedItem item = ((FeedMedia) p).getItem();
+ return item != null && FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred();
+ } else {
+ return false;
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
index 2915da5a1..477eea9a6 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
@@ -7,6 +7,9 @@ import android.media.RemoteControlClient;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
@@ -61,10 +64,8 @@ public class PlaybackServiceMediaPlayer {
private final ThreadPoolExecutor executor;
public PlaybackServiceMediaPlayer(Context context, PSMPCallback callback) {
- if (context == null)
- throw new IllegalArgumentException("context = null");
- if (callback == null)
- throw new IllegalArgumentException("callback = null");
+ Validate.notNull(context);
+ Validate.notNull(callback);
this.context = context;
this.callback = callback;
@@ -115,8 +116,8 @@ public class PlaybackServiceMediaPlayer {
* @param prepareImmediately Set to true if the method should also prepare the episode for playback.
*/
public void playMediaObject(final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
- if (playable == null)
- throw new IllegalArgumentException("playable = null");
+ Validate.notNull(playable);
+
if (BuildConfig.DEBUG) Log.d(TAG, "Play media object.");
executor.submit(new Runnable() {
@Override
@@ -125,6 +126,7 @@ public class PlaybackServiceMediaPlayer {
try {
playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
} catch (RuntimeException e) {
+ e.printStackTrace();
throw e;
} finally {
playerLock.unlock();
@@ -142,8 +144,7 @@ public class PlaybackServiceMediaPlayer {
* @see #playMediaObject(de.danoeh.antennapod.util.playback.Playable, boolean, boolean, boolean)
*/
private void playMediaObject(final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
- if (playable == null)
- throw new IllegalArgumentException("playable = null");
+ Validate.notNull(playable);
if (!playerLock.isHeldByCurrentThread())
throw new IllegalStateException("method requires playerLock");
@@ -458,8 +459,8 @@ public class PlaybackServiceMediaPlayer {
* Seek to the start of the specified chapter.
*/
public void seekToChapter(Chapter c) {
- if (c == null)
- throw new IllegalArgumentException("c = null");
+ Validate.notNull(c);
+
seekTo((int) c.getStart());
}
@@ -662,8 +663,8 @@ public class PlaybackServiceMediaPlayer {
* @param newMedia The new playable object of the PSMP object. This can be null.
*/
private synchronized void setPlayerStatus(PlayerStatus newStatus, Playable newMedia) {
- if (newStatus == null)
- throw new IllegalArgumentException("newStatus = null");
+ Validate.notNull(newStatus);
+
if (BuildConfig.DEBUG) Log.d(TAG, "Setting player status to " + newStatus);
this.playerStatus = newStatus;
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
index 3ab06910a..680ec2e2f 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.service.playback;
import android.content.Context;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.FeedItem;
@@ -52,10 +55,8 @@ public class PlaybackServiceTaskManager {
* @param callback A PSTMCallback object for notifying the user about updates. Must not be null.
*/
public PlaybackServiceTaskManager(Context context, PSTMCallback callback) {
- if (context == null)
- throw new IllegalArgumentException("context must not be null");
- if (callback == null)
- throw new IllegalArgumentException("callback must not be null");
+ Validate.notNull(context);
+ Validate.notNull(callback);
this.context = context;
this.callback = callback;
@@ -195,8 +196,7 @@ public class PlaybackServiceTaskManager {
* @throws java.lang.IllegalArgumentException if waitingTime <= 0
*/
public synchronized void setSleepTimer(long waitingTime) {
- if (waitingTime <= 0)
- throw new IllegalArgumentException("waitingTime <= 0");
+ Validate.isTrue(waitingTime > 0, "Waiting time <= 0");
if (BuildConfig.DEBUG)
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
@@ -271,8 +271,7 @@ public class PlaybackServiceTaskManager {
* On completion, the callback's onChapterLoaded method will be called.
*/
public synchronized void startChapterLoader(final Playable media) {
- if (media == null)
- throw new IllegalArgumentException("media = null");
+ Validate.notNull(media);
if (isChapterLoaderActive()) {
cancelChapterLoader();
diff --git a/src/de/danoeh/antennapod/spa/SPAUtil.java b/src/de/danoeh/antennapod/spa/SPAUtil.java
index 0d83741ed..75cbd8b5a 100644
--- a/src/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/src/de/danoeh/antennapod/spa/SPAUtil.java
@@ -5,6 +5,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.receiver.SPAReceiver;
@@ -56,7 +59,7 @@ public class SPAUtil {
*/
public static void resetSPAPreferences(Context c) {
if (BuildConfig.DEBUG) {
- if (c == null) throw new IllegalArgumentException("c = null");
+ Validate.notNull(c);
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
diff --git a/src/de/danoeh/antennapod/storage/DBTasks.java b/src/de/danoeh/antennapod/storage/DBTasks.java
index c6a34aeea..8d0ffd9c1 100644
--- a/src/de/danoeh/antennapod/storage/DBTasks.java
+++ b/src/de/danoeh/antennapod/storage/DBTasks.java
@@ -156,7 +156,7 @@ public final class DBTasks {
if (FlattrUtils.hasToken()) {
if (BuildConfig.DEBUG) Log.d(TAG, "Flattring all pending things.");
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_NOTIFICATION).executeAsync(); // flattr pending things
+ new FlattrClickWorker(context).executeAsync(); // flattr pending things
if (BuildConfig.DEBUG) Log.d(TAG, "Fetching flattr status.");
new FlattrStatusFetcher(context).start();
@@ -386,9 +386,11 @@ public final class DBTasks {
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
+ * @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
+ * its media ID is in the mediaIds list.
* @return A Future that can be used for waiting for the methods completion.
*/
- public static Future<?> autodownloadUndownloadedItems(final Context context) {
+ public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
return autodownloadExec.submit(new Runnable() {
@Override
public void run() {
@@ -417,11 +419,17 @@ public final class DBTasks {
- (downloadedEpisodes - deletedEpisodes);
}
+ Arrays.sort(mediaIds); // sort for binary search
+ final boolean ignoreMediaIds = mediaIds.length == 0;
List<FeedItem> itemsToDownload = new ArrayList<FeedItem>();
+
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (int i = 0; i < queue.size(); i++) { // ignore playing item
FeedItem item = queue.get(i);
- if (item.hasMedia() && !item.getMedia().isDownloaded()
+ long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
+ if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
+ && item.hasMedia()
+ && !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
@@ -433,9 +441,13 @@ public final class DBTasks {
}
}
}
+
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
- if (item.hasMedia() && !item.getMedia().isDownloaded()
+ long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
+ if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
+ && item.hasMedia()
+ && !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
diff --git a/src/de/danoeh/antennapod/storage/DBWriter.java b/src/de/danoeh/antennapod/storage/DBWriter.java
index ffdfc65fd..9916ac97f 100644
--- a/src/de/danoeh/antennapod/storage/DBWriter.java
+++ b/src/de/danoeh/antennapod/storage/DBWriter.java
@@ -868,7 +868,7 @@ public class DBWriter {
adapter.setFeedItemFlattrStatus(item);
adapter.close();
if (startFlattrClickWorker) {
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_TOAST).executeAsync();
+ new FlattrClickWorker(context).executeAsync();
}
}
});
@@ -891,7 +891,7 @@ public class DBWriter {
adapter.setFeedFlattrStatus(feed);
adapter.close();
if (startFlattrClickWorker) {
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_TOAST).executeAsync();
+ new FlattrClickWorker(context).executeAsync();
}
}
});
diff --git a/src/de/danoeh/antennapod/storage/DownloadRequester.java b/src/de/danoeh/antennapod/storage/DownloadRequester.java
index 7bf21352a..d305c572b 100644
--- a/src/de/danoeh/antennapod/storage/DownloadRequester.java
+++ b/src/de/danoeh/antennapod/storage/DownloadRequester.java
@@ -14,6 +14,7 @@ import de.danoeh.antennapod.util.URLChecker;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import java.io.File;
import java.util.Map;
@@ -57,8 +58,9 @@ public class DownloadRequester {
* @return True if the download request was accepted, false otherwise.
*/
public boolean download(Context context, DownloadRequest request) {
- if (context == null) throw new IllegalArgumentException("context = null");
- if (request == null) throw new IllegalArgumentException("request = null");
+ Validate.notNull(context);
+ Validate.notNull(request);
+
if (downloads.containsKey(request.getSource())) {
if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
return false;
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 06c8b1fc9..671ac30d5 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -10,14 +10,23 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.feed.*;
-import de.danoeh.antennapod.service.download.DownloadStatus;
-import de.danoeh.antennapod.util.flattr.FlattrStatus;
+
+import org.apache.commons.lang3.Validate;
import java.util.Arrays;
import java.util.List;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedComponent;
+import de.danoeh.antennapod.feed.FeedImage;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.feed.FeedPreferences;
+import de.danoeh.antennapod.service.download.DownloadStatus;
+import de.danoeh.antennapod.util.flattr.FlattrStatus;
+
// TODO Remove media column from feeditem table
/**
@@ -1024,9 +1033,8 @@ public class PodDBAdapter {
* @throws IllegalArgumentException if limit < 0
*/
public final Cursor getCompletedMediaCursor(int limit) {
- if (limit < 0) {
- throw new IllegalArgumentException("Limit must be >= 0");
- }
+ Validate.isTrue(limit >= 0, "Limit must be >= 0");
+
Cursor c = db.query(TABLE_NAME_FEED_MEDIA, null,
KEY_PLAYBACK_COMPLETION_DATE + " > 0 LIMIT " + limit, null, null,
null, null);
diff --git a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
index 5ed9ff2b0..2496e112a 100644
--- a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
+++ b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
@@ -4,6 +4,8 @@ import android.util.Log;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.Feed;
import org.apache.commons.io.input.XmlStreamReader;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@@ -64,7 +66,7 @@ public class TypeGetter {
} else {
if (BuildConfig.DEBUG)
Log.d(TAG, "Type is invalid");
- throw new UnsupportedFeedtypeException(Type.INVALID);
+ throw new UnsupportedFeedtypeException(Type.INVALID, tag);
}
} else {
eventType = xpp.next();
@@ -73,7 +75,19 @@ public class TypeGetter {
} catch (XmlPullParserException e) {
e.printStackTrace();
- } catch (IOException e) {
+ // XML document might actually be a HTML document -> try to parse as HTML
+ String rootElement = null;
+ try {
+ if (Jsoup.parse(new File(feed.getFile_url()), null) != null) {
+ rootElement = "html";
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } finally {
+ throw new UnsupportedFeedtypeException(Type.INVALID, rootElement);
+ }
+
+ } catch (IOException e) {
e.printStackTrace();
}
}
diff --git a/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java b/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
index 67fbc9cc9..605dad2fb 100644
--- a/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
+++ b/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
@@ -5,18 +5,27 @@ import de.danoeh.antennapod.syndication.handler.TypeGetter.Type;
public class UnsupportedFeedtypeException extends Exception {
private static final long serialVersionUID = 9105878964928170669L;
private TypeGetter.Type type;
+ private String rootElement;
public UnsupportedFeedtypeException(Type type) {
super();
this.type = type;
-
}
-
- public TypeGetter.Type getType() {
+
+ public UnsupportedFeedtypeException(Type type, String rootElement) {
+ this.type = type;
+ this.rootElement = rootElement;
+ }
+
+ public TypeGetter.Type getType() {
return type;
}
-
- @Override
+
+ public String getRootElement() {
+ return rootElement;
+ }
+
+ @Override
public String getMessage() {
if (type == TypeGetter.Type.INVALID) {
return "Invalid type";
diff --git a/src/de/danoeh/antennapod/util/Converter.java b/src/de/danoeh/antennapod/util/Converter.java
index 46a0d30b4..f4c2b2f66 100644
--- a/src/de/danoeh/antennapod/util/Converter.java
+++ b/src/de/danoeh/antennapod/util/Converter.java
@@ -78,5 +78,26 @@ public final class Converter {
return String.format("%02d:%02d", h, m);
}
+
+ /** Converts long duration string (HH:MM:SS) to milliseconds. */
+ public static int durationStringLongToMs(String input) {
+ String[] parts = input.split(":");
+ if (parts.length != 3) {
+ return 0;
+ }
+ return Integer.valueOf(parts[0]) * 3600 * 1000 +
+ Integer.valueOf(parts[1]) * 60 * 1000 +
+ Integer.valueOf(parts[2]) * 1000;
+ }
+
+ /** Converts short duration string (HH:MM) to milliseconds. */
+ public static int durationStringShortToMs(String input) {
+ String[] parts = input.split(":");
+ if (parts.length != 2) {
+ return 0;
+ }
+ return Integer.valueOf(parts[0]) * 3600 * 1000 +
+ Integer.valueOf(parts[1]) * 1000 * 60;
+ }
}
diff --git a/src/de/danoeh/antennapod/util/DuckType.java b/src/de/danoeh/antennapod/util/DuckType.java
index 0dfc01508..163110418 100644
--- a/src/de/danoeh/antennapod/util/DuckType.java
+++ b/src/de/danoeh/antennapod/util/DuckType.java
@@ -6,6 +6,8 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import de.danoeh.antennapod.BuildConfig;
+
/**
* Allows "duck typing" or dynamic invocation based on method signature rather
* than type hierarchy. In other words, rather than checking whether something
@@ -63,7 +65,7 @@ public class DuckType {
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> T to(Class iface) {
- assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
+ if (BuildConfig.DEBUG && !iface.isInterface()) throw new AssertionError("cannot coerce object to a class, must be an interface");
if (isA(iface)) {
return (T) iface.cast(objectToCoerce);
}
diff --git a/src/de/danoeh/antennapod/util/URLChecker.java b/src/de/danoeh/antennapod/util/URLChecker.java
index eb522ffa8..9997daaf7 100644
--- a/src/de/danoeh/antennapod/util/URLChecker.java
+++ b/src/de/danoeh/antennapod/util/URLChecker.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.util;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
/**
@@ -27,12 +30,16 @@ public final class URLChecker {
*/
public static String prepareURL(String url) {
StringBuilder builder = new StringBuilder();
+ url = StringUtils.trim(url);
if (url.startsWith("feed://")) {
if (BuildConfig.DEBUG) Log.d(TAG, "Replacing feed:// with http://");
url = url.replaceFirst("feed://", "http://");
} else if (url.startsWith("pcast://")) {
if (BuildConfig.DEBUG) Log.d(TAG, "Replacing pcast:// with http://");
url = url.replaceFirst("pcast://", "http://");
+ } else if (url.startsWith("itpc")) {
+ if (BuildConfig.DEBUG) Log.d(TAG, "Replacing itpc:// with http://");
+ url = url.replaceFirst("itpc://", "http://");
} else if (!(url.startsWith("http://") || url.startsWith("https://"))) {
if (BuildConfig.DEBUG) Log.d(TAG, "Adding http:// at the beginning of the URL");
builder.append("http://");
diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
index 6733430da..2c7a7f074 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.util.menuhandler;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
@@ -113,6 +114,25 @@ public class FeedItemMenuHandler {
return true;
}
+ /**
+ * The same method as onPrepareMenu(MenuInterface, FeedItem, boolean, QueueAccess), but lets the
+ * caller also specify a list of menu items that should not be shown.
+ *
+ * @param excludeIds Menu item that should be excluded
+ * @return true if selectedItem is not null.
+ */
+ public static boolean onPrepareMenu(MenuInterface mi,
+ FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess, int... excludeIds) {
+ boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ if (rc && excludeIds != null) {
+ for (int id : excludeIds) {
+ mi.setItemVisibility(id, false);
+ }
+ }
+
+ return rc;
+ }
+
public static boolean onMenuItemClicked(Context context, int menuItemId,
FeedItem selectedItem) throws DownloadRequestException {
DownloadRequester requester = DownloadRequester.getInstance();
diff --git a/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
index e75fa394a..7aa04d24c 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
@@ -4,6 +4,7 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
+
import de.danoeh.antennapod.R;
/**
@@ -17,4 +18,14 @@ public class MenuItemUtils {
MenuItemCompat.setActionView(item, searchView);
return item;
}
+
+ /**
+ * Checks if the navigation drawer of the DrawerActivity is opened. This can be useful for Fragments
+ * that hide their menu if the navigation drawer is open.
+ *
+ * @return True if the drawer is open, false otherwise (also if the parameter is null)
+ */
+ public static boolean isActivityDrawerOpen(NavDrawerActivity activity) {
+ return activity != null && activity.isDrawerOpen();
+ }
}
diff --git a/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java b/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java
new file mode 100644
index 000000000..9c611a452
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java
@@ -0,0 +1,9 @@
+package de.danoeh.antennapod.util.menuhandler;
+
+/**
+ * Defines useful methods for activities that have a navigation drawer
+ */
+public interface NavDrawerActivity {
+
+ public boolean isDrawerOpen();
+}
diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java
index 8eefb0be5..9ed45abfc 100644
--- a/src/de/danoeh/antennapod/util/playback/Playable.java
+++ b/src/de/danoeh/antennapod/util/playback/Playable.java
@@ -12,6 +12,7 @@ import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.Validate;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -216,9 +217,8 @@ public interface Playable extends Parcelable,
private Playable playable;
public DefaultPlayableImageLoader(Playable playable) {
- if (playable == null) {
- throw new IllegalArgumentException("Playable must not be null");
- }
+ Validate.notNull(playable);
+
this.playable = playable;
}
diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
index 1992fce2c..64dbf4868 100644
--- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java
+++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
@@ -15,12 +15,17 @@ import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.service.playback.PlayerStatus;
@@ -37,7 +42,6 @@ import java.util.concurrent.*;
public abstract class PlaybackController {
private static final String TAG = "PlaybackController";
- public static final int DEFAULT_SEEK_DELTA = 30000;
public static final int INVALID_TIME = -1;
private final Activity activity;
@@ -62,8 +66,8 @@ public abstract class PlaybackController {
private boolean reinitOnPause;
public PlaybackController(Activity activity, boolean reinitOnPause) {
- if (activity == null)
- throw new IllegalArgumentException("activity = null");
+ Validate.notNull(activity);
+
this.activity = activity;
this.reinitOnPause = reinitOnPause;
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
@@ -360,7 +364,7 @@ public abstract class PlaybackController {
@Override
public void onReceive(Context context, Intent intent) {
if (isConnectedToPlaybackService()) {
- if (intent.getAction().equals(
+ if (StringUtils.equals(intent.getAction(),
PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
release();
onShutdownNotification();
@@ -605,7 +609,7 @@ public abstract class PlaybackController {
@Override
public void onClick(View v) {
if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(-DEFAULT_SEEK_DELTA);
+ playbackService.seekDelta(-UserPreferences.getSeekDeltaMs());
}
}
};
@@ -616,7 +620,7 @@ public abstract class PlaybackController {
@Override
public void onClick(View v) {
if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(DEFAULT_SEEK_DELTA);
+ playbackService.seekDelta(UserPreferences.getSeekDeltaMs());
}
}
};
@@ -680,6 +684,12 @@ public abstract class PlaybackController {
}
}
+ public void seekTo(int time) {
+ if (playbackService != null) {
+ playbackService.seekTo(time);
+ }
+ }
+
public void setVideoSurface(SurfaceHolder holder) {
if (playbackService != null) {
playbackService.setVideoSurface(holder);
diff --git a/src/de/danoeh/antennapod/util/playback/Timeline.java b/src/de/danoeh/antennapod/util/playback/Timeline.java
new file mode 100644
index 000000000..ceed68183
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/playback/Timeline.java
@@ -0,0 +1,161 @@
+package de.danoeh.antennapod.util.playback;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.Log;
+import android.util.TypedValue;
+
+import org.apache.commons.lang3.Validate;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.util.Converter;
+import de.danoeh.antennapod.util.ShownotesProvider;
+
+/**
+ * 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.
+ */
+public class Timeline {
+ private static final String TAG = "Timeline";
+
+ private static final String WEBVIEW_STYLE = "@font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } a.timecode { color: #669900; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }";
+
+
+ private ShownotesProvider shownotesProvider;
+
+
+ private final String colorString;
+ private final int pageMargin;
+
+ public Timeline(Context context, ShownotesProvider shownotesProvider) {
+ if (shownotesProvider == null) throw new IllegalArgumentException("shownotesProvider = null");
+ this.shownotesProvider = shownotesProvider;
+
+ TypedArray res = context
+ .getTheme()
+ .obtainStyledAttributes(
+ new int[]{android.R.attr.textColorPrimary});
+ int colorResource = res.getColor(0, 0);
+ colorString = String.format("#%06X",
+ 0xFFFFFF & colorResource);
+ res.recycle();
+
+ pageMargin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, 8, context.getResources()
+ .getDisplayMetrics()
+ );
+ }
+
+ 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(?:(?:(([0-9][0-9])):))?(([0-9][0-9])):(([0-9][0-9]))\\b");
+
+ /**
+ * Applies an app-specific CSS stylesheet and adds timecode links (optional).
+ * <p/>
+ * This method does NOT change the original shownotes string of the shownotesProvider object and it should
+ * also not be changed by the caller.
+ *
+ * @param addTimecodes True if this method should add timecode links
+ * @return The processed HTML string.
+ */
+ public String processShownotes(final boolean addTimecodes) {
+ final Playable playable = (shownotesProvider instanceof Playable) ? (Playable) shownotesProvider : null;
+
+ // load shownotes
+
+ String shownotes;
+ try {
+ shownotes = shownotesProvider.loadShownotes().call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ if (shownotes == null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "shownotesProvider contained no shownotes. Returning empty string");
+ return "";
+ }
+
+ Document document = Jsoup.parse(shownotes);
+
+ // apply style
+ String styleStr = String.format(WEBVIEW_STYLE, colorString, "100%", pageMargin,
+ pageMargin, pageMargin, pageMargin);
+ document.head().appendElement("style").attr("type", "text/css").text(styleStr);
+
+ // apply timecode links
+ if (addTimecodes) {
+ Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes");
+ for (Element element : elementsWithTimeCodes) {
+ Matcher matcherLong = TIMECODE_REGEX.matcher(element.text());
+ StringBuffer buffer = new StringBuffer();
+ while (matcherLong.find()) {
+ String h = matcherLong.group(1);
+ String group = matcherLong.group(0);
+ int time = (h != null) ? Converter.durationStringLongToMs(group) :
+ Converter.durationStringShortToMs(group);
+
+ String rep;
+ if (playable == null || playable.getDuration() > time) {
+ rep = String.format(TIMECODE_LINK, time, group);
+ } else {
+ rep = group;
+ }
+ matcherLong.appendReplacement(buffer, rep);
+ }
+ matcherLong.appendTail(buffer);
+
+ element.html(buffer.toString());
+ }
+ }
+
+ Log.i(TAG, "Out: " + document.toString());
+ return document.toString();
+ }
+
+
+ /**
+ * Returns true if the given link is a timecode link.
+ */
+ public static boolean isTimecodeLink(String link) {
+ return link != null && link.matches(TIMECODE_LINK_REGEX.pattern());
+ }
+
+ /**
+ * Returns the time in milliseconds that is attached to this link or -1
+ * if the link is no valid timecode link.
+ */
+ public static int getTimecodeLinkTime(String link) {
+ if (isTimecodeLink(link)) {
+ Matcher m = TIMECODE_LINK_REGEX.matcher(link);
+
+ try {
+ if (m.find()) {
+ return Integer.valueOf(m.group(1));
+ }
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+ return -1;
+ }
+
+
+ public void setShownotesProvider(ShownotesProvider shownotesProvider) {
+ Validate.notNull(shownotesProvider);
+ this.shownotesProvider = shownotesProvider;
+ }
+}
diff --git a/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java b/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java
new file mode 100644
index 000000000..ac38ec876
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java
@@ -0,0 +1,78 @@
+package de.danoeh.antennapod.util.syndication;
+
+import android.net.Uri;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Finds RSS/Atom URLs in a HTML document using the auto-discovery techniques described here:
+ * <p/>
+ * http://www.rssboard.org/rss-autodiscovery
+ * <p/>
+ * http://blog.whatwg.org/feed-autodiscovery
+ */
+public class FeedDiscoverer {
+
+ private static final String MIME_RSS = "application/rss+xml";
+ private static final String MIME_ATOM = "application/atom+xml";
+
+ /**
+ * Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
+ *
+ * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
+ * a title cannot be found).
+ */
+ public Map<String, String> findLinks(File in, String baseUrl) throws IOException {
+ return findLinks(Jsoup.parse(in, null), baseUrl);
+ }
+
+ /**
+ * Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
+ *
+ * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
+ * a title cannot be found).
+ */
+ public Map<String, String> findLinks(String in, String baseUrl) throws IOException {
+ return findLinks(Jsoup.parse(in), baseUrl);
+ }
+
+ private Map<String, String> findLinks(Document document, String baseUrl) {
+ Map<String, String> res = new LinkedHashMap<String, String>();
+ Elements links = document.head().getElementsByTag("link");
+ for (Element link : links) {
+ String rel = link.attr("rel");
+ String href = link.attr("href");
+ if (!StringUtils.isEmpty(href) &&
+ (rel.equals("alternate") || rel.equals("feed"))) {
+ String type = link.attr("type");
+ if (type.equals(MIME_RSS) || type.equals(MIME_ATOM)) {
+ String title = link.attr("title");
+ String processedUrl = processURL(baseUrl, href);
+ if (processedUrl != null) {
+ res.put(processedUrl,
+ (StringUtils.isEmpty(title)) ? href : title);
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ private String processURL(String baseUrl, String strUrl) {
+ Uri uri = Uri.parse(strUrl);
+ if (uri.isRelative()) {
+ Uri res = Uri.parse(baseUrl).buildUpon().path(strUrl).build();
+ return (res != null) ? res.toString() : null;
+ } else {
+ return strUrl;
+ }
+ }
+}
diff --git a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java
index 2dfd6a544..23fc224c8 100644
--- a/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java
+++ b/src/instrumentationTest/de/test/antennapod/ui/MainActivityTest.java
@@ -66,8 +66,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
-
- final View home = solo.getView(android.R.id.home);
+ final View home = solo.getView(UITestUtils.HOME_VIEW);
// all episodes
solo.waitForView(android.R.id.list);
diff --git a/src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java b/src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java
index 835973560..98d93a35d 100644
--- a/src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java
+++ b/src/instrumentationTest/de/test/antennapod/ui/PlaybackTest.java
@@ -78,7 +78,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
private void startLocalPlaybackFromQueue() {
assertTrue(solo.waitForActivity(MainActivity.class));
- solo.clickOnView(solo.getView(android.R.id.home));
+ solo.clickOnView(solo.getView(UITestUtils.HOME_VIEW));
solo.clickOnText(solo.getString(R.string.queue_label));
assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
solo.clickOnImageButton(0);
diff --git a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java
index a02d4e55c..8877d46d6 100644
--- a/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java
+++ b/src/instrumentationTest/de/test/antennapod/ui/UITestUtils.java
@@ -1,7 +1,11 @@
package instrumentationTest.de.test.antennapod.ui;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
+import android.os.Build;
+
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.*;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.storage.PodDBAdapter;
@@ -24,6 +28,7 @@ import java.util.List;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
+@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class UITestUtils {
private static final String DATA_FOLDER = "test/UITestUtils";
@@ -31,6 +36,8 @@ public class UITestUtils {
public static final int NUM_FEEDS = 5;
public static final int NUM_ITEMS_PER_FEED = 10;
+ public static final int HOME_VIEW = (Build.VERSION.SDK_INT >= 11) ? android.R.id.home : R.id.home;
+
private Context context;
private HTTPBin server = new HTTPBin();
diff --git a/src/instrumentationTest/de/test/antennapod/util/ConverterTest.java b/src/instrumentationTest/de/test/antennapod/util/ConverterTest.java
new file mode 100644
index 000000000..8e5674b06
--- /dev/null
+++ b/src/instrumentationTest/de/test/antennapod/util/ConverterTest.java
@@ -0,0 +1,35 @@
+package instrumentationTest.de.test.antennapod.util;
+
+import android.test.AndroidTestCase;
+
+import de.danoeh.antennapod.util.Converter;
+
+/**
+ * Test class for converter
+ */
+public class ConverterTest extends AndroidTestCase {
+
+ public void testGetDurationStringLong() throws Exception {
+ String expected = "13:05:10";
+ int input = 47110000;
+ assertEquals(expected, Converter.getDurationStringLong(input));
+ }
+
+ public void testGetDurationStringShort() throws Exception {
+ String expected = "13:05";
+ int input = 47110000;
+ assertEquals(expected, Converter.getDurationStringShort(input));
+ }
+
+ public void testDurationStringLongToMs() throws Exception {
+ String input = "01:20:30";
+ long expected = 4830000;
+ assertEquals(expected, Converter.durationStringLongToMs(input));
+ }
+
+ public void testDurationStringShortToMs() throws Exception {
+ String input = "8:30";
+ long expected = 30600000;
+ assertEquals(expected, Converter.durationStringShortToMs(input));
+ }
+}
diff --git a/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java b/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
index 64d45dc60..91e5d966f 100644
--- a/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
+++ b/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
@@ -37,4 +37,22 @@ public class URLCheckerTest extends AndroidTestCase {
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);
}
+
+ public void testItcpProtocol() {
+ final String in = "itpc://example.com";
+ final String out = URLChecker.prepareURL(in);
+ assertEquals("http://example.com", out);
+ }
+
+ public void testWhiteSpaceUrlShouldNotAppend() {
+ final String in = "\n http://example.com \t";
+ final String out = URLChecker.prepareURL(in);
+ assertEquals("http://example.com", out);
+ }
+
+ public void testWhiteSpaceShouldAppend() {
+ final String in = "\n example.com \t";
+ final String out = URLChecker.prepareURL(in);
+ assertEquals("http://example.com", out);
+ }
}
diff --git a/src/instrumentationTest/de/test/antennapod/util/playback/TimelineTest.java b/src/instrumentationTest/de/test/antennapod/util/playback/TimelineTest.java
new file mode 100644
index 000000000..1b1d011d4
--- /dev/null
+++ b/src/instrumentationTest/de/test/antennapod/util/playback/TimelineTest.java
@@ -0,0 +1,127 @@
+package instrumentationTest.de.test.antennapod.util.playback;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.Date;
+import java.util.List;
+
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.util.playback.Playable;
+import de.danoeh.antennapod.util.playback.Timeline;
+
+/**
+ * Test class for timeline
+ */
+public class TimelineTest extends InstrumentationTestCase {
+
+ private Context context;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ context = getInstrumentation().getTargetContext();
+ }
+
+ private Playable newTestPlayable(List<Chapter> chapters, String shownotes) {
+ FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), true, null);
+ item.setChapters(chapters);
+ item.setContentEncoded(shownotes);
+ FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
+ media.setDuration(Integer.MAX_VALUE);
+ item.setMedia(media);
+ return media;
+ }
+
+ public void testProcessShownotesAddTimecodeHHMMSSNoChapters() throws Exception {
+ final String timeStr = "10:11:12";
+ final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeHHMMNoChapters() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode " + timeStr + " here.</p>");
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeParentheses() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode (" + timeStr + ") here.</p>");
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeBrackets() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode [" + timeStr + "] here.</p>");
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ public void testProcessShownotesAddTimecodeAngleBrackets() throws Exception {
+ final String timeStr = "10:11";
+ final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
+
+ Playable p = newTestPlayable(null, "<p> Some test text with a timecode <" + timeStr + "> here.</p>");
+ Timeline t = new Timeline(context, p);
+ String res = t.processShownotes(true);
+ checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
+ }
+
+ private void checkLinkCorrect(String res, long[] timecodes, String[] timecodeStr) {
+ assertNotNull(res);
+ Document d = Jsoup.parse(res);
+ Elements links = d.body().getElementsByTag("a");
+ int countedLinks = 0;
+ for (Element link : links) {
+ String href = link.attributes().get("href");
+ String text = link.text();
+ if (href.startsWith("antennapod://")) {
+ assertTrue(href.endsWith(String.valueOf(timecodes[countedLinks])));
+ assertEquals(timecodeStr[countedLinks], text);
+ countedLinks++;
+ assertTrue("Contains too many links: " + countedLinks + " > " + timecodes.length, countedLinks <= timecodes.length);
+ }
+ }
+ assertEquals(timecodes.length, countedLinks);
+ }
+
+ public void testIsTimecodeLink() throws Exception {
+ 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"));
+ }
+
+ public void testGetTimecodeLinkTime() throws Exception {
+ assertEquals(-1, Timeline.getTimecodeLinkTime(null));
+ assertEquals(-1, Timeline.getTimecodeLinkTime("http://timecode/123"));
+ assertEquals(123, Timeline.getTimecodeLinkTime("antennapod://timecode/123"));
+
+ }
+}
diff --git a/src/instrumentationTest/de/test/antennapod/util/syndication/FeedDiscovererTest.java b/src/instrumentationTest/de/test/antennapod/util/syndication/FeedDiscovererTest.java
new file mode 100644
index 000000000..51a28089d
--- /dev/null
+++ b/src/instrumentationTest/de/test/antennapod/util/syndication/FeedDiscovererTest.java
@@ -0,0 +1,109 @@
+package instrumentationTest.de.test.antennapod.util.syndication;
+
+import android.test.InstrumentationTestCase;
+import de.danoeh.antennapod.util.syndication.FeedDiscoverer;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Map;
+
+/**
+ * Test class for FeedDiscoverer
+ */
+public class FeedDiscovererTest extends InstrumentationTestCase {
+
+ private FeedDiscoverer fd;
+
+ private File testDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ fd = new FeedDiscoverer();
+ testDir = getInstrumentation().getTargetContext().getExternalFilesDir("FeedDiscovererTest");
+ testDir.mkdir();
+ assertTrue(testDir.exists());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ FileUtils.deleteDirectory(testDir);
+ super.tearDown();
+ }
+
+ private String createTestHtmlString(String rel, String type, String href, String title) {
+ return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\" title=\"%s\"></head><body></body></html>",
+ rel, type, href, title);
+ }
+
+ private String createTestHtmlString(String rel, String type, String href) {
+ return String.format("<html><head><title>Test</title><link rel=\"%s\" type=\"%s\" href=\"%s\"></head><body></body></html>",
+ rel, type, href);
+ }
+
+ private void checkFindUrls(boolean isAlternate, boolean isRss, boolean withTitle, boolean isAbsolute, boolean fromString) throws Exception {
+ final String title = "Test title";
+ final String hrefAbs = "http://example.com/feed";
+ final String hrefRel = "/feed";
+ final String base = "http://example.com";
+
+ final String rel = (isAlternate) ? "alternate" : "feed";
+ final String type = (isRss) ? "application/rss+xml" : "application/atom+xml";
+ final String href = (isAbsolute) ? hrefAbs : hrefRel;
+
+ Map<String, String> res;
+ String html = (withTitle) ? createTestHtmlString(rel, type, href, title)
+ : createTestHtmlString(rel, type, href);
+ if (fromString) {
+ res = fd.findLinks(html, base);
+ } else {
+ File testFile = new File(testDir, "feed");
+ FileOutputStream out = new FileOutputStream(testFile);
+ IOUtils.write(html, out);
+ out.close();
+ res = fd.findLinks(testFile, base);
+ }
+
+ assertNotNull(res);
+ assertEquals(1, res.size());
+ for (String key : res.keySet()) {
+ assertEquals(hrefAbs, key);
+ }
+ assertTrue(res.containsKey(hrefAbs));
+ if (withTitle) {
+ assertEquals(title, res.get(hrefAbs));
+ } else {
+ assertEquals(href, res.get(hrefAbs));
+ }
+ }
+
+ public void testAlternateRSSWithTitleAbsolute() throws Exception {
+ checkFindUrls(true, true, true, true, true);
+ }
+
+ public void testAlternateRSSWithTitleRelative() throws Exception {
+ checkFindUrls(true, true, true, false, true);
+ }
+
+ public void testAlternateRSSNoTitleAbsolute() throws Exception {
+ checkFindUrls(true, true, false, true, true);
+ }
+
+ public void testAlternateRSSNoTitleRelative() throws Exception {
+ checkFindUrls(true, true, false, false, true);
+ }
+
+ public void testAlternateAtomWithTitleAbsolute() throws Exception {
+ checkFindUrls(true, false, true, true, true);
+ }
+
+ public void testFeedAtomWithTitleAbsolute() throws Exception {
+ checkFindUrls(false, false, true, true, true);
+ }
+
+ public void testAlternateRSSWithTitleAbsoluteFromFile() throws Exception {
+ checkFindUrls(true, true, true, true, false);
+ }
+}
diff --git a/submodules/dslv b/submodules/dslv
-Subproject 5f58dff340f705b4dc7f920f81c33d382919c3a
+Subproject 80011c50e444e1c7d5e13b57bdb127b524a1ff9