summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-08-06 22:04:55 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2014-08-06 22:04:55 +0200
commita8bf235017d5896c0691ad056727dafc72c63596 (patch)
treebfa2b3a02fc9704bc672b985351dc6e84dccd945
parent583b2adaae5769fb8bee4d63e4ef10846d7de1b6 (diff)
parent8dc740bb8f5b6149d6d0c70a00d4f4831f33a427 (diff)
downloadAntennaPod-a8bf235017d5896c0691ad056727dafc72c63596.zip
Merge branch 'develop'0.9.9.3
-rw-r--r--AndroidManifest.xml4
-rw-r--r--CHANGELOG.md6
-rw-r--r--assets/LICENSE_OKHTTP.txt11
-rw-r--r--assets/LICENSE_OKIO.txt202
-rw-r--r--assets/LICENSE_PICASSO.txt13
-rw-r--r--assets/about.html18
-rw-r--r--build.gradle14
-rw-r--r--pom.xml2
-rw-r--r--proguard.cfg4
-rw-r--r--res/layout/feeditem_dialog.xml3
-rw-r--r--res/values-ca/strings.xml11
-rw-r--r--res/values-fr/strings.xml10
-rw-r--r--res/values-pt/strings.xml12
-rw-r--r--res/values-sv-rSE/strings.xml15
-rw-r--r--src/de/danoeh/antennapod/AppConfig.java2
-rw-r--r--src/de/danoeh/antennapod/PodcastApp.java8
-rw-r--r--src/de/danoeh/antennapod/activity/AudioplayerActivity.java13
-rw-r--r--src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java38
-rw-r--r--src/de/danoeh/antennapod/activity/FeedInfoActivity.java7
-rw-r--r--src/de/danoeh/antennapod/activity/PreferenceActivity.java2
-rw-r--r--src/de/danoeh/antennapod/activity/VideoplayerActivity.java5
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java17
-rw-r--r--src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java527
-rw-r--r--src/de/danoeh/antennapod/adapter/NavListAdapter.java11
-rw-r--r--src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java21
-rw-r--r--src/de/danoeh/antennapod/adapter/QueueListAdapter.java15
-rw-r--r--src/de/danoeh/antennapod/adapter/SearchlistAdapter.java123
-rw-r--r--src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java15
-rw-r--r--src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java115
-rw-r--r--src/de/danoeh/antennapod/asynctask/CachedBitmap.java27
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageDiskCache.java397
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageLoader.java246
-rw-r--r--src/de/danoeh/antennapod/asynctask/PicassoImageResource.java25
-rw-r--r--src/de/danoeh/antennapod/asynctask/PicassoProvider.java141
-rw-r--r--src/de/danoeh/antennapod/feed/Feed.java14
-rw-r--r--src/de/danoeh/antennapod/feed/FeedImage.java42
-rw-r--r--src/de/danoeh/antennapod/feed/FeedItem.java57
-rw-r--r--src/de/danoeh/antennapod/feed/FeedMedia.java64
-rw-r--r--src/de/danoeh/antennapod/fragment/CoverFragment.java146
-rw-r--r--src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java408
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemlistFragment.java11
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackService.java41
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java133
-rw-r--r--src/de/danoeh/antennapod/storage/DBTasks.java4
-rw-r--r--src/de/danoeh/antennapod/util/BitmapDecoder.java50
-rw-r--r--src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example7
-rw-r--r--src/de/danoeh/antennapod/util/flattr/FlattrUtils.java525
-rw-r--r--src/de/danoeh/antennapod/util/playback/ExternalMedia.java28
-rw-r--r--src/de/danoeh/antennapod/util/playback/IPlayer.java3
-rw-r--r--src/de/danoeh/antennapod/util/playback/Playable.java79
-rw-r--r--src/instrumentationTest/de/test/antennapod/ui/VideoplayerActivityTest.java38
-rw-r--r--src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java2
52 files changed, 1700 insertions, 2032 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 65aa8ea59..65eac99ea 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="39"
- android:versionName="0.9.9.2">
+ android:versionCode="40"
+ android:versionName="0.9.9.3">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cdd715ae..2508edf49 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
Change Log
==========
+Version 0.9.9.3
+---------------
+* Fixed video playback problems
+* Improved image loading
+* Other bugfixes and improvements
+
Version 0.9.9.2
---------------
* Added support for feed discovery if a website URL is entered
diff --git a/assets/LICENSE_OKHTTP.txt b/assets/LICENSE_OKHTTP.txt
new file mode 100644
index 000000000..90edcee40
--- /dev/null
+++ b/assets/LICENSE_OKHTTP.txt
@@ -0,0 +1,11 @@
+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_OKIO.txt b/assets/LICENSE_OKIO.txt
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/assets/LICENSE_OKIO.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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_PICASSO.txt b/assets/LICENSE_PICASSO.txt
new file mode 100644
index 000000000..0bf6b9f8e
--- /dev/null
+++ b/assets/LICENSE_PICASSO.txt
@@ -0,0 +1,13 @@
+Copyright 2013 Square, Inc.
+
+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/about.html b/assets/about.html
index d572b7518..8b8746add 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.2</p>
+ <p>AntennaPod, Version 0.9.9.3</p>
<p>Copyright © 2014 Daniel Oeh</p>
@@ -53,16 +53,30 @@
by Jake Wharton, licensed under the Apache 2.0 license <a href="LICENSE_NINE_OLD_ANDROIDS.txt">(View)</a>
<h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2>
-by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE_COMMONS.txt">(View)</a>
+by The Apache Software Foundation, licensed under the Apache 2.0 license <a
+ href="LICENSE_APACHE_COMMONS.txt">(View)</a>
+
<h2>flattr4j <a href="http://www.shredzone.org/projects/flattr4j/wiki">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_FLATTR4J.txt">(View)</a>
+
<h2>drag-sort-listview <a href="https://github.com/bauerca/drag-sort-listview">(Link)</a></h2>
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>
+<h2>Picasso <a href="https://github.com/square/picasso">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_PICASSO.txt">(View)</a>
+
+<h2>OkHttp <a href="https://github.com/square/okhttp">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
+
+<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_OKIO.txt">(View)</a>
</html>
diff --git a/build.gradle b/build.gradle
index 140d483da..441b17385 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,6 +34,10 @@ dependencies {
exclude group: 'com.android.support', module: 'support-v4'
}
compile 'org.jsoup:jsoup:1.7.3'
+ compile 'com.squareup.picasso:picasso:2.3.+'
+ compile 'com.squareup.okhttp:okhttp:2.0.+'
+ compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
+ compile 'com.squareup.okio:okio:1.0.0'
}
android {
@@ -85,13 +89,23 @@ android {
}
buildTypes {
+ def STRING = "String"
+ def FLATTR_APP_KEY = "FLATTR_APP_KEY"
+ def FLATTR_APP_SECRET = "FLATTR_APP_SECRET"
+ def mFlattrAppKey = (project.hasProperty('flattrAppKey')) ? flattrAppKey : "\"\""
+ def mFlattrAppSecret = (project.hasProperty('flattrAppSecret')) ? flattrAppSecret : "\"\""
+
debug {
applicationIdSuffix ".debug"
+ buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey
+ buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
}
release {
runProguard true
proguardFile 'proguard.cfg'
signingConfig signingConfigs.releaseConfig
+ buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey
+ buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
}
}
diff --git a/pom.xml b/pom.xml
index 658989cee..81e223b10 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.2</version>
+ <version>0.9.9.3</version>
<name>AntennaPod</name>
diff --git a/proguard.cfg b/proguard.cfg
index 323e0b673..1838f007c 100644
--- a/proguard.cfg
+++ b/proguard.cfg
@@ -50,6 +50,10 @@
-keep public class org.jsoup.** {
public *;
}
+
+-dontwarn com.squareup.okhttp.**
+-dontwarn okio.**
+
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
diff --git a/res/layout/feeditem_dialog.xml b/res/layout/feeditem_dialog.xml
index 7d05603e8..c8dca8460 100644
--- a/res/layout/feeditem_dialog.xml
+++ b/res/layout/feeditem_dialog.xml
@@ -11,7 +11,8 @@
android:layout_margin="16dp"
android:id="@+id/txtvTitle"
android:layout_alignParentTop="true"
- style="@style/AntennaPod.Dialog.Title"/>
+ style="@style/AntennaPod.Dialog.Title"
+ android:maxLines="10"/>
<View
android:id="@+id/title_divider"
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index a9f96fb31..ae2addb05 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -31,6 +31,7 @@
<string name="copy_url_label">Copia l\'enllaç</string>
<string name="share_url_label">Comparteix l\'enllaç</string>
<string name="copied_url_msg">S\'ha copiat l\'enllaç al porta-retalls.</string>
+ <string name="go_to_position_label">Vés a aquesta posició</string>
<!--Playback history-->
<string name="clear_history_label">Esborra l\'historial</string>
<!--Other-->
@@ -59,6 +60,7 @@
<string name="auto_download_label">Inclou a baixades automàtiques</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Enllaç del canal</string>
+ <string name="etxtFeedurlHint">URL, canal o lloc web</string>
<string name="txtvfeedurl_label">Afegeix podcast amb l\'URL</string>
<string name="podcastdirectories_label">Cerca podcast al directori</string>
<string name="podcastdirectories_descr">Podeu cercar nous podcasts al directori de gpodder.net mitjançant el seu nom, categoria o popularitat.</string>
@@ -133,6 +135,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">S\'està carregant</string>
<string name="playbackservice_notification_title">Podcast en reproducció</string>
+ <string name="unknown_media_key">AntennaPod - Control desconegut: %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">Buida la cua</string>
<string name="undo">Desfés</string>
@@ -146,6 +149,7 @@
<string name="return_home_label">Torna a l\'inici</string>
<string name="flattr_auth_success">L\'autenticació ha acabat correctament. Ja podeu compartir amb Flattr des de l\'aplicació.</string>
<string name="no_flattr_token_title">No s\'ha trobat cap testimoni Flattr</string>
+ <string name="no_flattr_token_notification_msg">Sembla que el compte flattr no està vinculat amb AntennaPod. Toqueu aquí per autenticar-vos.</string>
<string name="no_flattr_token_msg">Sembla que el vostre compte de Flattr no està vinculat amb AntennaPod. Podeu connectar el vostre compte Flattr amb AntennaPod per a compartir continguts des de l\'aplicació, o bé accediu a la plana web de Flattr i compartiu els continguts des d\'allà.</string>
<string name="authenticate_now_label">Autentica</string>
<string name="action_forbidden_title">L\'acció no és permesa</string>
@@ -199,6 +203,7 @@
<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">Configura la compartició automàtica per Flattr</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>
@@ -221,9 +226,15 @@
<string name="pref_gpodnet_setlogin_information_sum">Canvia les dades d\'inici de sessió del vostre compte de gpodder.net</string>
<string name="pref_playback_speed_title">Velocitats de reproducció</string>
<string name="pref_playback_speed_sum">Personalitzeu les velocitats disponibles per a una velocitat de reproducció d\'àudio variable</string>
+ <string name="pref_seek_delta_title">Salta a l\'instant</string>
+ <string name="pref_seek_delta_sum">Salta aquesta quantitat de segons en rebobinar o en avançar ràpidament</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-->
+ <string name="auto_flattr_enable">Activa la compartició automàtica per Flattr</string>
+ <string name="auto_flattr_after_percent">Comparteix per Flattr l\'episodi en haver-ne reproduït el %d per cent</string>
+ <string name="auto_flattr_ater_beginning">Comparteix per Flattr l\'episodi en haver-ne iniciat la reproducció</string>
+ <string name="auto_flattr_ater_end">Comparteix per Flattr l\'episodi en acabar-se\'n la reproducció</string>
<!--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-fr/strings.xml b/res/values-fr/strings.xml
index 76ecd8340..afc441b99 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -31,6 +31,7 @@
<string name="copy_url_label">Copier l\'URL</string>
<string name="share_url_label">Partager l\'URL</string>
<string name="copied_url_msg">URL copiée dans le presse-papier</string>
+ <string name="go_to_position_label">Aller à cette position</string>
<!--Playback history-->
<string name="clear_history_label">Effacer le journal</string>
<!--Other-->
@@ -59,6 +60,7 @@
<string name="auto_download_label">Télécharger automatiquement à l\'avenir</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL du flux</string>
+ <string name="etxtFeedurlHint">URL ou flux ou site web</string>
<string name="txtvfeedurl_label">Ajouter un podcast par son URL</string>
<string name="podcastdirectories_label">Trouver le podcast dans la bibliothèque</string>
<string name="podcastdirectories_descr">Vous pouvez chercher de nouveaux podcasts en filtrant par nom, catégorie ou popularité dans la bibliothèque gpodder.net</string>
@@ -133,6 +135,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">Mise en mémoire</string>
<string name="playbackservice_notification_title">Lecture de podcast en cours</string>
+ <string name="unknown_media_key">AntennaPod - Touche média inconnue : %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">Effacer la liste</string>
<string name="undo">Annuler</string>
@@ -146,6 +149,7 @@
<string name="return_home_label">Revenir au départ</string>
<string name="flattr_auth_success">L\'authentification a réussi. Vous pouvez maintenant flattr depuis cette application.</string>
<string name="no_flattr_token_title">Aucun jeton Flattr trouvé.</string>
+ <string name="no_flattr_token_notification_msg">Votre compte flattr semble ne pas être connecté à AntennaPod. Touchez ici pour vous connecter.</string>
<string name="no_flattr_token_msg">Votre compte Flattr se semble pas être connecté à AntennaPod. Vous pouvez soit connecter votre compte Flattr à AntennaPod pour pouvoir flattr depuis l\'application, ou vous pouvez aller sur le site de ce que vous voulez flattr.</string>
<string name="authenticate_now_label">S\'authentifier</string>
<string name="action_forbidden_title">Action interdite</string>
@@ -199,6 +203,7 @@
<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">Configurer les paiements flattr automatiques</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>
@@ -221,9 +226,14 @@
<string name="pref_gpodnet_setlogin_information_sum">Modifier les information de connexion pour votre compte gpodder.net</string>
<string name="pref_playback_speed_title">Vitesses de lecture</string>
<string name="pref_playback_speed_sum">Modifier la liste des vitesses disponibles pour la lecture audio</string>
+ <string name="pref_seek_delta_sum">Bouger d\'autant de secondes en rembobinant ou en faisant une avance rapide </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-->
+ <string name="auto_flattr_enable">Activer le paiement flattr automatique</string>
+ <string name="auto_flattr_after_percent">Lancer un paiement flattr pour un épisode dès que %d de l\'épisode a été joué</string>
+ <string name="auto_flattr_ater_beginning">Lancer le paiement flattr d\'un épisode dès que la lecture commence</string>
+ <string name="auto_flattr_ater_end">Lancer le paiement flattr d\'un épisode à la fin de la lecture</string>
<!--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-pt/strings.xml b/res/values-pt/strings.xml
index 1a49ee1d5..f1e525384 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -31,6 +31,7 @@
<string name="copy_url_label">Copiar URL</string>
<string name="share_url_label">Partilhar URL</string>
<string name="copied_url_msg">URL copiado para a área de transferência.</string>
+ <string name="go_to_position_label">Ir para esta posição</string>
<!--Playback history-->
<string name="clear_history_label">Limpar histórico</string>
<!--Other-->
@@ -59,6 +60,7 @@
<string name="auto_download_label">Incluir nas transferências automáticas</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL da fonte</string>
+ <string name="etxtFeedurlHint">URL da fonte ou sítio web</string>
<string name="txtvfeedurl_label">Adicionar podcast via URL</string>
<string name="podcastdirectories_label">Localizar podcasts no diretório</string>
<string name="podcastdirectories_descr">Pode procurar os novos podcasts no gPodder.net por nome, categoria ou popularidade.</string>
@@ -133,6 +135,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">A processar...</string>
<string name="playbackservice_notification_title">Reproduzir podcast</string>
+ <string name="unknown_media_key">Tecla multimédia desconhecida: %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">Limpar fila</string>
<string name="undo">Anular</string>
@@ -146,6 +149,7 @@
<string name="return_home_label">Voltar ao ecrã</string>
<string name="flattr_auth_success">Autenticação efetuada! Já pode fazer o flattr com a aplicação.</string>
<string name="no_flattr_token_title">Token flattr não encontrado</string>
+ <string name="no_flattr_token_notification_msg">Parece que a sua conta flattr não está integrada ao AntennaPod. Clique aqui para autenticar.</string>
<string name="no_flattr_token_msg">Parece que a sua conta flattr não está vinculada ao AntennaPod. Pode vincular a sua conta ao AntennaPod ou aceder ao sítio web para fazer o flattr.</string>
<string name="authenticate_now_label">Autenticar</string>
<string name="action_forbidden_title">Ação negada</string>
@@ -199,6 +203,7 @@
<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">Configurar flattr automático</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>
@@ -221,9 +226,15 @@
<string name="pref_gpodnet_setlogin_information_sum">Mudar informação de acesso à sua conta gpodder.net.</string>
<string name="pref_playback_speed_title">Velocidades de reprodução</string>
<string name="pref_playback_speed_sum">Personalize as velocidades de reprodução disponíveis.</string>
+ <string name="pref_seek_delta_title">Intervalo de procura</string>
+ <string name="pref_seek_delta_sum">Ao recuar ou avançar, procurar este valor de segundos</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-->
+ <string name="auto_flattr_enable">Ativar flattr automático</string>
+ <string name="auto_flattr_after_percent">Flattr de episódios ao atingir %d porcento de reprodução</string>
+ <string name="auto_flattr_ater_beginning">Flattr de episodios ao iniciar a reprodução</string>
+ <string name="auto_flattr_ater_end">Flattr de episódios ao terminar a reprodução</string>
<!--Search-->
<string name="search_hint">Procurar fontes ou episódios</string>
<string name="found_in_shownotes_label">Encontrado nas notas</string>
@@ -326,4 +337,5 @@
<string name="authentication_label">Autenticação</string>
<string name="authentication_descr">Altere o seu nome de utilizador e senha para este podcast e seus episódios.</string>
<!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Importar subscrições de aplicações single-purpose...</string>
</resources>
diff --git a/res/values-sv-rSE/strings.xml b/res/values-sv-rSE/strings.xml
index beda2187e..e17f54fa5 100644
--- a/res/values-sv-rSE/strings.xml
+++ b/res/values-sv-rSE/strings.xml
@@ -31,6 +31,7 @@
<string name="copy_url_label">Kopiera URL</string>
<string name="share_url_label">Dela URL</string>
<string name="copied_url_msg">Kopierade URL till clipboard.</string>
+ <string name="go_to_position_label">Gå hit</string>
<!--Playback history-->
<string name="clear_history_label">Rensa historik</string>
<!--Other-->
@@ -59,6 +60,7 @@
<string name="auto_download_label">Inkludera i automatiska nedladdningar</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Flödets URL</string>
+ <string name="etxtFeedurlHint">URL till flöde eller webbsida</string>
<string name="txtvfeedurl_label">Lägg till podcast via URL</string>
<string name="podcastdirectories_label">Hitta podcast i mapp</string>
<string name="podcastdirectories_descr">Du kan söka efter podcasts baserat på namn, kategori eller populäritet på tjänsten gpodder.net</string>
@@ -133,6 +135,7 @@
<string name="position_default_label">00:00:00</string>
<string name="player_buffering_msg">Buffrar</string>
<string name="playbackservice_notification_title">Spelar podcast</string>
+ <string name="unknown_media_key">AntannaPod - Okänd mediaknapp: %1$d</string>
<!--Queue operations-->
<string name="clear_queue_label">Rensa kön</string>
<string name="undo">Ångra</string>
@@ -146,6 +149,7 @@
<string name="return_home_label">Återgå till Startsidan</string>
<string name="flattr_auth_success">Autentiseringen lyckades! Du kan nu Flattra saker i appen.</string>
<string name="no_flattr_token_title">Ingen Flattr token hittades</string>
+ <string name="no_flattr_token_notification_msg">Ditt Flattr-konto verkar inte vara anslutet till AntennaPod. Tryck här för att autentisera.</string>
<string name="no_flattr_token_msg">Ditt Flattr konto verkar inte vara ansluten till AntennaPod. Du kan antingen ansluta ditt konto till AntennaPod att Flattr saker i app eller så kan du besöka webbplatsen för att Flattr det där.</string>
<string name="authenticate_now_label">Autentisera</string>
<string name="action_forbidden_title">Åtgärd förbjuden</string>
@@ -167,7 +171,7 @@
<!--Variable Speed-->
<string name="download_plugin_label">Ladda ner tillägg</string>
<string name="no_playback_plugin_title">Tillägg ej installerat</string>
- <string name="no_playback_plugin_msg">För att variabel uppspelningshastighet skall fungera måste ett tredjepartstillägg installeras.\n\nTryck på \'Ladda ner tillägg\' för att ladda ner ett gratis tilläg från Play store.\n\nAntennaPod ansvarar inte för problem med detta tillägg och de bör rapporteras till tilläggets skapare.\n</string>
+ <string name="no_playback_plugin_msg">För att variabel uppspelningshastighet skall fungera måste ett tredjepartstillägg installeras.\n\nTryck på \'Ladda ner tillägg\' för att ladda ner ett gratis tillägg från Play Store.\n\nAntennaPod ansvarar inte för problem med detta tillägg och de bör rapporteras till tilläggets skapare.</string>
<string name="set_playback_speed_label">Uppspelningshastigheter</string>
<!--Empty list labels-->
<string name="no_items_label">Det finns inget i denna lista.</string>
@@ -199,6 +203,7 @@
<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">Konfigurerar automatisk Flattring</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>
@@ -221,9 +226,15 @@
<string name="pref_gpodnet_setlogin_information_sum">Ändra inloggningsinformationen för ditt gpodder.net konto.</string>
<string name="pref_playback_speed_title">Uppspelningshastigheter</string>
<string name="pref_playback_speed_sum">Anpassa de tillgängliga hastigheterna för variabel uppspelningshastighet.</string>
+ <string name="pref_seek_delta_title">Söktid</string>
+ <string name="pref_seek_delta_sum">Sök så här många sekunder vid snabbspolning bakåt eller framåt</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-->
+ <string name="auto_flattr_enable">Aktivera automatisk Flattring</string>
+ <string name="auto_flattr_after_percent">Flattra episoden så snart %d procent har spelats</string>
+ <string name="auto_flattr_ater_beginning">Flattra episoden när den startas</string>
+ <string name="auto_flattr_ater_end">Flattra episoden när den spelats klart</string>
<!--Search-->
<string name="search_hint">Sök efter flöden eller avsnitt</string>
<string name="found_in_shownotes_label">Hittad i shownotes</string>
@@ -235,7 +246,7 @@
<string name="opml_import_txtv_button_lable">OPML-filer låter dig flytta dina podcasts från en podcatcher till en annan.</string>
<string name="opml_import_explanation">Om du vill importera en OPML-fil, måste du placera den i följande katalog och tryck på knappen nedan för att starta importen.</string>
<string name="start_import_label">Påbörja importering</string>
- <string name="opml_import_label">OPML import</string>
+ <string name="opml_import_label">Importera OPML-fil</string>
<string name="opml_directory_error">FEL! </string>
<string name="reading_opml_label">Läser OPML-fil</string>
<string name="opml_reader_error">Ett fel har skett vid iläsning av opml dokumentet:</string>
diff --git a/src/de/danoeh/antennapod/AppConfig.java b/src/de/danoeh/antennapod/AppConfig.java
index 0e12a350f..7a75e3a18 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.2";
+ public final static String USER_AGENT = "AntennaPod/0.9.9.3";
}
diff --git a/src/de/danoeh/antennapod/PodcastApp.java b/src/de/danoeh/antennapod/PodcastApp.java
index 4c4766327..74628f3d6 100644
--- a/src/de/danoeh/antennapod/PodcastApp.java
+++ b/src/de/danoeh/antennapod/PodcastApp.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod;
import android.app.Application;
import android.content.res.Configuration;
import android.util.Log;
-import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -36,13 +35,6 @@ public class PodcastApp extends Application {
SPAUtil.sendSPAppsQueryFeedsIntent(this);
}
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- Log.w(TAG, "Received onLowOnMemory warning. Cleaning image cache...");
- ImageLoader.getInstance().wipeImageCache();
- }
-
public static float getLogicalDensity() {
return LOGICAL_DENSITY;
}
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 6373ff240..50f5d8f2e 100644
--- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -31,7 +31,7 @@ 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.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.EventDistributor;
@@ -381,8 +381,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public void run() {
- ImageLoader.getInstance().loadThumbnailBitmap(media,
- butNavLeft);
+ PicassoProvider.getMediaMetadataPicassoInstance(AudioplayerActivity.this)
+ .load(media.getImageUri())
+ .into(butNavLeft);
}
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));
@@ -396,9 +397,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public void run() {
- ImageLoader.getInstance().loadThumbnailBitmap(media,
- butNavLeft);
+ PicassoProvider.getMediaMetadataPicassoInstance(AudioplayerActivity.this)
+ .load(media.getImageUri())
+ .into(butNavLeft);
}
+
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));
diff --git a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
index e89f8d05c..86b278bf0 100644
--- a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
@@ -9,11 +9,28 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.*;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+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;
+import java.util.List;
+import java.util.Map;
+
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.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
@@ -21,15 +38,6 @@ 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;
-import java.util.List;
-import java.util.Map;
/**
* Default implementation of OnlineFeedViewActivity. Shows the downloaded feed's items with their descriptions,
@@ -115,9 +123,13 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
if (feed.getImage() != null) {
- ImageDiskCache.getDefaultInstance().loadThumbnailBitmap(feed.getImage().getDownload_url(), cover, (int) getResources().getDimension(
- R.dimen.thumbnail_length));
+ int imageSize = (int) getResources().getDimension(R.dimen.thumbnail_length);
+ PicassoProvider.getDefaultPicassoInstance(this)
+ .load(feed.getImage().getDownload_url())
+ .resize(imageSize, imageSize)
+ .into(cover);
}
+
title.setText(feed.getTitle());
author.setText(feed.getAuthor());
description.setText(feed.getDescription());
diff --git a/src/de/danoeh/antennapod/activity/FeedInfoActivity.java b/src/de/danoeh/antennapod/activity/FeedInfoActivity.java
index 7f60d0b10..b46bc7546 100644
--- a/src/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/src/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -12,7 +12,7 @@ import android.view.MenuItem;
import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedPreferences;
@@ -78,8 +78,9 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
public void run() {
- ImageLoader.getInstance().loadThumbnailBitmap(
- feed.getImage(), imgvCover);
+ PicassoProvider.getDefaultPicassoInstance(FeedInfoActivity.this)
+ .load(feed.getImageUri())
+ .into(imgvCover);
}
});
diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
index a62ad3ae9..cd6731c02 100644
--- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -45,6 +45,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
private static final String TAG = "PreferenceActivity";
private static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
+ private static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
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_PREFS = "prefAutoFlattrPrefs";
@@ -332,6 +333,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
boolean hasFlattrToken = FlattrUtils.hasToken();
+ findPreference(PREF_FLATTR_SETTINGS).setEnabled(FlattrUtils.hasAPICredentials());
findPreference(PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
findPreference(PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
findPreference(PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
diff --git a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 46fa98c49..81661a288 100644
--- a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -6,12 +6,12 @@ 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.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
+import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -50,10 +50,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
setTheme(R.style.Theme_AntennaPod_Dark);
}
+ @SuppressLint("AppCompatMethod")
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= 11) {
- supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
+ requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
diff --git a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 33b11774f..641a1368d 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -9,8 +9,9 @@ import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.util.Converter;
@@ -22,10 +23,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
private final Context context;
private final ItemAccess itemAccess;
+ private final int imageSize;
+
public DownloadedEpisodesListAdapter(Context context, ItemAccess itemAccess) {
super();
this.context = context;
this.itemAccess = itemAccess;
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_downloaded_item);
}
@Override
@@ -83,12 +87,11 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setOnClickListener(secondaryActionListener);
- ImageLoader.getInstance().loadThumbnailBitmap(
- item,
- holder.imageView,
- (int) convertView.getResources().getDimension(
- R.dimen.thumbnail_length)
- );
+ PicassoProvider.getMediaMetadataPicassoInstance(context)
+ .load(item.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.imageView);
+
return convertView;
}
diff --git a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
index 5e857c131..56c3e1ca6 100644
--- a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
@@ -6,9 +6,14 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -19,276 +24,282 @@ import de.danoeh.antennapod.util.Converter;
* structure of this list is: [header] [queueItems] [header] [unreadItems].
*/
public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
- private static final String TAG = "ExternalEpisodesListAdapter";
+ private static final String TAG = "ExternalEpisodesListAdapter";
- public static final int GROUP_POS_QUEUE = 0;
- public static final int GROUP_POS_UNREAD = 1;
+ public static final int GROUP_POS_QUEUE = 0;
+ public static final int GROUP_POS_UNREAD = 1;
- private Context context;
+ private Context context;
private ItemAccess itemAccess;
- private ActionButtonCallback feedItemActionCallback;
- private OnGroupActionClicked groupActionCallback;
+ private ActionButtonCallback feedItemActionCallback;
+ private OnGroupActionClicked groupActionCallback;
+
+ private final int imageSize;
- public ExternalEpisodesListAdapter(Context context,
- ActionButtonCallback callback,
- OnGroupActionClicked groupActionCallback,
- ItemAccess itemAccess) {
- super();
- this.context = context;
+ public ExternalEpisodesListAdapter(Context context,
+ ActionButtonCallback callback,
+ OnGroupActionClicked groupActionCallback,
+ ItemAccess itemAccess) {
+ super();
+ this.context = context;
this.itemAccess = itemAccess;
- this.feedItemActionCallback = callback;
- this.groupActionCallback = groupActionCallback;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public FeedItem getChild(int groupPosition, int childPosition) {
- if (groupPosition == GROUP_POS_QUEUE) {
- return itemAccess.getQueueItemAt(childPosition);
- } else if (groupPosition == GROUP_POS_UNREAD) {
+ this.feedItemActionCallback = callback;
+ this.groupActionCallback = groupActionCallback;
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public FeedItem getChild(int groupPosition, int childPosition) {
+ if (groupPosition == GROUP_POS_QUEUE) {
+ return itemAccess.getQueueItemAt(childPosition);
+ } else if (groupPosition == GROUP_POS_UNREAD) {
return itemAccess.getUnreadItemAt(childPosition);
- }
- return null;
- }
-
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- @Override
- public View getChildView(int groupPosition, final int childPosition,
- boolean isLastChild, View convertView, ViewGroup parent) {
- Holder holder;
- final FeedItem item = getChild(groupPosition, childPosition);
-
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.external_itemlist_item,
- parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.feedTitle = (TextView) convertView
- .findViewById(R.id.txtvFeedname);
- holder.lenSize = (TextView) convertView
- .findViewById(R.id.txtvLenSize);
- holder.downloadStatus = (ImageView) convertView
- .findViewById(R.id.imgvDownloadStatus);
- holder.feedImage = (ImageView) convertView
- .findViewById(R.id.imgvFeedimage);
- holder.butAction = (ImageButton) convertView
- .findViewById(R.id.butAction);
- holder.statusPlaying = (View) convertView
- .findViewById(R.id.statusPlaying);
- holder.episodeProgress = (ProgressBar) convertView
- .findViewById(R.id.pbar_episode_progress);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
- holder.title.setText(item.getTitle());
- holder.feedTitle.setText(item.getFeed().getTitle());
- FeedItem.State state = item.getState();
-
- if (groupPosition == GROUP_POS_QUEUE) {
- switch (state) {
- case PLAYING:
- holder.statusPlaying.setVisibility(View.VISIBLE);
- holder.episodeProgress.setVisibility(View.VISIBLE);
- break;
- case IN_PROGRESS:
- holder.statusPlaying.setVisibility(View.GONE);
- holder.episodeProgress.setVisibility(View.VISIBLE);
- break;
- case NEW:
- holder.statusPlaying.setVisibility(View.GONE);
- holder.episodeProgress.setVisibility(View.GONE);
- break;
- default:
- holder.statusPlaying.setVisibility(View.GONE);
- holder.episodeProgress.setVisibility(View.GONE);
- break;
- }
- } else {
- holder.statusPlaying.setVisibility(View.GONE);
- holder.episodeProgress.setVisibility(View.GONE);
- }
-
- FeedMedia media = item.getMedia();
- if (media != null) {
-
- if (state == FeedItem.State.PLAYING
- || state == FeedItem.State.IN_PROGRESS) {
- if (media.getDuration() > 0) {
- holder.episodeProgress.setProgress((int) (((double) media
- .getPosition()) / media.getDuration() * 100));
- holder.lenSize.setText(Converter
- .getDurationStringLong(media.getDuration()
- - media.getPosition()));
- }
- } else if (!media.isDownloaded()) {
- holder.lenSize.setText(context.getString(R.string.size_prefix)
- + Converter.byteToString(media.getSize()));
- } else {
- holder.lenSize.setText(context
- .getString(R.string.length_prefix)
- + Converter.getDurationStringLong(media.getDuration()));
- }
-
- TypedArray drawables = context.obtainStyledAttributes(new int[] {
- R.attr.av_download, R.attr.navigation_refresh });
- final int[] labels = new int[] {R.string.status_downloaded_label, R.string.downloading_label};
- holder.lenSize.setVisibility(View.VISIBLE);
- if (!media.isDownloaded()) {
- if (DownloadRequester.getInstance().isDownloadingFile(media)) {
- holder.downloadStatus.setVisibility(View.VISIBLE);
- holder.downloadStatus.setImageDrawable(drawables
- .getDrawable(1));
+ }
+ return null;
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, final int childPosition,
+ boolean isLastChild, View convertView, ViewGroup parent) {
+ Holder holder;
+ final FeedItem item = getChild(groupPosition, childPosition);
+
+ if (convertView == null) {
+ holder = new Holder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.external_itemlist_item,
+ parent, false);
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.feedTitle = (TextView) convertView
+ .findViewById(R.id.txtvFeedname);
+ holder.lenSize = (TextView) convertView
+ .findViewById(R.id.txtvLenSize);
+ holder.downloadStatus = (ImageView) convertView
+ .findViewById(R.id.imgvDownloadStatus);
+ holder.feedImage = (ImageView) convertView
+ .findViewById(R.id.imgvFeedimage);
+ holder.butAction = (ImageButton) convertView
+ .findViewById(R.id.butAction);
+ holder.statusPlaying = (View) convertView
+ .findViewById(R.id.statusPlaying);
+ holder.episodeProgress = (ProgressBar) convertView
+ .findViewById(R.id.pbar_episode_progress);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.title.setText(item.getTitle());
+ holder.feedTitle.setText(item.getFeed().getTitle());
+ FeedItem.State state = item.getState();
+
+ if (groupPosition == GROUP_POS_QUEUE) {
+ switch (state) {
+ case PLAYING:
+ holder.statusPlaying.setVisibility(View.VISIBLE);
+ holder.episodeProgress.setVisibility(View.VISIBLE);
+ break;
+ case IN_PROGRESS:
+ holder.statusPlaying.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.VISIBLE);
+ break;
+ case NEW:
+ holder.statusPlaying.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.GONE);
+ break;
+ default:
+ holder.statusPlaying.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.GONE);
+ break;
+ }
+ } else {
+ holder.statusPlaying.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.GONE);
+ }
+
+ FeedMedia media = item.getMedia();
+ if (media != null) {
+
+ if (state == FeedItem.State.PLAYING
+ || state == FeedItem.State.IN_PROGRESS) {
+ if (media.getDuration() > 0) {
+ holder.episodeProgress.setProgress((int) (((double) media
+ .getPosition()) / media.getDuration() * 100));
+ holder.lenSize.setText(Converter
+ .getDurationStringLong(media.getDuration()
+ - media.getPosition()));
+ }
+ } else if (!media.isDownloaded()) {
+ holder.lenSize.setText(context.getString(R.string.size_prefix)
+ + Converter.byteToString(media.getSize()));
+ } else {
+ holder.lenSize.setText(context
+ .getString(R.string.length_prefix)
+ + Converter.getDurationStringLong(media.getDuration()));
+ }
+
+ TypedArray drawables = context.obtainStyledAttributes(new int[]{
+ R.attr.av_download, R.attr.navigation_refresh});
+ final int[] labels = new int[]{R.string.status_downloaded_label, R.string.downloading_label};
+ holder.lenSize.setVisibility(View.VISIBLE);
+ if (!media.isDownloaded()) {
+ if (DownloadRequester.getInstance().isDownloadingFile(media)) {
+ holder.downloadStatus.setVisibility(View.VISIBLE);
+ holder.downloadStatus.setImageDrawable(drawables
+ .getDrawable(1));
holder.downloadStatus.setContentDescription(context.getString(labels[1]));
- } else {
- holder.downloadStatus.setVisibility(View.INVISIBLE);
- }
- } else {
- holder.downloadStatus.setVisibility(View.VISIBLE);
- holder.downloadStatus
- .setImageDrawable(drawables.getDrawable(0));
+ } else {
+ holder.downloadStatus.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ holder.downloadStatus.setVisibility(View.VISIBLE);
+ holder.downloadStatus
+ .setImageDrawable(drawables.getDrawable(0));
holder.downloadStatus.setContentDescription(context.getString(labels[0]));
- }
- } else {
- holder.downloadStatus.setVisibility(View.INVISIBLE);
- holder.lenSize.setVisibility(View.INVISIBLE);
- }
-
- ImageLoader.getInstance().loadThumbnailBitmap(
- item,
- holder.feedImage,
- (int) convertView.getResources().getDimension(
- R.dimen.thumbnail_length));
- holder.butAction.setFocusable(false);
- holder.butAction.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- feedItemActionCallback.onActionButtonPressed(item);
- }
- });
-
- return convertView;
-
- }
-
- static class Holder {
- TextView title;
- TextView feedTitle;
- TextView lenSize;
- ImageView downloadStatus;
- ImageView feedImage;
- ImageButton butAction;
- View statusPlaying;
- ProgressBar episodeProgress;
- }
-
- @Override
- public int getChildrenCount(int groupPosition) {
- if (groupPosition == GROUP_POS_QUEUE) {
- return itemAccess.getQueueSize();
- } else if (groupPosition == GROUP_POS_UNREAD) {
- return itemAccess.getUnreadItemsSize();
- }
- return 0;
- }
-
- @Override
- public int getGroupCount() {
- // Hide 'unread items' group if empty
- if (itemAccess.getUnreadItemsSize() > 0) {
- return 2;
- } else {
- return 1;
- }
- }
-
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- @Override
- public View getGroupView(final int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
- TextView headerTitle = (TextView) convertView
- .findViewById(0);
- ImageButton actionButton = (ImageButton) convertView
- .findViewById(R.id.butAction);
- TextView numItems = (TextView) convertView.findViewById(0);
-
- String headerString = null;
- int childrenCount = 0;
-
- if (groupPosition == 0) {
- headerString = context.getString(R.string.queue_label);
- childrenCount = getChildrenCount(GROUP_POS_QUEUE);
- } else {
- headerString = context.getString(R.string.waiting_list_label);
- childrenCount = getChildrenCount(GROUP_POS_UNREAD);
- }
- headerTitle.setText(headerString);
- if (childrenCount <= 0) {
- numItems.setVisibility(View.INVISIBLE);
- } else {
- numItems.setVisibility(View.VISIBLE);
- numItems.setText(Integer.toString(childrenCount));
- }
- actionButton.setFocusable(false);
- actionButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- groupActionCallback.onClick(getGroupId(groupPosition));
- }
- });
- return convertView;
- }
-
- @Override
- public boolean isEmpty() {
- return itemAccess.getUnreadItemsSize() == 0
- && itemAccess.getQueueSize() == 0;
- }
-
- @Override
- public Object getGroup(int groupPosition) {
- return null;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- public interface OnGroupActionClicked {
- public void onClick(long groupId);
- }
+ }
+ } else {
+ holder.downloadStatus.setVisibility(View.INVISIBLE);
+ holder.lenSize.setVisibility(View.INVISIBLE);
+ }
+
+ PicassoProvider.getMediaMetadataPicassoInstance(context)
+ .load(item.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.feedImage);
+
+ holder.butAction.setFocusable(false);
+ holder.butAction.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ feedItemActionCallback.onActionButtonPressed(item);
+ }
+ });
+
+ return convertView;
+
+ }
+
+ static class Holder {
+ TextView title;
+ TextView feedTitle;
+ TextView lenSize;
+ ImageView downloadStatus;
+ ImageView feedImage;
+ ImageButton butAction;
+ View statusPlaying;
+ ProgressBar episodeProgress;
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ if (groupPosition == GROUP_POS_QUEUE) {
+ return itemAccess.getQueueSize();
+ } else if (groupPosition == GROUP_POS_UNREAD) {
+ return itemAccess.getUnreadItemsSize();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getGroupCount() {
+ // Hide 'unread items' group if empty
+ if (itemAccess.getUnreadItemsSize() > 0) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ @Override
+ public View getGroupView(final int groupPosition, boolean isExpanded,
+ View convertView, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
+ TextView headerTitle = (TextView) convertView
+ .findViewById(0);
+ ImageButton actionButton = (ImageButton) convertView
+ .findViewById(R.id.butAction);
+ TextView numItems = (TextView) convertView.findViewById(0);
+
+ String headerString = null;
+ int childrenCount = 0;
+
+ if (groupPosition == 0) {
+ headerString = context.getString(R.string.queue_label);
+ childrenCount = getChildrenCount(GROUP_POS_QUEUE);
+ } else {
+ headerString = context.getString(R.string.waiting_list_label);
+ childrenCount = getChildrenCount(GROUP_POS_UNREAD);
+ }
+ headerTitle.setText(headerString);
+ if (childrenCount <= 0) {
+ numItems.setVisibility(View.INVISIBLE);
+ } else {
+ numItems.setVisibility(View.VISIBLE);
+ numItems.setText(Integer.toString(childrenCount));
+ }
+ actionButton.setFocusable(false);
+ actionButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ groupActionCallback.onClick(getGroupId(groupPosition));
+ }
+ });
+ return convertView;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return itemAccess.getUnreadItemsSize() == 0
+ && itemAccess.getQueueSize() == 0;
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return null;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ public interface OnGroupActionClicked {
+ public void onClick(long groupId);
+ }
public static interface ItemAccess {
public int getQueueSize();
+
public int getUnreadItemsSize();
+
public FeedItem getQueueItemAt(int position);
+
public FeedItem getUnreadItemAt(int position);
}
diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
index 9676372fb..ed85c8836 100644
--- a/src/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -11,7 +11,7 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Feed;
/**
@@ -32,6 +32,8 @@ public class NavListAdapter extends BaseAdapter {
private ItemAccess itemAccess;
private Context context;
+ private final int imageSize;
+
public NavListAdapter(ItemAccess itemAccess, Context context) {
this.itemAccess = itemAccess;
this.context = context;
@@ -41,6 +43,7 @@ public class NavListAdapter extends BaseAdapter {
drawables = new Drawable[]{ta.getDrawable(0), ta.getDrawable(1), ta.getDrawable(2),
ta.getDrawable(3), ta.getDrawable(4)};
ta.recycle();
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist);
}
@Override
@@ -189,7 +192,11 @@ public class NavListAdapter extends BaseAdapter {
}
holder.title.setText(feed.getTitle());
- ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), holder.image, (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist));
+
+ PicassoProvider.getDefaultPicassoInstance(context)
+ .load(feed.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.image);
return convertView;
}
diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
index 07fd3e6b1..4370de14d 100644
--- a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
@@ -5,9 +5,14 @@ import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.BaseAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -22,6 +27,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
+ private final int imageSize;
public NewEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
super();
@@ -29,6 +35,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
this.actionButtonCallback = actionButtonCallback;
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_itemlist);
}
@Override
@@ -124,13 +131,11 @@ public class NewEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
+ PicassoProvider.getMediaMetadataPicassoInstance(context)
+ .load(item.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.imageView);
- ImageLoader.getInstance().loadThumbnailBitmap(
- item,
- holder.imageView,
- (int) convertView.getResources().getDimension(
- R.dimen.thumbnail_length)
- );
return convertView;
}
diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
index f671ba5c6..c670089b9 100644
--- a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -6,7 +6,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -22,12 +22,15 @@ public class QueueListAdapter extends BaseAdapter {
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
+ private final int imageSize;
+
public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
super();
this.context = context;
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
this.actionButtonCallback = actionButtonCallback;
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_queue_item);
}
@@ -92,13 +95,11 @@ public class QueueListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
+ PicassoProvider.getMediaMetadataPicassoInstance(context)
+ .load(item.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.imageView);
- ImageLoader.getInstance().loadThumbnailBitmap(
- item,
- holder.imageView,
- (int) convertView.getResources().getDimension(
- R.dimen.thumbnail_length)
- );
return convertView;
}
diff --git a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index ecfbb4660..6b1fefaad 100644
--- a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -4,28 +4,31 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedComponent;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.SearchResult;
-import java.util.List;
-
-/** List adapter for search activity. */
+/**
+ * List adapter for search activity.
+ */
public class SearchlistAdapter extends BaseAdapter {
- private final Context context;
+ private final Context context;
private final ItemAccess itemAccess;
+ private final int imageSize;
+
public SearchlistAdapter(Context context, ItemAccess itemAccess) {
this.context = context;
this.itemAccess = itemAccess;
+ this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
}
@Override
@@ -44,61 +47,65 @@ public class SearchlistAdapter extends BaseAdapter {
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final Holder holder;
- SearchResult result = getItem(position);
- FeedComponent component = result.getComponent();
-
- // Inflate Layout
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- 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);
- holder.subtitle = (TextView) convertView
- .findViewById(R.id.txtvSubtitle);
-
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
- if (component.getClass() == Feed.class) {
- final Feed feed = (Feed) component;
- holder.title.setText(feed.getTitle());
- holder.subtitle.setVisibility(View.GONE);
- ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(),
- holder.cover, (int) convertView.getResources().getDimension(R.dimen.thumbnail_length));
- } else if (component.getClass() == FeedItem.class) {
- final FeedItem item = (FeedItem) component;
- holder.title.setText(item.getTitle());
- if (result.getSubtitle() != null) {
- holder.subtitle.setVisibility(View.VISIBLE);
- holder.subtitle.setText(result.getSubtitle());
- }
-
- ImageLoader.getInstance().loadThumbnailBitmap(
- item.getFeed().getImage(),
- holder.cover,
- (int) convertView.getResources().getDimension(
- R.dimen.thumbnail_length));
-
- }
-
- return convertView;
- }
-
- static class Holder {
- ImageView cover;
- TextView title;
- TextView subtitle;
- }
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final Holder holder;
+ SearchResult result = getItem(position);
+ FeedComponent component = result.getComponent();
+
+ // Inflate Layout
+ if (convertView == null) {
+ holder = new Holder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ 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);
+ holder.subtitle = (TextView) convertView
+ .findViewById(R.id.txtvSubtitle);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+ if (component.getClass() == Feed.class) {
+ final Feed feed = (Feed) component;
+ holder.title.setText(feed.getTitle());
+ holder.subtitle.setVisibility(View.GONE);
+
+ PicassoProvider.getDefaultPicassoInstance(context)
+ .load(feed.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.cover);
+
+ } else if (component.getClass() == FeedItem.class) {
+ final FeedItem item = (FeedItem) component;
+ holder.title.setText(item.getTitle());
+ if (result.getSubtitle() != null) {
+ holder.subtitle.setVisibility(View.VISIBLE);
+ holder.subtitle.setText(result.getSubtitle());
+ }
+
+ PicassoProvider.getDefaultPicassoInstance(context)
+ .load(item.getFeed().getImageUri())
+ .resize(imageSize, imageSize)
+ .into(holder.cover);
+
+ }
+
+ return convertView;
+ }
+
+ static class Holder {
+ ImageView cover;
+ TextView title;
+ TextView subtitle;
+ }
public static interface ItemAccess {
int getCount();
+
SearchResult getItem(int position);
}
diff --git a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index f20232a6f..dcad2d524 100644
--- a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -7,22 +7,21 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageDiskCache;
-import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
+
/**
* Adapter for displaying a list of GPodnetPodcast-Objects.
*/
public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
- private final ImageDiskCache diskCache;
private final int thumbnailLength;
public PodcastListAdapter(Context context, int resource, List<GpodnetPodcast> objects) {
super(context, resource, objects);
- diskCache = ImageDiskCache.getDefaultInstance();
thumbnailLength = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
}
@@ -50,7 +49,11 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
holder.title.setText(podcast.getTitle());
holder.description.setText(podcast.getDescription());
- diskCache.loadThumbnailBitmap(podcast.getLogoUrl(), holder.image, thumbnailLength);
+
+ PicassoProvider.getDefaultPicassoInstance(convertView.getContext())
+ .load(podcast.getLogoUrl())
+ .resize(thumbnailLength, thumbnailLength)
+ .into(holder.image);
return convertView;
}
diff --git a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java b/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java
deleted file mode 100644
index 43118c3af..000000000
--- a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.os.Handler;
-import android.util.Log;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader.ImageWorkerTaskResource;
-import de.danoeh.antennapod.util.BitmapDecoder;
-
-public class BitmapDecodeWorkerTask extends Thread {
-
- protected int PREFERRED_LENGTH;
- public static final int FADE_DURATION = 500;
-
- /**
- * Can be thumbnail or cover
- */
- protected int imageType;
-
- private static final String TAG = "BitmapDecodeWorkerTask";
- private ImageView target;
- protected CachedBitmap cBitmap;
-
- protected ImageLoader.ImageWorkerTaskResource imageResource;
-
- private Handler handler;
-
- private final int defaultCoverResource;
-
- public BitmapDecodeWorkerTask(Handler handler, ImageView target,
- ImageWorkerTaskResource imageResource, int length, int imageType) {
- super();
- this.handler = handler;
- this.target = target;
- this.imageResource = imageResource;
- this.PREFERRED_LENGTH = length;
- this.imageType = imageType;
- this.defaultCoverResource = android.R.color.transparent;
- }
-
- /**
- * Should return true if tag of the imageview is still the same it was
- * before the bitmap was decoded
- */
- protected boolean tagsMatching(ImageView target) {
- Object tag = target.getTag(R.id.imageloader_key);
- return tag != null && tag.equals(imageResource.getImageLoaderCacheKey());
- }
-
- protected void onPostExecute() {
- // check if imageview is still supposed to display this image
- if (tagsMatching(target) && cBitmap.getBitmap() != null) {
- Drawable[] drawables = new Drawable[]{
- PodcastApp.getInstance().getResources().getDrawable(android.R.color.transparent),
- new BitmapDrawable(PodcastApp.getInstance().getResources(), cBitmap.getBitmap())
- };
- TransitionDrawable transitionDrawable = new TransitionDrawable(drawables);
- target.setImageDrawable(transitionDrawable);
- transitionDrawable.startTransition(FADE_DURATION);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Not displaying image");
- }
- }
-
- @Override
- public void run() {
- cBitmap = new CachedBitmap(BitmapDecoder.decodeBitmapFromWorkerTaskResource(
- PREFERRED_LENGTH, imageResource), PREFERRED_LENGTH);
- if (cBitmap.getBitmap() != null) {
- storeBitmapInCache(cBitmap);
- } else {
- Log.w(TAG, "Could not load bitmap. Using default image.");
- cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
- target.getResources(), defaultCoverResource),
- PREFERRED_LENGTH);
- }
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Finished loading bitmaps");
-
- endBackgroundTask();
- }
-
- protected final void endBackgroundTask() {
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- onPostExecute();
- }
-
- });
- }
-
- protected void onInvalidStream() {
- cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
- target.getResources(), defaultCoverResource), PREFERRED_LENGTH);
- }
-
- protected void storeBitmapInCache(CachedBitmap cb) {
- ImageLoader loader = ImageLoader.getInstance();
- if (imageType == ImageLoader.IMAGE_TYPE_COVER) {
- loader.addBitmapToCoverCache(imageResource.getImageLoaderCacheKey(), cb);
- } else if (imageType == ImageLoader.IMAGE_TYPE_THUMBNAIL) {
- loader.addBitmapToThumbnailCache(imageResource.getImageLoaderCacheKey(), cb);
- }
- }
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java b/src/de/danoeh/antennapod/asynctask/CachedBitmap.java
deleted file mode 100644
index 5a89b7b53..000000000
--- a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.graphics.Bitmap;
-
-/** Stores a bitmap and the length it was decoded with. */
-public class CachedBitmap {
-
- private Bitmap bitmap;
- private int length;
-
- public CachedBitmap(Bitmap bitmap, int length) {
- super();
- this.bitmap = bitmap;
- this.length = length;
- }
-
- public Bitmap getBitmap() {
- return bitmap;
- }
- public int getLength() {
- return length;
- }
-
-
-
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
deleted file mode 100644
index 77609f28b..000000000
--- a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
+++ /dev/null
@@ -1,397 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.os.Handler;
-import android.util.Log;
-import android.util.Pair;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-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;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Provides local cache for storing downloaded image. An image disk cache downloads images and stores them as long
- * as the cache is not full. Once the cache is full, the image disk cache will delete older images.
- */
-public class ImageDiskCache {
- private static final String TAG = "ImageDiskCache";
-
- private static HashMap<String, ImageDiskCache> cacheSingletons = new HashMap<String, ImageDiskCache>();
-
- /**
- * Return a default instance of an ImageDiskCache. This cache will store data in the external cache folder.
- */
- public static synchronized ImageDiskCache getDefaultInstance() {
- final String DEFAULT_PATH = "imagecache";
- final long DEFAULT_MAX_CACHE_SIZE = 10 * 1024 * 1024;
-
- File cacheDir = PodcastApp.getInstance().getExternalCacheDir();
- if (cacheDir == null) {
- return null;
- }
- return getInstance(new File(cacheDir, DEFAULT_PATH).getAbsolutePath(), DEFAULT_MAX_CACHE_SIZE);
- }
-
- /**
- * Return an instance of an ImageDiskCache that stores images in the specified folder.
- */
- public static synchronized ImageDiskCache getInstance(String path, long maxCacheSize) {
- Validate.notNull(path);
-
- if (cacheSingletons.containsKey(path)) {
- return cacheSingletons.get(path);
- }
-
- ImageDiskCache cache = cacheSingletons.get(path);
- if (cache == null) {
- cache = new ImageDiskCache(path, maxCacheSize);
- cacheSingletons.put(new File(path).getAbsolutePath(), cache);
- }
- cacheSingletons.put(path, cache);
- return cache;
- }
-
- /**
- * Filename - cache object mapping
- */
- private static final String CACHE_FILE_NAME = "cachefile";
- private ExecutorService executor;
- private ConcurrentHashMap<String, DiskCacheObject> diskCache;
- private final long maxCacheSize;
- private int cacheSize;
- private final File cacheFolder;
- private Handler handler;
-
- private ImageDiskCache(String path, long maxCacheSize) {
- this.maxCacheSize = maxCacheSize;
- this.cacheFolder = new File(path);
- if (!cacheFolder.exists() && !cacheFolder.mkdir()) {
- throw new IllegalArgumentException("Image disk cache could not create cache folder in: " + path);
- }
-
- executor = Executors.newFixedThreadPool(Runtime.getRuntime()
- .availableProcessors());
- handler = new Handler();
- }
-
- private synchronized void initCacheFolder() {
- if (diskCache == null) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Initializing cache folder");
- File cacheFile = new File(cacheFolder, CACHE_FILE_NAME);
- if (cacheFile.exists()) {
- try {
- InputStream in = new FileInputStream(cacheFile);
- BufferedInputStream buffer = new BufferedInputStream(in);
- ObjectInputStream objectInput = new ObjectInputStream(buffer);
- diskCache = (ConcurrentHashMap<String, DiskCacheObject>) objectInput.readObject();
- // calculate cache size
- for (DiskCacheObject dco : diskCache.values()) {
- cacheSize += dco.size;
- }
- deleteInvalidFiles();
- } catch (IOException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- } catch (ClassCastException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- }
- } else {
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- }
- }
- }
-
- private List<File> getCacheFileList() {
- Collection<DiskCacheObject> values = diskCache.values();
- List<File> files = new ArrayList<File>();
- for (DiskCacheObject dco : values) {
- files.add(dco.getFile());
- }
- files.add(new File(cacheFolder, CACHE_FILE_NAME));
- return files;
- }
-
- private Pair<String, DiskCacheObject> getOldestCacheObject() {
- Collection<String> keys = diskCache.keySet();
- DiskCacheObject oldest = null;
- String oldestKey = null;
-
- for (String key : keys) {
-
- if (oldestKey == null) {
- oldestKey = key;
- oldest = diskCache.get(key);
- } else {
- DiskCacheObject dco = diskCache.get(key);
- if (oldest.timestamp > dco.timestamp) {
- oldestKey = key;
- oldest = diskCache.get(key);
- }
- }
- }
- return new Pair<String, DiskCacheObject>(oldestKey, oldest);
- }
-
- private synchronized void deleteCacheObject(String key, DiskCacheObject value) {
- Log.i(TAG, "Deleting cached object: " + key);
- diskCache.remove(key);
- boolean result = value.getFile().delete();
- if (!result) {
- Log.w(TAG, "Could not delete file " + value.fileUrl);
- }
- cacheSize -= value.size;
- }
-
- private synchronized void deleteInvalidFiles() {
- // delete files that are not stored inside the cache
- File[] files = cacheFolder.listFiles();
- List<File> cacheFiles = getCacheFileList();
- for (File file : files) {
- if (!cacheFiles.contains(file)) {
- Log.i(TAG, "Deleting unused file: " + file.getAbsolutePath());
- boolean result = file.delete();
- if (!result) {
- Log.w(TAG, "Could not delete file: " + file.getAbsolutePath());
- }
- }
- }
- }
-
- private synchronized void cleanup() {
- if (cacheSize > maxCacheSize) {
- while (cacheSize > maxCacheSize) {
- Pair<String, DiskCacheObject> oldest = getOldestCacheObject();
- deleteCacheObject(oldest.first, oldest.second);
- }
- }
- }
-
- /**
- * Loads a new image from the disk cache. If the image that the url points to has already been downloaded, the image will
- * be loaded from the disk. Otherwise, the image will be downloaded first.
- * The image will be stored in the thumbnail cache.
- */
- public void loadThumbnailBitmap(final String url, final ImageView target, final int length) {
- if (url == null) {
- Log.w(TAG, "loadThumbnailBitmap: Call was ignored because url = null");
- return;
- }
- final ImageLoader il = ImageLoader.getInstance();
- target.setTag(R.id.image_disk_cache_key, url);
- if (diskCache != null) {
- DiskCacheObject dco = getFromCacheIfAvailable(url);
- if (dco != null) {
- il.loadThumbnailBitmap(dco.loadImage(), target, length);
- return;
- }
- }
- target.setImageResource(android.R.color.transparent);
- executor.submit(new ImageDownloader(url) {
- @Override
- protected void onImageLoaded(DiskCacheObject diskCacheObject) {
- final Object tag = target.getTag(R.id.image_disk_cache_key);
- if (tag != null && StringUtils.equals((String) tag, url)) {
- il.loadThumbnailBitmap(diskCacheObject.loadImage(), target, length);
- }
- }
- });
-
- }
-
- /**
- * Loads a new image from the disk cache. If the image that the url points to has already been downloaded, the image will
- * be loaded from the disk. Otherwise, the image will be downloaded first.
- * The image will be stored in the cover cache.
- */
- public void loadCoverBitmap(final String url, final ImageView target, final int length) {
- if (url == null) {
- Log.w(TAG, "loadCoverBitmap: Call was ignored because url = null");
- return;
- }
- final ImageLoader il = ImageLoader.getInstance();
- target.setTag(R.id.image_disk_cache_key, url);
- if (diskCache != null) {
- DiskCacheObject dco = getFromCacheIfAvailable(url);
- if (dco != null) {
- il.loadThumbnailBitmap(dco.loadImage(), target, length);
- return;
- }
- }
- target.setImageResource(android.R.color.transparent);
- executor.submit(new ImageDownloader(url) {
- @Override
- protected void onImageLoaded(DiskCacheObject diskCacheObject) {
- final Object tag = target.getTag(R.id.image_disk_cache_key);
- if (tag != null && StringUtils.equals((String) tag, url)) {
- il.loadCoverBitmap(diskCacheObject.loadImage(), target, length);
- }
- }
- });
- }
-
- private synchronized void addToDiskCache(String url, DiskCacheObject obj) {
- if (diskCache == null) {
- initCacheFolder();
- }
- if (BuildConfig.DEBUG) Log.d(TAG, "Adding new image to disk cache: " + url);
- diskCache.put(url, obj);
- cacheSize += obj.size;
- if (cacheSize > maxCacheSize) {
- cleanup();
- }
- saveCacheInfoFile();
- }
-
- private synchronized void saveCacheInfoFile() {
- OutputStream out = null;
- try {
- out = new BufferedOutputStream(new FileOutputStream(new File(cacheFolder, CACHE_FILE_NAME)));
- ObjectOutputStream objOut = new ObjectOutputStream(out);
- objOut.writeObject(diskCache);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- IOUtils.closeQuietly(out);
- }
- }
-
- private synchronized DiskCacheObject getFromCacheIfAvailable(String key) {
- if (diskCache == null) {
- initCacheFolder();
- }
- DiskCacheObject dco = diskCache.get(key);
- if (dco != null) {
- dco.timestamp = System.currentTimeMillis();
- }
- return dco;
- }
-
- ConcurrentHashMap<String, File> runningDownloads = new ConcurrentHashMap<String, File>();
-
- private abstract class ImageDownloader implements Runnable {
- private String downloadUrl;
-
- public ImageDownloader(String downloadUrl) {
- this.downloadUrl = downloadUrl;
- }
-
- protected abstract void onImageLoaded(DiskCacheObject diskCacheObject);
-
- public void run() {
- DiskCacheObject tmp = getFromCacheIfAvailable(downloadUrl);
- if (tmp != null) {
- onImageLoaded(tmp);
- return;
- }
-
- DiskCacheObject dco = null;
- File newFile = new File(cacheFolder, Integer.toString(downloadUrl.hashCode()));
- synchronized (ImageDiskCache.this) {
- if (runningDownloads.containsKey(newFile.getAbsolutePath())) {
- Log.d(TAG, "Download is already running: " + newFile.getAbsolutePath());
- return;
- } else {
- runningDownloads.put(newFile.getAbsolutePath(), newFile);
- }
- }
- if (newFile.exists()) {
- newFile.delete();
- }
-
- HttpDownloader result = downloadFile(newFile.getAbsolutePath(), downloadUrl);
- if (result.getResult().isSuccessful()) {
- long size = result.getDownloadRequest().getSoFar();
-
- dco = new DiskCacheObject(newFile.getAbsolutePath(), size);
- addToDiskCache(downloadUrl, dco);
- if (BuildConfig.DEBUG) Log.d(TAG, "Image was downloaded");
- } else {
- Log.w(TAG, "Download of url " + downloadUrl + " failed. Reason: " + result.getResult().getReasonDetailed() + "(" + result.getResult().getReason() + ")");
- }
-
- if (dco != null) {
- final DiskCacheObject dcoRef = dco;
- handler.post(new Runnable() {
- @Override
- public void run() {
- onImageLoaded(dcoRef);
- }
- });
-
- }
- runningDownloads.remove(newFile.getAbsolutePath());
-
- }
-
- private HttpDownloader downloadFile(String destination, String source) {
- DownloadRequest request = new DownloadRequest(destination, source, "", 0, 0);
- HttpDownloader downloader = new HttpDownloader(request);
- downloader.call();
- return downloader;
- }
- }
-
- private static class DiskCacheObject implements Serializable {
- private final String fileUrl;
-
- /**
- * Last usage of this image cache object.
- */
- private long timestamp;
- private final long size;
-
- public DiskCacheObject(String fileUrl, long size) {
- Validate.notNull(fileUrl);
- this.fileUrl = fileUrl;
- this.timestamp = System.currentTimeMillis();
- this.size = size;
- }
-
- public File getFile() {
- return new File(fileUrl);
- }
-
- public ImageLoader.ImageWorkerTaskResource loadImage() {
- return new ImageLoader.ImageWorkerTaskResource() {
-
- @Override
- public InputStream openImageInputStream() {
- try {
- return new FileInputStream(getFile());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- IOUtils.closeQuietly(input);
- return openImageInputStream();
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return fileUrl;
- }
- };
- }
- }
-}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageLoader.java b/src/de/danoeh/antennapod/asynctask/ImageLoader.java
deleted file mode 100644
index 6c60b7b1f..000000000
--- a/src/de/danoeh/antennapod/asynctask/ImageLoader.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.os.Handler;
-import android.support.v4.util.LruCache;
-import android.util.Log;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-
-import java.io.InputStream;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * Caches and loads FeedImage bitmaps in the background
- */
-public class ImageLoader {
- private static final String TAG = "ImageLoader";
- private static ImageLoader singleton;
-
- public static final int IMAGE_TYPE_THUMBNAIL = 0;
- public static final int IMAGE_TYPE_COVER = 1;
-
- /**
- * Used by loadThumbnailBitmap and loadCoverBitmap to denote an ImageView that displays the default image resource.
- * This is the case if the given source to load the image from was null or did not return any image data.
- */
- private static final Object DEFAULT_IMAGE_RESOURCE_TAG = new Object();
-
- private Handler handler;
- private ExecutorService executor;
-
- /**
- * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of
- * the FeedImage the bitmap belongs to.
- */
-
- final int memClass = ((ActivityManager) PodcastApp.getInstance()
- .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
-
- // Use 1/8th of the available memory for this memory cache.
- final int thumbnailCacheSize = 1024 * 1024 * memClass / 8;
-
- private LruCache<String, CachedBitmap> coverCache;
- private LruCache<String, CachedBitmap> thumbnailCache;
-
- private ImageLoader() {
- handler = new Handler();
- executor = createExecutor();
-
- coverCache = new LruCache<String, CachedBitmap>(1);
-
- thumbnailCache = new LruCache<String, CachedBitmap>(thumbnailCacheSize) {
-
- @SuppressLint("NewApi")
- @Override
- protected int sizeOf(String key, CachedBitmap value) {
- if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12)
- return value.getBitmap().getByteCount();
- else
- return (value.getBitmap().getRowBytes() * value.getBitmap()
- .getHeight());
-
- }
-
- };
- }
-
- private ExecutorService createExecutor() {
- return Executors.newFixedThreadPool(Runtime.getRuntime()
- .availableProcessors(), new ThreadFactory() {
-
- @Override
- public Thread newThread(Runnable r) {
- Thread t = new Thread(r);
- t.setPriority(Thread.MIN_PRIORITY);
- return t;
- }
- });
- }
-
- public static synchronized ImageLoader getInstance() {
- if (singleton == null) {
- singleton = new ImageLoader();
- }
- return singleton;
- }
-
- /**
- * Load a bitmap from the cover cache. If the bitmap is not in the cache, it
- * will be loaded from the disk. This method should either be called if the
- * ImageView's size has already been set or inside a Runnable which is
- * posted to the ImageView's message queue.
- */
- public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) {
- loadCoverBitmap(source, target, target.getHeight());
- }
-
- /**
- * Load a bitmap from the cover cache. If the bitmap is not in the cache, it
- * will be loaded from the disk. This method should either be called if the
- * ImageView's size has already been set or inside a Runnable which is
- * posted to the ImageView's message queue.
- */
- public void loadCoverBitmap(ImageWorkerTaskResource source,
- ImageView target, int length) {
- final int defaultCoverResource = getDefaultCoverResource(target
- .getContext());
- final String cacheKey;
- if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) {
- final Object currentTag = target.getTag(R.id.imageloader_key);
- if (currentTag == null || !cacheKey.equals(currentTag)) {
- target.setTag(R.id.imageloader_key, cacheKey);
- CachedBitmap cBitmap = getBitmapFromCoverCache(cacheKey);
- if (cBitmap != null && cBitmap.getLength() >= length) {
- target.setImageBitmap(cBitmap.getBitmap());
- } else {
- target.setImageResource(defaultCoverResource);
- BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask(
- handler, target, source, length, IMAGE_TYPE_COVER);
- executor.submit(worker);
- }
- }
- } else {
- target.setImageResource(defaultCoverResource);
- target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG);
- }
- }
-
- /**
- * Load a bitmap from the thumbnail cache. If the bitmap is not in the
- * cache, it will be loaded from the disk. This method should either be
- * called if the ImageView's size has already been set or inside a Runnable
- * which is posted to the ImageView's message queue.
- */
- public void loadThumbnailBitmap(ImageWorkerTaskResource source,
- ImageView target) {
- loadThumbnailBitmap(source, target, target.getHeight());
- }
-
- /**
- * Load a bitmap from the thumbnail cache. If the bitmap is not in the
- * cache, it will be loaded from the disk. This method should either be
- * called if the ImageView's size has already been set or inside a Runnable
- * which is posted to the ImageView's message queue.
- */
- public void loadThumbnailBitmap(ImageWorkerTaskResource source,
- ImageView target, int length) {
- final int defaultCoverResource = getDefaultCoverResource(target
- .getContext());
- final String cacheKey;
- if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) {
- final Object currentTag = target.getTag(R.id.imageloader_key);
- if (currentTag == null || !cacheKey.equals(currentTag)) {
- target.setTag(R.id.imageloader_key, cacheKey);
- CachedBitmap cBitmap = getBitmapFromThumbnailCache(cacheKey);
- if (cBitmap != null && cBitmap.getLength() >= length) {
- target.setImageBitmap(cBitmap.getBitmap());
- } else {
- target.setImageResource(defaultCoverResource);
- BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask(
- handler, target, source, length, IMAGE_TYPE_THUMBNAIL);
- executor.submit(worker);
- }
- }
- } else {
- target.setImageResource(defaultCoverResource);
- target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG);
- }
- }
-
- public void clearExecutorQueue() {
- executor.shutdownNow();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Executor was shut down.");
- executor = createExecutor();
-
- }
-
- public void wipeImageCache() {
- coverCache.evictAll();
- thumbnailCache.evictAll();
- }
-
- public boolean isInThumbnailCache(String fileUrl) {
- return thumbnailCache.get(fileUrl) != null;
- }
-
- private CachedBitmap getBitmapFromThumbnailCache(String key) {
- return thumbnailCache.get(key);
- }
-
- public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) {
- thumbnailCache.put(key, bitmap);
- }
-
- public boolean isInCoverCache(String fileUrl) {
- return coverCache.get(fileUrl) != null;
- }
-
- private CachedBitmap getBitmapFromCoverCache(String key) {
- return coverCache.get(key);
- }
-
- public void addBitmapToCoverCache(String key, CachedBitmap bitmap) {
- coverCache.put(key, bitmap);
- }
-
- private int getDefaultCoverResource(Context context) {
- return android.R.color.transparent;
- }
-
- /**
- * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap.
- */
- public interface ImageWorkerTaskResource {
- /**
- * Opens a new InputStream that can be decoded as a bitmap by the
- * BitmapFactory.
- */
- public InputStream openImageInputStream();
-
- /**
- * Returns an InputStream that points to the beginning of the image
- * resource. Implementations can either create a new InputStream or
- * reset the existing one, depending on their implementation of
- * openInputStream. If a new InputStream is returned, the one given as a
- * parameter MUST be closed.
- *
- * @param input The input stream that was returned by openImageInputStream()
- */
- public InputStream reopenImageInputStream(InputStream input);
-
- /**
- * Returns a string that identifies the image resource. Example: file
- * path of an image
- */
- public String getImageLoaderCacheKey();
- }
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java
new file mode 100644
index 000000000..84179cfcb
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java
@@ -0,0 +1,25 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.net.Uri;
+
+/**
+ * Classes that implement this interface provide access to an image resource that can
+ * be loaded by the Picasso library.
+ */
+public interface PicassoImageResource {
+
+ /**
+ * This scheme should be used by PicassoImageResources to
+ * indicate that the image Uri points to a file that is not an image
+ * (e.g. a media file). This workaround is needed so that the Picasso library
+ * loads these Uri with a Downloader instead of trying to load it directly.
+ * <p/>
+ * For example implementations, see FeedMedia or ExternalMedia.
+ */
+ public static final String SCHEME_MEDIA = "media";
+
+ /**
+ * Returns a Uri to the image or null if no image is available.
+ */
+ public Uri getImageUri();
+}
diff --git a/src/de/danoeh/antennapod/asynctask/PicassoProvider.java b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java
new file mode 100644
index 000000000..9ecf87023
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java
@@ -0,0 +1,141 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.squareup.picasso.Cache;
+import com.squareup.picasso.Downloader;
+import com.squareup.picasso.LruCache;
+import com.squareup.picasso.OkHttpDownloader;
+import com.squareup.picasso.Picasso;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Provides access to Picasso instances.
+ */
+public class PicassoProvider {
+ private static final String TAG = "PicassoProvider";
+
+ private static final boolean DEBUG = false;
+
+ private static ExecutorService executorService;
+ private static Cache memoryCache;
+
+ private static Picasso defaultPicassoInstance;
+ private static Picasso mediaMetadataPicassoInstance;
+
+ private static synchronized ExecutorService getExecutorService() {
+ if (executorService == null) {
+ executorService = Executors.newFixedThreadPool(3);
+ }
+ return executorService;
+ }
+
+ private static synchronized Cache getMemoryCache(Context context) {
+ if (memoryCache == null) {
+ memoryCache = new LruCache(context);
+ }
+ return memoryCache;
+ }
+
+ /**
+ * Returns a Picasso instance that uses an OkHttpDownloader. This instance can only load images
+ * from image files.
+ * <p/>
+ * This instance should be used as long as no images from media files are loaded.
+ */
+ public static synchronized Picasso getDefaultPicassoInstance(Context context) {
+ Validate.notNull(context);
+ if (defaultPicassoInstance == null) {
+ defaultPicassoInstance = new Picasso.Builder(context)
+ .indicatorsEnabled(DEBUG)
+ .loggingEnabled(DEBUG)
+ .downloader(new OkHttpDownloader(context))
+ .executor(getExecutorService())
+ .memoryCache(getMemoryCache(context))
+ .listener(new Picasso.Listener() {
+ @Override
+ public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
+ Log.e(TAG, "Failed to load Uri:" + uri.toString());
+ e.printStackTrace();
+ }
+ })
+ .build();
+ }
+ return defaultPicassoInstance;
+ }
+
+ /**
+ * Returns a Picasso instance that uses a MediaMetadataRetriever if the given Uri is a media file
+ * and a default OkHttpDownloader otherwise.
+ */
+ public static synchronized Picasso getMediaMetadataPicassoInstance(Context context) {
+ Validate.notNull(context);
+ if (mediaMetadataPicassoInstance == null) {
+ mediaMetadataPicassoInstance = new Picasso.Builder(context)
+ .indicatorsEnabled(DEBUG)
+ .loggingEnabled(DEBUG)
+ .downloader(new MediaMetadataDownloader(context))
+ .executor(getExecutorService())
+ .memoryCache(getMemoryCache(context))
+ .listener(new Picasso.Listener() {
+ @Override
+ public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
+ Log.e(TAG, "Failed to load Uri:" + uri.toString());
+ e.printStackTrace();
+ }
+ })
+ .build();
+ }
+ return mediaMetadataPicassoInstance;
+ }
+
+ private static class MediaMetadataDownloader implements Downloader {
+
+ private static final String TAG = "MediaMetadataDownloader";
+
+ private final OkHttpDownloader okHttpDownloader;
+
+ public MediaMetadataDownloader(Context context) {
+ Validate.notNull(context);
+ okHttpDownloader = new OkHttpDownloader(context);
+ }
+
+ @Override
+ public Response load(Uri uri, boolean b) throws IOException {
+ if (StringUtils.equals(uri.getScheme(), PicassoImageResource.SCHEME_MEDIA)) {
+ String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FilenameUtils.getExtension(uri.getLastPathSegment()));
+ if (StringUtils.startsWith(type, "image")) {
+ File imageFile = new File(uri.toString());
+ return new Response(new BufferedInputStream(new FileInputStream(imageFile)), true, imageFile.length());
+ } else {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ mmr.setDataSource(uri.getPath());
+ byte[] data = mmr.getEmbeddedPicture();
+ mmr.release();
+ if (data != null) {
+ return new Response(new ByteArrayInputStream(data), true, data.length);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ return okHttpDownloader.load(uri, b);
+ }
+ }
+}
diff --git a/src/de/danoeh/antennapod/feed/Feed.java b/src/de/danoeh/antennapod/feed/Feed.java
index f9da65e03..b5415c69c 100644
--- a/src/de/danoeh/antennapod/feed/Feed.java
+++ b/src/de/danoeh/antennapod/feed/Feed.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.feed;
import android.content.Context;
+import android.net.Uri;
+
+import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.EpisodeFilter;
@@ -16,7 +19,7 @@ import java.util.List;
*
* @author daniel
*/
-public class Feed extends FeedFile implements FlattrThing {
+public class Feed extends FeedFile implements FlattrThing, PicassoImageResource {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
public static final String TYPE_RSS091 = "rss";
@@ -430,4 +433,13 @@ public class Feed extends FeedFile implements FlattrThing {
preferences.setFeedID(id);
}
}
+
+ @Override
+ public Uri getImageUri() {
+ if (image != null) {
+ return image.getImageUri();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/feed/FeedImage.java b/src/de/danoeh/antennapod/feed/FeedImage.java
index 9c9170294..c588f5e71 100644
--- a/src/de/danoeh/antennapod/feed/FeedImage.java
+++ b/src/de/danoeh/antennapod/feed/FeedImage.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.feed;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import android.net.Uri;
+
+import de.danoeh.antennapod.asynctask.PicassoImageResource;
+
import org.apache.commons.io.IOUtils;
import java.io.File;
@@ -10,8 +13,7 @@ import java.io.InputStream;
-public class FeedImage extends FeedFile implements
- ImageLoader.ImageWorkerTaskResource {
+public class FeedImage extends FeedFile implements PicassoImageResource {
public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title;
@@ -64,30 +66,12 @@ public class FeedImage extends FeedFile implements
this.owner = owner;
}
- @Override
- public InputStream openImageInputStream() {
- if (file_url != null) {
- File file = new File(file_url);
- if (file.exists()) {
- try {
- return new FileInputStream(file_url);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
- return null;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return file_url;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- IOUtils.closeQuietly(input);
- return openImageInputStream();
- }
-
+ @Override
+ public Uri getImageUri() {
+ if (file_url != null && downloaded) {
+ return Uri.fromFile(new File(file_url));
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/feed/FeedItem.java b/src/de/danoeh/antennapod/feed/FeedItem.java
index 956131ab2..78091ea33 100644
--- a/src/de/danoeh/antennapod/feed/FeedItem.java
+++ b/src/de/danoeh/antennapod/feed/FeedItem.java
@@ -1,7 +1,9 @@
package de.danoeh.antennapod.feed;
+import android.net.Uri;
+
import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.flattr.FlattrStatus;
@@ -17,8 +19,7 @@ import java.util.concurrent.Callable;
*
* @author daniel
*/
-public class FeedItem extends FeedComponent implements
- ImageLoader.ImageWorkerTaskResource, ShownotesProvider, FlattrThing {
+public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, PicassoImageResource {
/**
* The id/guid that can be found in the rss/atom feed. Might not be set.
@@ -261,6 +262,17 @@ public class FeedItem extends FeedComponent implements
};
}
+ @Override
+ public Uri getImageUri() {
+ if (hasMedia()) {
+ return media.getImageUri();
+ } else if (feed != null) {
+ return feed.getImageUri();
+ } else {
+ return null;
+ }
+ }
+
public enum State {
NEW, IN_PROGRESS, READ, PLAYING
}
@@ -277,45 +289,6 @@ public class FeedItem extends FeedComponent implements
return (isRead() ? State.READ : State.NEW);
}
- @Override
- public InputStream openImageInputStream() {
- InputStream out = null;
- if (hasItemImageDownloaded()) {
- out = image.openImageInputStream();
- } else if (hasMedia()) {
- out = media.openImageInputStream();
- } else if (feed.getImage() != null) {
- out = feed.getImage().openImageInputStream();
- }
- return out;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- InputStream out = null;
- if (hasItemImageDownloaded()) {
- out = image.reopenImageInputStream(input);
- } else if (hasMedia()) {
- out = media.reopenImageInputStream(input);
- } else if (feed.getImage() != null) {
- out = feed.getImage().reopenImageInputStream(input);
- }
- return out;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- String out = null;
- if (hasItemImageDownloaded()) {
- out = image.getImageLoaderCacheKey();
- } else if (hasMedia()) {
- out = media.getImageLoaderCacheKey();
- } else if (feed.getImage() != null) {
- out = feed.getImage().getImageLoaderCacheKey();
- }
- return out;
- }
-
public long getFeedId() {
return feedId;
}
diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java
index dc941cb48..f555654d0 100644
--- a/src/de/danoeh/antennapod/feed/FeedMedia.java
+++ b/src/de/danoeh/antennapod/feed/FeedMedia.java
@@ -2,21 +2,24 @@ package de.danoeh.antennapod.feed;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.storage.DBReader;
-import de.danoeh.antennapod.storage.DBWriter;
-import de.danoeh.antennapod.util.ChapterUtils;
-import de.danoeh.antennapod.util.playback.Playable;
+import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
+import de.danoeh.antennapod.PodcastApp;
+import de.danoeh.antennapod.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.ChapterUtils;
+import de.danoeh.antennapod.util.playback.Playable;
+
public class FeedMedia extends FeedFile implements Playable {
private static final String TAG = "FeedMedia";
@@ -382,52 +385,13 @@ public class FeedMedia extends FeedFile implements Playable {
};
@Override
- public InputStream openImageInputStream() {
- InputStream out;
- if (item.hasItemImageDownloaded()) {
- out = item.openImageInputStream();
+ public Uri getImageUri() {
+ if (localFileAvailable()) {
+ return new Uri.Builder().scheme(SCHEME_MEDIA).encodedPath(getLocalMediaUrl()).build();
+ } else if (item != null && item.getFeed() != null) {
+ return item.getFeed().getImageUri();
} else {
- out = new Playable.DefaultPlayableImageLoader(this)
- .openImageInputStream();
- }
- if (out == null) {
- if (item.getFeed().getImage() != null) {
- return item.getFeed().getImage().openImageInputStream();
- }
- }
- return out;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- String out;
- if (item == null) {
return null;
- } else if (item.hasItemImageDownloaded()) {
- out = item.getImageLoaderCacheKey();
- } else {
- out = new Playable.DefaultPlayableImageLoader(this)
- .getImageLoaderCacheKey();
- }
- if (out == null) {
- if (item.getFeed().getImage() != null) {
- return item.getFeed().getImage().getImageLoaderCacheKey();
- }
- }
- return out;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- if (input instanceof FileInputStream) {
- if (item.hasItemImageDownloaded()) {
- return item.getImage().reopenImageInputStream(input);
- } else {
- return item.getFeed().getImage().reopenImageInputStream(input);
- }
- } else {
- return new Playable.DefaultPlayableImageLoader(this)
- .reopenImageInputStream(input);
}
}
}
diff --git a/src/de/danoeh/antennapod/fragment/CoverFragment.java b/src/de/danoeh/antennapod/fragment/CoverFragment.java
index 0e1fe35e0..ffce518bf 100644
--- a/src/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/src/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.fragment;
+import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
@@ -7,91 +8,98 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.util.playback.Playable;
-/** Displays the cover and the title of a FeedItem. */
+/**
+ * Displays the cover and the title of a FeedItem.
+ */
public class CoverFragment extends Fragment implements
- AudioplayerContentFragment {
- private static final String TAG = "CoverFragment";
- private static final String ARG_PLAYABLE = "arg.playable";
+ AudioplayerContentFragment {
+ private static final String TAG = "CoverFragment";
+ private static final String ARG_PLAYABLE = "arg.playable";
- private Playable media;
+ private Playable media;
- private ImageView imgvCover;
+ private ImageView imgvCover;
- private boolean viewCreated = false;
+ private boolean viewCreated = false;
- public static CoverFragment newInstance(Playable item) {
- CoverFragment f = new CoverFragment();
- if (item != null) {
- Bundle args = new Bundle();
- args.putParcelable(ARG_PLAYABLE, item);
- f.setArguments(args);
- }
- return f;
- }
+ public static CoverFragment newInstance(Playable item) {
+ CoverFragment f = new CoverFragment();
+ if (item != null) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_PLAYABLE, item);
+ f.setArguments(args);
+ }
+ return f;
+ }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- Bundle args = getArguments();
- if (args != null) {
- media = args.getParcelable(ARG_PLAYABLE);
- } else {
- Log.e(TAG, TAG + " was called with invalid arguments");
- }
- }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ Bundle args = getArguments();
+ if (args != null) {
+ media = args.getParcelable(ARG_PLAYABLE);
+ } else {
+ Log.e(TAG, TAG + " was called with invalid arguments");
+ }
+ }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.cover_fragment, container, false);
- imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
- viewCreated = true;
- return root;
- }
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.cover_fragment, container, false);
+ imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
+ viewCreated = true;
+ return root;
+ }
- private void loadMediaInfo() {
- if (media != null) {
- imgvCover.post(new Runnable() {
+ private void loadMediaInfo() {
+ if (media != null) {
+ imgvCover.post(new Runnable() {
- @Override
- public void run() {
- ImageLoader.getInstance().loadCoverBitmap(
- media, imgvCover);
- }
- });
- } else {
- Log.w(TAG, "loadMediaInfo was called while media was null");
- }
- }
+ @Override
+ public void run() {
+ Context c = getActivity();
+ if (c != null) {
+ PicassoProvider.getMediaMetadataPicassoInstance(c)
+ .load(media.getImageUri())
+ .into(imgvCover);
+ }
+ }
+ });
+ } else {
+ Log.w(TAG, "loadMediaInfo was called while media was null");
+ }
+ }
- @Override
- public void onStart() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "On Start");
- super.onStart();
- if (media != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Loading media info");
- loadMediaInfo();
- } else {
- Log.w(TAG, "Unable to load media info: media was null");
- }
- }
+ @Override
+ public void onStart() {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "On Start");
+ super.onStart();
+ if (media != null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Loading media info");
+ loadMediaInfo();
+ } else {
+ Log.w(TAG, "Unable to load media info: media was null");
+ }
+ }
- @Override
- public void onDataSetChanged(Playable media) {
- this.media = media;
- if (viewCreated) {
- loadMediaInfo();
- }
+ @Override
+ public void onDataSetChanged(Playable media) {
+ this.media = media;
+ if (viewCreated) {
+ loadMediaInfo();
+ }
- }
+ }
}
diff --git a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index db47cd8a4..77587194b 100644
--- a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -10,9 +10,10 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.util.playback.Playable;
@@ -23,215 +24,216 @@ import de.danoeh.antennapod.util.playback.PlaybackController;
* if the PlaybackService is running
*/
public class ExternalPlayerFragment extends Fragment {
- private static final String TAG = "ExternalPlayerFragment";
-
- private ViewGroup fragmentLayout;
- private ImageView imgvCover;
- private ViewGroup layoutInfo;
- private TextView txtvTitle;
- private ImageButton butPlay;
-
- private PlaybackController controller;
-
- public ExternalPlayerFragment() {
- super();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.external_player_fragment,
- container, false);
- fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
- imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
- layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo);
- txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
- butPlay = (ImageButton) root.findViewById(R.id.butPlay);
-
- layoutInfo.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "layoutInfo was clicked");
-
- if (controller.getMedia() != null) {
- startActivity(PlaybackService.getPlayerActivityIntent(
- getActivity(), controller.getMedia()));
- }
- }
- });
- return root;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- controller = setupPlaybackController();
- butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
- }
-
- private PlaybackController setupPlaybackController() {
- return new PlaybackController(getActivity(), true) {
-
- @Override
- public void setupGUI() {
- }
-
- @Override
- public void onPositionObserverUpdate() {
- }
-
- @Override
- public void onReloadNotification(int code) {
- }
-
- @Override
- public void onBufferStart() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onBufferEnd() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onBufferUpdate(float progress) {
- }
-
- @Override
- public void onSleepTimerUpdate() {
- }
-
- @Override
- public void handleError(int code) {
- }
-
- @Override
- public ImageButton getPlayButton() {
- return butPlay;
- }
-
- @Override
- public void postStatusMsg(int msg) {
- }
-
- @Override
- public void clearStatusMsg() {
- }
-
- @Override
- public boolean loadMediaInfo() {
+ private static final String TAG = "ExternalPlayerFragment";
+
+ private ViewGroup fragmentLayout;
+ private ImageView imgvCover;
+ private ViewGroup layoutInfo;
+ private TextView txtvTitle;
+ private ImageButton butPlay;
+
+ private PlaybackController controller;
+
+ public ExternalPlayerFragment() {
+ super();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.external_player_fragment,
+ container, false);
+ fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
+ imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
+ layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo);
+ txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
+ butPlay = (ImageButton) root.findViewById(R.id.butPlay);
+
+ layoutInfo.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "layoutInfo was clicked");
+
+ if (controller.getMedia() != null) {
+ startActivity(PlaybackService.getPlayerActivityIntent(
+ getActivity(), controller.getMedia()));
+ }
+ }
+ });
+ return root;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ controller = setupPlaybackController();
+ butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
+ }
+
+ private PlaybackController setupPlaybackController() {
+ return new PlaybackController(getActivity(), true) {
+
+ @Override
+ public void setupGUI() {
+ }
+
+ @Override
+ public void onPositionObserverUpdate() {
+ }
+
+ @Override
+ public void onReloadNotification(int code) {
+ }
+
+ @Override
+ public void onBufferStart() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onBufferEnd() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onBufferUpdate(float progress) {
+ }
+
+ @Override
+ public void onSleepTimerUpdate() {
+ }
+
+ @Override
+ public void handleError(int code) {
+ }
+
+ @Override
+ public ImageButton getPlayButton() {
+ return butPlay;
+ }
+
+ @Override
+ public void postStatusMsg(int msg) {
+ }
+
+ @Override
+ public void clearStatusMsg() {
+ }
+
+ @Override
+ public boolean loadMediaInfo() {
ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
if (fragment != null) {
- return fragment.loadMediaInfo();
+ return fragment.loadMediaInfo();
} else {
return false;
}
- }
-
- @Override
- public void onAwaitingVideoSurface() {
- }
-
- @Override
- public void onServiceQueried() {
- }
-
- @Override
- public void onShutdownNotification() {
- if (fragmentLayout != null) {
- fragmentLayout.setVisibility(View.GONE);
- }
- controller = setupPlaybackController();
- if (butPlay != null) {
- butPlay.setOnClickListener(controller
- .newOnPlayButtonClickListener());
- }
-
- }
-
- @Override
- public void onPlaybackEnd() {
- if (fragmentLayout != null) {
- fragmentLayout.setVisibility(View.GONE);
- }
- controller = setupPlaybackController();
- if (butPlay != null) {
- butPlay.setOnClickListener(controller
- .newOnPlayButtonClickListener());
- }
- }
-
- @Override
- public void onPlaybackSpeedChange() {
- // TODO Auto-generated method stub
-
- }
- };
- }
-
- @Override
- public void onResume() {
- super.onResume();
- controller.init();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment is about to be destroyed");
- if (controller != null) {
- controller.release();
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (controller != null) {
- controller.pause();
- }
- }
-
- private boolean loadMediaInfo() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Loading media info");
- if (controller.serviceAvailable()) {
- Playable media = controller.getMedia();
- if (media != null) {
- txtvTitle.setText(media.getEpisodeTitle());
- ImageLoader.getInstance().loadThumbnailBitmap(
- media,
- imgvCover,
- (int) getActivity().getResources().getDimension(
- R.dimen.external_player_height));
-
- fragmentLayout.setVisibility(View.VISIBLE);
- if (controller.isPlayingVideo()) {
- butPlay.setVisibility(View.GONE);
- } else {
- butPlay.setVisibility(View.VISIBLE);
- }
+ }
+
+ @Override
+ public void onAwaitingVideoSurface() {
+ }
+
+ @Override
+ public void onServiceQueried() {
+ }
+
+ @Override
+ public void onShutdownNotification() {
+ if (fragmentLayout != null) {
+ fragmentLayout.setVisibility(View.GONE);
+ }
+ controller = setupPlaybackController();
+ if (butPlay != null) {
+ butPlay.setOnClickListener(controller
+ .newOnPlayButtonClickListener());
+ }
+
+ }
+
+ @Override
+ public void onPlaybackEnd() {
+ if (fragmentLayout != null) {
+ fragmentLayout.setVisibility(View.GONE);
+ }
+ controller = setupPlaybackController();
+ if (butPlay != null) {
+ butPlay.setOnClickListener(controller
+ .newOnPlayButtonClickListener());
+ }
+ }
+
+ @Override
+ public void onPlaybackSpeedChange() {
+ // TODO Auto-generated method stub
+
+ }
+ };
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ controller.init();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Fragment is about to be destroyed");
+ if (controller != null) {
+ controller.release();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (controller != null) {
+ controller.pause();
+ }
+ }
+
+ private boolean loadMediaInfo() {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Loading media info");
+ if (controller.serviceAvailable()) {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ txtvTitle.setText(media.getEpisodeTitle());
+
+ int imageSize = (int) getResources().getDimension(R.dimen.external_player_height);
+ PicassoProvider.getMediaMetadataPicassoInstance(getActivity())
+ .load(media.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(imgvCover);
+
+ fragmentLayout.setVisibility(View.VISIBLE);
+ if (controller.isPlayingVideo()) {
+ butPlay.setVisibility(View.GONE);
+ } else {
+ butPlay.setVisibility(View.VISIBLE);
+ }
return true;
- } else {
- Log.w(TAG,
- "loadMediaInfo was called while the media object of playbackService was null!");
+ } else {
+ Log.w(TAG,
+ "loadMediaInfo was called while the media object of playbackService was null!");
return false;
- }
- } else {
- Log.w(TAG,
- "loadMediaInfo was called while playbackService was null!");
+ }
+ } else {
+ Log.w(TAG,
+ "loadMediaInfo was called while playbackService was null!");
return false;
- }
- }
+ }
+ }
- private String getPositionString(int position, int duration) {
- return Converter.getDurationStringLong(position) + " / "
- + Converter.getDurationStringLong(duration);
- }
+ private String getPositionString(int position, int duration) {
+ return Converter.getDurationStringLong(position) + " / "
+ + Converter.getDurationStringLong(duration);
+ }
}
diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
index d37f17b6d..5ef914f6c 100644
--- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -34,7 +34,7 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.asynctask.DownloadObserver;
import de.danoeh.antennapod.asynctask.FeedRemover;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.ConfirmationDialog;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.dialog.FeedItemDialog;
@@ -349,8 +349,13 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
- ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), imgvCover,
- (int) getResources().getDimension(R.dimen.thumbnail_length_onlinefeedview));
+
+ int imageSize = (int) getResources().getDimension(R.dimen.thumbnail_length_onlinefeedview);
+ PicassoProvider.getDefaultPicassoInstance(getActivity())
+ .load(feed.getImageUri())
+ .resize(imageSize, imageSize)
+ .into(imgvCover);
+
if (feed.getLink() == null) {
butVisitWebsite.setVisibility(View.INVISIBLE);
} else {
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
index 163a57ed2..59d7ddbb9 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
@@ -4,7 +4,12 @@ import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
-import android.content.*;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
@@ -26,11 +31,14 @@ import android.widget.Toast;
import org.apache.commons.lang3.StringUtils;
+import java.io.IOException;
+import java.util.List;
+
import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.VideoplayerActivity;
+import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
@@ -41,14 +49,10 @@ import de.danoeh.antennapod.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.receiver.PlayerWidget;
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 java.util.List;
-
/**
* Controls the MediaPlayer that plays a FeedMedia-file
*/
@@ -257,7 +261,8 @@ public class PlaybackService extends Service {
}
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
- if (BuildConfig.DEBUG) Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
stopForeground(true);
} else {
@@ -678,11 +683,16 @@ public class PlaybackService extends Service {
Log.d(TAG, "Starting background work");
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (info.playable != null) {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- icon = BitmapDecoder
- .decodeBitmapFromWorkerTaskResource(iconSize,
- info.playable);
+ try {
+ int iconSize = getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ icon = PicassoProvider.getMediaMetadataPicassoInstance(PlaybackService.this)
+ .load(info.playable.getImageUri())
+ .resize(iconSize, iconSize)
+ .get();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
@@ -766,7 +776,7 @@ public class PlaybackService extends Service {
if (updatePlayedDuration && playable instanceof FeedMedia) {
FeedMedia m = (FeedMedia) playable;
FeedItem item = m.getItem();
- m.setPlayedDuration(m.getPlayedDuration() + ((int)(deltaPlayedDuration * playbackSpeed)));
+ m.setPlayedDuration(m.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed)));
// Auto flattr
if (isAutoFlattrable(m) &&
(m.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
@@ -778,8 +788,9 @@ public class PlaybackService extends Service {
}
}
playable.saveCurrentPosition(PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()),
- position);
+ .getDefaultSharedPreferences(getApplicationContext()),
+ position
+ );
}
}
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
index 477eea9a6..49f20012d 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
@@ -4,12 +4,23 @@ import android.content.ComponentName;
import android.content.Context;
import android.media.AudioManager;
import android.media.RemoteControlClient;
+import android.net.wifi.WifiManager;
+import android.os.PowerManager;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
import org.apache.commons.lang3.Validate;
+import java.io.IOException;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
@@ -20,14 +31,6 @@ import de.danoeh.antennapod.util.playback.IPlayer;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.VideoPlayer;
-import java.io.IOException;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReentrantLock;
-
/**
* Manages the MediaPlayer object of the PlaybackService.
*/
@@ -63,6 +66,11 @@ public class PlaybackServiceMediaPlayer {
private final ThreadPoolExecutor executor;
+ /**
+ * A wifi-lock that is acquired if the media file is being streamed.
+ */
+ private WifiManager.WifiLock wifiLock;
+
public PlaybackServiceMediaPlayer(Context context, PSMPCallback callback) {
Validate.notNull(context);
Validate.notNull(callback);
@@ -227,7 +235,7 @@ public class PlaybackServiceMediaPlayer {
audioFocusChangeListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
-
+ acquireWifiLockIfNecessary();
setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed()));
mediaPlayer.start();
if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
@@ -273,7 +281,7 @@ public class PlaybackServiceMediaPlayer {
@Override
public void run() {
playerLock.lock();
-
+ releaseWifiLockIfNecessary();
if (playerStatus == PlayerStatus.PLAYING) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Pausing playback.");
@@ -374,13 +382,14 @@ public class PlaybackServiceMediaPlayer {
@Override
public void run() {
playerLock.lock();
-
+ releaseWifiLockIfNecessary();
if (media != null) {
playMediaObject(media, true, stream, startWhenPrepared.get(), false);
} else if (mediaPlayer != null) {
mediaPlayer.reset();
} else {
- if (BuildConfig.DEBUG) Log.d(TAG, "Call to reinit was ignored: media and mediaPlayer were null");
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Call to reinit was ignored: media and mediaPlayer were null");
}
playerLock.unlock();
}
@@ -590,6 +599,7 @@ public class PlaybackServiceMediaPlayer {
if (mediaPlayer != null) {
mediaPlayer.release();
}
+ releaseWifiLockIfNecessary();
}
public void setVideoSurface(final SurfaceHolder surface) {
@@ -682,6 +692,7 @@ public class PlaybackServiceMediaPlayer {
mediaPlayer = new AudioPlayer(context);
}
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
return setMediaPlayerListeners(mediaPlayer);
}
@@ -694,59 +705,62 @@ public class PlaybackServiceMediaPlayer {
public void run() {
playerLock.lock();
- switch (focusChange) {
- case AudioManager.AUDIOFOCUS_LOSS:
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Lost audio focus");
- pause(true, false);
- callback.shouldStop();
- break;
- case AudioManager.AUDIOFOCUS_GAIN:
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Gained audio focus");
- if (pausedBecauseOfTransientAudiofocusLoss) // we paused => play now
- resume();
- else // we ducked => raise audio level back
+ // If there is an incoming call, playback should be paused permanently
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final int callState = (tm != null) ? tm.getCallState() : 0;
+ if (BuildConfig.DEBUG) Log.d(TAG, "Call state: " + callState);
+ Log.i(TAG, "Call state:" + callState);
+
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS || callState != TelephonyManager.CALL_STATE_IDLE) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Lost audio focus");
+ pause(true, false);
+ callback.shouldStop();
+ } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Gained audio focus");
+ if (pausedBecauseOfTransientAudiofocusLoss) { // we paused => play now
+ resume();
+ } else { // we ducked => raise audio level back
+ audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+ AudioManager.ADJUST_RAISE, 0);
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ if (playerStatus == PlayerStatus.PLAYING) {
+ if (!UserPreferences.shouldPauseForFocusLoss()) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Lost audio focus temporarily. Ducking...");
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
- AudioManager.ADJUST_RAISE, 0);
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
- if (playerStatus == PlayerStatus.PLAYING) {
- if (!UserPreferences.shouldPauseForFocusLoss()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Lost audio focus temporarily. Ducking...");
- audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
- AudioManager.ADJUST_LOWER, 0);
- pausedBecauseOfTransientAudiofocusLoss = false;
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
- pause(false, false);
- pausedBecauseOfTransientAudiofocusLoss = true;
- }
- }
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- if (playerStatus == PlayerStatus.PLAYING) {
+ AudioManager.ADJUST_LOWER, 0);
+ pausedBecauseOfTransientAudiofocusLoss = false;
+ } else {
if (BuildConfig.DEBUG)
- Log.d(TAG, "Lost audio focus temporarily. Pausing...");
+ Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
pause(false, false);
pausedBecauseOfTransientAudiofocusLoss = true;
}
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ if (playerStatus == PlayerStatus.PLAYING) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Lost audio focus temporarily. Pausing...");
+ pause(false, false);
+ pausedBecauseOfTransientAudiofocusLoss = true;
+ }
+ playerLock.unlock();
}
-
- playerLock.unlock();
}
});
-
}
};
+
public void endPlayback() {
executor.submit(new Runnable() {
@Override
public void run() {
playerLock.lock();
+ releaseWifiLockIfNecessary();
if (playerStatus != PlayerStatus.INDETERMINATE) {
setPlayerStatus(PlayerStatus.INDETERMINATE, media);
@@ -774,11 +788,13 @@ public class PlaybackServiceMediaPlayer {
@Override
public void run() {
playerLock.lock();
+ releaseWifiLockIfNecessary();
if (playerStatus == PlayerStatus.INDETERMINATE) {
setPlayerStatus(PlayerStatus.STOPPED, null);
} else {
- if (BuildConfig.DEBUG) Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
}
playerLock.unlock();
@@ -786,6 +802,23 @@ public class PlaybackServiceMediaPlayer {
});
}
+ private synchronized void acquireWifiLockIfNecessary() {
+ if (stream) {
+ if (wifiLock == null) {
+ wifiLock = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
+ .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
+ wifiLock.setReferenceCounted(false);
+ }
+ wifiLock.acquire();
+ }
+ }
+
+ private synchronized void releaseWifiLockIfNecessary() {
+ if (wifiLock != null && wifiLock.isHeld()) {
+ wifiLock.release();
+ }
+ }
+
/**
* Holds information about a PSMP object.
*/
diff --git a/src/de/danoeh/antennapod/storage/DBTasks.java b/src/de/danoeh/antennapod/storage/DBTasks.java
index 8d0ffd9c1..a230ba797 100644
--- a/src/de/danoeh/antennapod/storage/DBTasks.java
+++ b/src/de/danoeh/antennapod/storage/DBTasks.java
@@ -872,7 +872,7 @@ public final class DBTasks {
item.getFlattrStatus().setFlattrQueue();
DBWriter.setFlattredStatus(context, item, true);
} else {
- FlattrUtils.showNoTokenDialog(context, item.getPaymentLink());
+ FlattrUtils.showNoTokenDialogOrRedirect(context, item.getPaymentLink());
}
}
@@ -888,7 +888,7 @@ public final class DBTasks {
feed.getFlattrStatus().setFlattrQueue();
DBWriter.setFlattredStatus(context, feed, true);
} else {
- FlattrUtils.showNoTokenDialog(context, feed.getPaymentLink());
+ FlattrUtils.showNoTokenDialogOrRedirect(context, feed.getPaymentLink());
}
}
diff --git a/src/de/danoeh/antennapod/util/BitmapDecoder.java b/src/de/danoeh/antennapod/util/BitmapDecoder.java
deleted file mode 100644
index 5296d675a..000000000
--- a/src/de/danoeh/antennapod/util/BitmapDecoder.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package de.danoeh.antennapod.util;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.asynctask.ImageLoader;
-import org.apache.commons.io.IOUtils;
-
-import java.io.InputStream;
-
-public class BitmapDecoder {
- private static final String TAG = "BitmapDecoder";
-
- private static int calculateSampleSize(int preferredLength, int length) {
- int sampleSize = 1;
- if (length > preferredLength) {
- sampleSize = Math.round(((float) length / (float) preferredLength));
- }
- return sampleSize;
- }
-
- public static Bitmap decodeBitmapFromWorkerTaskResource(int preferredLength,
- ImageLoader.ImageWorkerTaskResource source) {
- InputStream input = source.openImageInputStream();
- if (input != null) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(input, new Rect(), options);
- int srcWidth = options.outWidth;
- int srcHeight = options.outHeight;
- int length = Math.max(srcWidth, srcHeight);
- int sampleSize = calculateSampleSize(preferredLength, length);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Using samplesize " + sampleSize);
- options.inJustDecodeBounds = false;
- options.inSampleSize = sampleSize;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- Bitmap decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input),
- null, options);
- if (decodedBitmap == null) {
- decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input));
- }
- IOUtils.closeQuietly(input);
- return decodedBitmap;
- }
- return null;
- }
-}
diff --git a/src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example b/src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example
deleted file mode 100644
index da16069ec..000000000
--- a/src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.danoeh.antennapod.util.flattr;
-
-/** Contains credentials to access the Flattr API*/
-public class FlattrConfig {
- static final String APP_KEY = "";
- static final String APP_SECRET = "";
-}
diff --git a/src/de/danoeh/antennapod/util/flattr/FlattrUtils.java b/src/de/danoeh/antennapod/util/flattr/FlattrUtils.java
index 9809f69a3..1ff3437c0 100644
--- a/src/de/danoeh/antennapod/util/flattr/FlattrUtils.java
+++ b/src/de/danoeh/antennapod/util/flattr/FlattrUtils.java
@@ -9,12 +9,8 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.FlattrAuthActivity;
-import de.danoeh.antennapod.asynctask.FlattrTokenFetcher;
-import de.danoeh.antennapod.storage.DBWriter;
+
+import org.apache.commons.lang3.StringUtils;
import org.shredzone.flattr4j.FlattrService;
import org.shredzone.flattr4j.exception.FlattrException;
import org.shredzone.flattr4j.model.Flattr;
@@ -23,252 +19,287 @@ import org.shredzone.flattr4j.oauth.AccessToken;
import org.shredzone.flattr4j.oauth.AndroidAuthenticator;
import org.shredzone.flattr4j.oauth.Scope;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.TimeZone;
+
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.PodcastApp;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.FlattrAuthActivity;
+import de.danoeh.antennapod.asynctask.FlattrTokenFetcher;
+import de.danoeh.antennapod.storage.DBWriter;
-/** Utility methods for doing something with flattr. */
+/**
+ * Utility methods for doing something with flattr.
+ */
public class FlattrUtils {
- private static final String TAG = "FlattrUtils";
-
- private static final String HOST_NAME = "de.danoeh.antennapod";
-
- private static final String PREF_ACCESS_TOKEN = "de.danoeh.antennapod.preference.flattrAccessToken";
-
- // Flattr URL for this app.
- public static final String APP_URL = "http://antennapod.com";
- // Human-readable flattr-page.
- public static final String APP_LINK = "https://flattr.com/thing/745609/";
- public static final String APP_THING_ID = "745609";
-
- private static volatile AccessToken cachedToken;
-
- private static AndroidAuthenticator createAuthenticator() {
- return new AndroidAuthenticator(HOST_NAME, FlattrConfig.APP_KEY,
- FlattrConfig.APP_SECRET);
- }
-
- public static void startAuthProcess(Context context) throws FlattrException {
- AndroidAuthenticator auth = createAuthenticator();
- auth.setScope(EnumSet.of(Scope.FLATTR));
- Intent intent = auth.createAuthenticateIntent();
- context.startActivity(intent);
- }
-
- private static AccessToken retrieveToken() {
- if (cachedToken == null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Retrieving access token");
- String token = PreferenceManager.getDefaultSharedPreferences(
- PodcastApp.getInstance())
- .getString(PREF_ACCESS_TOKEN, null);
- if (token != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Found access token. Caching.");
- cachedToken = new AccessToken(token);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "No access token found");
- return null;
- }
- }
- return cachedToken;
-
- }
-
- public static boolean hasToken() {
- return retrieveToken() != null;
- }
-
- public static void storeToken(AccessToken token) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Storing token");
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(PodcastApp.getInstance()).edit();
- if (token != null) {
- editor.putString(PREF_ACCESS_TOKEN, token.getToken());
- } else {
- editor.putString(PREF_ACCESS_TOKEN, null);
- }
- editor.commit();
- cachedToken = token;
- }
-
- public static void deleteToken() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Deleting flattr token");
- storeToken(null);
- }
-
- public static Thing getAppThing(Context context) {
- FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
- try {
- Thing thing = fs.getThing(Thing.withId(APP_THING_ID));
- return thing;
- } catch (FlattrException e) {
- e.printStackTrace();
- showErrorDialog(context, e.getMessage());
- return null;
- }
- }
-
- public static void clickUrl(Context context, String url)
- throws FlattrException {
- if (hasToken()) {
- FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
- fs.click(url);
- } else {
- Log.e(TAG, "clickUrl was called with null access token");
- }
- }
-
- public static List<Flattr> retrieveFlattredThings()
- throws FlattrException {
- ArrayList<Flattr> myFlattrs = new ArrayList<Flattr>();
-
- if (hasToken()) {
- FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
-
- Calendar firstOfMonth = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- firstOfMonth.set(Calendar.MILLISECOND, 0);
- firstOfMonth.set(Calendar.SECOND, 0);
- firstOfMonth.set(Calendar.MINUTE, 0);
- firstOfMonth.set(Calendar.HOUR_OF_DAY, 0);
- firstOfMonth.set(Calendar.DAY_OF_MONTH, Calendar.getInstance().getActualMinimum(Calendar.DAY_OF_MONTH));
-
- Date firstOfMonthDate = firstOfMonth.getTime();
-
- // subscriptions some times get flattrd slightly before midnight - give it an hour leeway
- firstOfMonthDate = new Date(firstOfMonthDate.getTime() - 60*60*1000);
-
- final int FLATTR_COUNT = 30;
- final int FLATTR_MAXPAGE = 5;
-
- for (int page = 0; page < FLATTR_MAXPAGE; page++) {
- for (Flattr fl: fs.getMyFlattrs(FLATTR_COUNT, page)) {
- if (fl.getCreated().after(firstOfMonthDate))
- myFlattrs.add(fl);
- else
- break;
- }
- }
-
- if (BuildConfig.DEBUG) {
- Log.d(TAG, "Got my flattrs list of length " + Integer.toString(myFlattrs.size()) + " comparison date" + firstOfMonthDate);
-
- for (Flattr fl: myFlattrs) {
- Thing thing = fl.getThing();
- Log.d(TAG, "Flattr thing: " + fl.getThingId() + " name: " + thing.getTitle() + " url: " + thing.getUrl() + " on: " + fl.getCreated());
- }
- }
-
- } else {
- Log.e(TAG, "retrieveFlattrdThings was called with null access token");
- }
-
- return myFlattrs;
- }
-
- public static void handleCallback(Context context, Uri uri) {
- AndroidAuthenticator auth = createAuthenticator();
- new FlattrTokenFetcher(context, auth, uri).executeAsync();
- }
-
- public static void revokeAccessToken(Context context) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Revoking access token");
- deleteToken();
- FlattrServiceCreator.deleteFlattrService();
- showRevokeDialog(context);
+ private static final String TAG = "FlattrUtils";
+
+ private static final String HOST_NAME = "de.danoeh.antennapod";
+
+ private static final String PREF_ACCESS_TOKEN = "de.danoeh.antennapod.preference.flattrAccessToken";
+
+ // Flattr URL for this app.
+ public static final String APP_URL = "http://antennapod.com";
+ // Human-readable flattr-page.
+ public static final String APP_LINK = "https://flattr.com/thing/745609/";
+ public static final String APP_THING_ID = "745609";
+
+ private static volatile AccessToken cachedToken;
+
+ private static AndroidAuthenticator createAuthenticator() {
+ return new AndroidAuthenticator(HOST_NAME, BuildConfig.FLATTR_APP_KEY,
+ BuildConfig.FLATTR_APP_SECRET);
+ }
+
+ public static void startAuthProcess(Context context) throws FlattrException {
+ AndroidAuthenticator auth = createAuthenticator();
+ auth.setScope(EnumSet.of(Scope.FLATTR));
+ Intent intent = auth.createAuthenticateIntent();
+ context.startActivity(intent);
+ }
+
+ private static AccessToken retrieveToken() {
+ if (cachedToken == null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Retrieving access token");
+ String token = PreferenceManager.getDefaultSharedPreferences(
+ PodcastApp.getInstance())
+ .getString(PREF_ACCESS_TOKEN, null);
+ if (token != null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Found access token. Caching.");
+ cachedToken = new AccessToken(token);
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "No access token found");
+ return null;
+ }
+ }
+ return cachedToken;
+
+ }
+
+ /**
+ * Returns true if FLATTR_APP_KEY and FLATTR_APP_SECRET in BuildConfig are not null and not empty
+ */
+ public static boolean hasAPICredentials() {
+ return StringUtils.isNotEmpty(BuildConfig.FLATTR_APP_KEY)
+ && StringUtils.isNoneEmpty(BuildConfig.FLATTR_APP_SECRET);
+ }
+
+ public static boolean hasToken() {
+ return retrieveToken() != null;
+ }
+
+ public static void storeToken(AccessToken token) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Storing token");
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(PodcastApp.getInstance()).edit();
+ if (token != null) {
+ editor.putString(PREF_ACCESS_TOKEN, token.getToken());
+ } else {
+ editor.putString(PREF_ACCESS_TOKEN, null);
+ }
+ editor.commit();
+ cachedToken = token;
+ }
+
+ public static void deleteToken() {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Deleting flattr token");
+ storeToken(null);
+ }
+
+ public static Thing getAppThing(Context context) {
+ FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
+ try {
+ Thing thing = fs.getThing(Thing.withId(APP_THING_ID));
+ return thing;
+ } catch (FlattrException e) {
+ e.printStackTrace();
+ showErrorDialog(context, e.getMessage());
+ return null;
+ }
+ }
+
+ public static void clickUrl(Context context, String url)
+ throws FlattrException {
+ if (hasToken()) {
+ FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
+ fs.click(url);
+ } else {
+ Log.e(TAG, "clickUrl was called with null access token");
+ }
+ }
+
+ public static List<Flattr> retrieveFlattredThings()
+ throws FlattrException {
+ ArrayList<Flattr> myFlattrs = new ArrayList<Flattr>();
+
+ if (hasToken()) {
+ FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
+
+ Calendar firstOfMonth = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ firstOfMonth.set(Calendar.MILLISECOND, 0);
+ firstOfMonth.set(Calendar.SECOND, 0);
+ firstOfMonth.set(Calendar.MINUTE, 0);
+ firstOfMonth.set(Calendar.HOUR_OF_DAY, 0);
+ firstOfMonth.set(Calendar.DAY_OF_MONTH, Calendar.getInstance().getActualMinimum(Calendar.DAY_OF_MONTH));
+
+ Date firstOfMonthDate = firstOfMonth.getTime();
+
+ // subscriptions some times get flattrd slightly before midnight - give it an hour leeway
+ firstOfMonthDate = new Date(firstOfMonthDate.getTime() - 60 * 60 * 1000);
+
+ final int FLATTR_COUNT = 30;
+ final int FLATTR_MAXPAGE = 5;
+
+ for (int page = 0; page < FLATTR_MAXPAGE; page++) {
+ for (Flattr fl : fs.getMyFlattrs(FLATTR_COUNT, page)) {
+ if (fl.getCreated().after(firstOfMonthDate))
+ myFlattrs.add(fl);
+ else
+ break;
+ }
+ }
+
+ if (BuildConfig.DEBUG) {
+ Log.d(TAG, "Got my flattrs list of length " + Integer.toString(myFlattrs.size()) + " comparison date" + firstOfMonthDate);
+
+ for (Flattr fl : myFlattrs) {
+ Thing thing = fl.getThing();
+ Log.d(TAG, "Flattr thing: " + fl.getThingId() + " name: " + thing.getTitle() + " url: " + thing.getUrl() + " on: " + fl.getCreated());
+ }
+ }
+
+ } else {
+ Log.e(TAG, "retrieveFlattrdThings was called with null access token");
+ }
+
+ return myFlattrs;
+ }
+
+ public static void handleCallback(Context context, Uri uri) {
+ AndroidAuthenticator auth = createAuthenticator();
+ new FlattrTokenFetcher(context, auth, uri).executeAsync();
+ }
+
+ public static void revokeAccessToken(Context context) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Revoking access token");
+ deleteToken();
+ FlattrServiceCreator.deleteFlattrService();
+ showRevokeDialog(context);
DBWriter.clearAllFlattrStatus(context);
}
- // ------------------------------------------------ DIALOGS
-
- public static void showRevokeDialog(final Context context) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.access_revoked_title);
- builder.setMessage(R.string.access_revoked_info);
- builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
- builder.create().show();
- }
-
- public static void showNoTokenDialog(final Context context, final String url) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating showNoTokenDialog");
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.no_flattr_token_title);
- builder.setMessage(R.string.no_flattr_token_msg);
- builder.setPositiveButton(R.string.authenticate_now_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- context.startActivity(new Intent(context,
- FlattrAuthActivity.class));
- }
-
- });
- builder.setNegativeButton(R.string.visit_website_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri.parse(url);
- context.startActivity(new Intent(Intent.ACTION_VIEW,
- uri));
- }
-
- });
- builder.create().show();
- }
-
- public static void showForbiddenDialog(final Context context,
- final String url) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.action_forbidden_title);
- builder.setMessage(R.string.action_forbidden_msg);
- builder.setPositiveButton(R.string.authenticate_now_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- context.startActivity(new Intent(context,
- FlattrAuthActivity.class));
- }
-
- });
- builder.setNegativeButton(R.string.visit_website_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri.parse(url);
- context.startActivity(new Intent(Intent.ACTION_VIEW,
- uri));
- }
-
- });
- builder.create().show();
- }
-
- public static void showErrorDialog(final Context context, final String msg) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.error_label);
- builder.setMessage(msg);
- builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
- builder.create().show();
- }
+ // ------------------------------------------------ DIALOGS
+
+ public static void showRevokeDialog(final Context context) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.access_revoked_title);
+ builder.setMessage(R.string.access_revoked_info);
+ builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.create().show();
+ }
+
+ /**
+ * Opens a dialog that ask the user to either connect the app with flattr or to be redirected to
+ * the thing's website.
+ * If no API credentials are available, the user will immediately be redirected to the thing's website.
+ * */
+ public static void showNoTokenDialogOrRedirect(final Context context, final String url) {
+ if (hasAPICredentials()) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.no_flattr_token_title);
+ builder.setMessage(R.string.no_flattr_token_msg);
+ builder.setPositiveButton(R.string.authenticate_now_label,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ context.startActivity(new Intent(context,
+ FlattrAuthActivity.class));
+ }
+
+ }
+ );
+
+ builder.setNegativeButton(R.string.visit_website_label,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uri uri = Uri.parse(url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW,
+ uri));
+ }
+
+ }
+ );
+ builder.create().show();
+ } else {
+ Uri uri = Uri.parse(url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ }
+
+ public static void showForbiddenDialog(final Context context,
+ final String url) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.action_forbidden_title);
+ builder.setMessage(R.string.action_forbidden_msg);
+ builder.setPositiveButton(R.string.authenticate_now_label,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ context.startActivity(new Intent(context,
+ FlattrAuthActivity.class));
+ }
+
+ }
+ );
+ builder.setNegativeButton(R.string.visit_website_label,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uri uri = Uri.parse(url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW,
+ uri));
+ }
+
+ }
+ );
+ builder.create().show();
+ }
+
+ public static void showErrorDialog(final Context context, final String msg) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.error_label);
+ builder.setMessage(msg);
+ builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.create().show();
+ }
} \ No newline at end of file
diff --git a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
index 390498cea..3f6e6ae0a 100644
--- a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
+++ b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
@@ -3,12 +3,14 @@ package de.danoeh.antennapod.util.playback;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaMetadataRetriever;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.util.ChapterUtils;
+import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.Callable;
@@ -224,22 +226,12 @@ public class ExternalMedia implements Playable {
}
};
- @Override
- public InputStream openImageInputStream() {
- return new Playable.DefaultPlayableImageLoader(this)
- .openImageInputStream();
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return new Playable.DefaultPlayableImageLoader(this)
- .getImageLoaderCacheKey();
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- return new Playable.DefaultPlayableImageLoader(this)
- .reopenImageInputStream(input);
- }
-
+ @Override
+ public Uri getImageUri() {
+ if (localFileAvailable()) {
+ return new Uri.Builder().scheme(SCHEME_MEDIA).encodedPath(getLocalMediaUrl()).build();
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/util/playback/IPlayer.java b/src/de/danoeh/antennapod/util/playback/IPlayer.java
index 99f53fb52..2d4551b13 100644
--- a/src/de/danoeh/antennapod/util/playback/IPlayer.java
+++ b/src/de/danoeh/antennapod/util/playback/IPlayer.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.util.playback;
+import android.content.Context;
import android.view.SurfaceHolder;
import java.io.IOException;
@@ -63,4 +64,6 @@ public interface IPlayer {
void stop();
public void setVideoScalingMode(int mode);
+
+ public void setWakeMode(Context context, int mode);
}
diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java
index 9ed45abfc..004ae56bb 100644
--- a/src/de/danoeh/antennapod/util/playback/Playable.java
+++ b/src/de/danoeh/antennapod/util/playback/Playable.java
@@ -2,27 +2,23 @@ package de.danoeh.antennapod.util.playback;
import android.content.Context;
import android.content.SharedPreferences;
-import android.media.MediaMetadataRetriever;
import android.os.Parcelable;
import android.util.Log;
-import de.danoeh.antennapod.asynctask.ImageLoader;
+
+import java.util.List;
+
+import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
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;
-import java.util.List;
/**
* Interface for objects that can be played by the PlaybackService.
*/
public interface Playable extends Parcelable,
- ImageLoader.ImageWorkerTaskResource, ShownotesProvider {
+ ShownotesProvider, PicassoImageResource {
/**
* Save information about the playable in a preference so that it can be
@@ -208,69 +204,4 @@ public interface Playable extends Parcelable,
}
}
-
- /**
- * Uses local file as image resource if it is available.
- */
- public static class DefaultPlayableImageLoader implements
- ImageLoader.ImageWorkerTaskResource {
- private Playable playable;
-
- public DefaultPlayableImageLoader(Playable playable) {
- Validate.notNull(playable);
-
- this.playable = playable;
- }
-
- @Override
- public InputStream openImageInputStream() {
- if (playable.localFileAvailable()
- && playable.getLocalMediaUrl() != null) {
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- try {
- mmr.setDataSource(playable.getLocalMediaUrl());
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- return null;
- }
- byte[] imgData = mmr.getEmbeddedPicture();
- if (imgData != null) {
- return new PublicByteArrayInputStream(imgData);
-
- }
- }
- return null;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return playable.getLocalMediaUrl();
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- if (input instanceof PublicByteArrayInputStream) {
- IOUtils.closeQuietly(input);
- byte[] imgData = ((PublicByteArrayInputStream) input)
- .getByteArray();
- if (imgData != null) {
- ByteArrayInputStream out = new ByteArrayInputStream(imgData);
- return out;
- }
-
- }
- return null;
- }
-
- private static class PublicByteArrayInputStream extends
- ByteArrayInputStream {
- public PublicByteArrayInputStream(byte[] buf) {
- super(buf);
- }
-
- public byte[] getByteArray() {
- return buf;
- }
- }
- }
}
diff --git a/src/instrumentationTest/de/test/antennapod/ui/VideoplayerActivityTest.java b/src/instrumentationTest/de/test/antennapod/ui/VideoplayerActivityTest.java
new file mode 100644
index 000000000..807552571
--- /dev/null
+++ b/src/instrumentationTest/de/test/antennapod/ui/VideoplayerActivityTest.java
@@ -0,0 +1,38 @@
+package instrumentationTest.de.test.antennapod.ui;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.robotium.solo.Solo;
+
+import de.danoeh.antennapod.activity.VideoplayerActivity;
+
+/**
+ * Test class for VideoplayerActivity
+ */
+public class VideoplayerActivityTest extends ActivityInstrumentationTestCase2<VideoplayerActivity> {
+
+ private Solo solo;
+
+ public VideoplayerActivityTest() {
+ super(VideoplayerActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ solo = new Solo(getInstrumentation(), getActivity());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ solo.finishOpenedActivities();
+ super.tearDown();
+ }
+
+ /**
+ * Test if activity can be started.
+ */
+ public void testStartActivity() throws Exception {
+ solo.waitForActivity(VideoplayerActivity.class);
+ }
+}
diff --git a/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java b/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
index 91e5d966f..08fd0d486 100644
--- a/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
+++ b/src/instrumentationTest/de/test/antennapod/util/URLCheckerTest.java
@@ -38,7 +38,7 @@ public class URLCheckerTest extends AndroidTestCase {
assertEquals("http://example.com", out);
}
- public void testItcpProtocol() {
+ public void testItpcProtocol() {
final String in = "itpc://example.com";
final String out = URLChecker.prepareURL(in);
assertEquals("http://example.com", out);