summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tx/config1
-rw-r--r--app/build.gradle2
-rw-r--r--app/proguard.cfg7
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java32
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java33
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java2
-rw-r--r--app/src/main/AndroidManifest.xml8
-rw-r--r--app/src/main/assets/LICENSE_GLIDE.txt94
-rw-r--r--app/src/main/assets/LICENSE_PICASSO.txt13
-rw-r--r--app/src/main/assets/LICENSE_STACKBLUR.txt71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/AppConfig.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java69
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java43
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java17
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml42
-rw-r--r--app/src/main/res/layout/queue_listitem.xml37
-rw-r--r--app/src/main/res/xml/preferences.xml13
-rw-r--r--app/src/main/templates/about.html7
-rw-r--r--core/build.gradle3
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java (renamed from core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java)2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java533
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java141
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java267
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java53
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java58
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Converter.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java31
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java49
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java4
-rw-r--r--core/src/main/res/values-it-rIT/strings.xml1
-rw-r--r--core/src/main/res/values-no/strings.xml30
-rw-r--r--core/src/main/res/values-tr/strings.xml48
-rw-r--r--core/src/main/res/values/arrays.xml38
-rw-r--r--core/src/main/res/values/colors.xml1
-rw-r--r--core/src/main/res/values/strings.xml29
-rw-r--r--core/src/main/res/values/styles.xml4
74 files changed, 1400 insertions, 1147 deletions
diff --git a/.tx/config b/.tx/config
index b0bb46170..b2e179dc6 100644
--- a/.tx/config
+++ b/.tx/config
@@ -18,6 +18,7 @@ trans.it_IT = core/src/main/res/values-it-rIT/strings.xml
trans.ja = core/src/main/res/values-ja/strings.xml
trans.ko = core/src/main/res/values-ko/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml
+trans.no = core/src/main/res/values-no/strings.xml
trans.pl_PL = core/src/main/res/values-pl-rPL/strings.xml
trans.pt = core/src/main/res/values-pt/strings.xml
trans.pt_BR = core/src/main/res/values-pt-rBR/strings.xml
diff --git a/app/build.gradle b/app/build.gradle
index daeb9ca39..cd6fbd354 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,7 +16,7 @@ dependencies {
}
compile 'commons-io:commons-io:2.4'
compile 'org.jsoup:jsoup:1.7.3'
- compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
compile 'com.squareup.okio:okio:1.2.0'
diff --git a/app/proguard.cfg b/app/proguard.cfg
index 536f54eca..6020b5751 100644
--- a/app/proguard.cfg
+++ b/app/proguard.cfg
@@ -89,3 +89,10 @@
# android-iconify
-keep class com.joanzapata.** { *; }
+
+# Glide
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
+ **[] $VALUES;
+ public *;
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
index 1b77baea0..11a34813a 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -42,38 +42,6 @@ public class DBReaderTest extends InstrumentationTestCase {
adapter.close();
}
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBReader.getExpiredFeedsList(context, expirationTime);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
- }
-
- public void testGetExpiredFeedsListShouldReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- public void testGetExpiredFeedsListShouldNotReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
-
-
public void testGetFeedList() {
final Context context = getInstrumentation().getTargetContext();
List<Feed> feeds = saveFeedlist(context, 10, 0, false);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
index ac4a709d7..944fc7792 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -299,37 +299,4 @@ public class DBTasksTest extends InstrumentationTestCase {
lastDate = item.getPubDate();
}
}
-
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- UserPreferences.setUpdateInterval(TimeUnit.MILLISECONDS.toHours(expirationTime));
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBTasks.getExpiredFeeds(context);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldNotReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 0326174e3..65c962f01 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -78,7 +78,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
openNavDrawer();
solo.clickOnText(solo.getString(R.string.add_feed_label));
solo.enterText(0, feed.getDownload_url());
- solo.clickOnButton(0);
+ solo.clickOnButton(solo.getString(R.string.confirm_label));
solo.waitForActivity(DefaultOnlineFeedViewActivity.class);
solo.waitForView(R.id.butSubscribe);
assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 24c9c2726..d59cca51c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/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="1030002"
- android:versionName="1.3-RC2">
+ android:versionCode="1030004"
+ android:versionName="1.3-RC4">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
@@ -327,6 +327,10 @@
android:scheme="package"/>
</intent-filter>
</receiver>
+
+ <meta-data
+ android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
+ android:value="GlideModule" />
</application>
</manifest>
diff --git a/app/src/main/assets/LICENSE_GLIDE.txt b/app/src/main/assets/LICENSE_GLIDE.txt
new file mode 100644
index 000000000..f5111eeab
--- /dev/null
+++ b/app/src/main/assets/LICENSE_GLIDE.txt
@@ -0,0 +1,94 @@
+License for everything not in third_party and not otherwise marked:
+
+Copyright 2014 Google, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Google, Inc.
+---------------------------------------------------------------------------------------------
+License for third_party/disklrucache:
+
+Copyright 2012 Jake Wharton
+Copyright 2011 The Android Open Source Project
+
+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.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_decoder:
+
+Copyright (c) 2013 Xcellent Creations, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_encoder/AnimatedGifEncoder.java and
+third_party/gif_encoder/LZWEncoder.java:
+
+No copyright asserted on the source code of this class. May be used for any
+purpose, however, refer to the Unisys LZW patent for restrictions on use of
+the associated LZWEncoder class. Please forward any corrections to
+kweiner@fmsware.com.
+
+-----------------------------------------------------------------------------
+License for third_party/gif_encoder/NeuQuant.java
+
+Copyright (c) 1994 Anthony Dekker
+
+NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+"Kohonen neural networks for optimal colour quantization" in "Network:
+Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+the algorithm.
+
+Any party obtaining a copy of these files from the author, directly or
+indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+this software and documentation files (the "Software"), including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons who
+receive copies from any such party to do so, with the only requirement being
+that this copyright notice remain intact.
diff --git a/app/src/main/assets/LICENSE_PICASSO.txt b/app/src/main/assets/LICENSE_PICASSO.txt
deleted file mode 100644
index 0bf6b9f8e..000000000
--- a/app/src/main/assets/LICENSE_PICASSO.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-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/app/src/main/assets/LICENSE_STACKBLUR.txt b/app/src/main/assets/LICENSE_STACKBLUR.txt
new file mode 100644
index 000000000..b66736ccb
--- /dev/null
+++ b/app/src/main/assets/LICENSE_STACKBLUR.txt
@@ -0,0 +1,71 @@
+Apache License, Version 2.0
+
+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:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+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
+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/app/src/main/java/de/danoeh/antennapod/AppConfig.java b/app/src/main/java/de/danoeh/antennapod/AppConfig.java
deleted file mode 100644
index 24f13d4a3..000000000
--- a/app/src/main/java/de/danoeh/antennapod/AppConfig.java
+++ /dev/null
@@ -1,7 +0,0 @@
-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.4";
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 4b709ab74..f47fe3944 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -4,11 +4,11 @@ import android.app.Application;
import android.content.Intent;
import android.content.res.Configuration;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.FeedMediaSizeService;
+import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.spa.SPAUtil;
/** Main application class. */
@@ -39,9 +39,10 @@ public class PodcastApp extends Application {
singleton = this;
LOGICAL_DENSITY = getResources().getDisplayMetrics().density;
- PicassoProvider.setupPicassoInstance(this);
+ UpdateManager.init(this);
UserPreferences.init(this);
PlaybackPreferences.init(this);
+ NetworkUtils.init(this);
EventDistributor.getInstance();
SPAUtil.sendSPAppsQueryFeedsIntent(this);
diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
new file mode 100644
index 000000000..2f6bb1b50
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
@@ -0,0 +1,88 @@
+package de.danoeh.antennapod;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.io.File;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+/*
+ * This class's job is do perform maintenance tasks whenever the app has been updated
+ */
+public class UpdateManager {
+
+ public static final String TAG = UpdateManager.class.getSimpleName();
+
+ private static final String PREF_NAME = "app_version";
+ private static final String KEY_VERSION_CODE = "version_code";
+
+ private static int currentVersionCode;
+
+ private static Context context;
+ private static SharedPreferences prefs;
+
+ public static void init(Context context) {
+ UpdateManager.context = context;
+ prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
+ currentVersionCode = info.versionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to obtain package info for package name: " + context.getPackageName(), e);
+ currentVersionCode = 0;
+ return;
+ }
+ final int oldVersionCode = getStoredVersionCode();
+ Log.d(TAG, "old: " + oldVersionCode + ", current: " + currentVersionCode);
+ if(oldVersionCode < currentVersionCode) {
+ onUpgrade(oldVersionCode, currentVersionCode);
+ setCurrentVersionCode();
+ }
+ }
+
+ public static int getStoredVersionCode() {
+ return prefs.getInt(KEY_VERSION_CODE, -1);
+ }
+
+ public static void setCurrentVersionCode() {
+ prefs.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply();
+ }
+
+ private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
+ if(oldVersionCode < 1030099) {
+ // delete the now obsolete image cache
+ // from now on, Glide will handle caching images
+ new Thread() {
+ public void run() {
+ List<Feed> feeds = DBReader.getFeedList(context);
+ for (Feed podcast : feeds) {
+ List<FeedItem> episodes = DBReader.getFeedItemList(context, podcast);
+ for (FeedItem episode : episodes) {
+ FeedImage image = episode.getImage();
+ if (image != null && image.isDownloaded() && image.getFile_url() != null) {
+ File imageFile = new File(image.getFile_url());
+ if (imageFile.exists()) {
+ imageFile.delete();
+ }
+ image.setFile_url(null); // calls setDownloaded(false)
+ DBWriter.setFeedImage(context, image);
+ }
+ }
+ }
+ }
+ }.start();
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index cae29db4b..28301313b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -27,7 +27,8 @@ import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
@@ -556,9 +557,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
txtvTitle.setText(media.getEpisodeTitle());
getSupportActionBar().setTitle("");
- Picasso.with(this)
+ Glide.with(this)
.load(media.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(butShowCover);
setNavButtonVisibility();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
index 287ae3568..9114fdd1a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
@@ -17,7 +17,8 @@ import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
@@ -124,9 +125,13 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) {
- Picasso.with(this)
+ Glide.with(this)
.load(feed.getImage().getDownload_url())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
index fae8baf65..4d3a43c56 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
@@ -14,21 +15,22 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AbsSpinner;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Spinner;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.TextView;
import android.widget.Toast;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -123,9 +125,13 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
public void run() {
- Picasso.with(FeedInfoActivity.this)
+ Glide.with(FeedInfoActivity.this)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
}
});
@@ -150,6 +156,9 @@ public class FeedInfoActivity extends ActionBarActivity {
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
feed.getPreferences().setAutoDownload(checked);
feed.savePreferences(FeedInfoActivity.this);
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
+ feed, checked);
+ dialog.createNewDialog().show();
}
});
spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
@@ -272,4 +281,25 @@ public class FeedInfoActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
}
+
+ private class ApplyToEpisodesDialog extends ConfirmationDialog {
+
+ private final Feed feed;
+ private final boolean autoDownload;
+
+ public ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.feed = feed;
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(context, feed, autoDownload);
+ }
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index a5ec02cf2..b83e9bc15 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -480,7 +480,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
checked = i;
}
choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_unit_seconds);
+ + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
@@ -528,7 +528,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
checked = i;
}
choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_unit_seconds);
+ + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
index bc3d3abbc..aebcc5e41 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
@@ -1,6 +1,8 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -11,7 +13,13 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+
+import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -72,6 +80,7 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.new_episodes_listitem,
parent, false);
+ holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
@@ -82,13 +91,15 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.findViewById(R.id.imgvInPlaylist);
holder.progress = (ProgressBar) convertView
.findViewById(R.id.pbar_progress);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
+ holder.placeholder.setVisibility(View.VISIBLE);
+ holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
if (showOnlyNewEpisodes || false == item.isNew()) {
@@ -141,14 +152,54 @@ public class AllEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
- .into(holder.imageView);
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover));
return convertView;
}
+ private class CoverTarget extends GlideDrawableImageViewTarget {
+
+ private final WeakReference<Uri> fallback;
+ private final WeakReference<TextView> placeholder;
+ private final WeakReference<ImageView> cover;
+
+ public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
+ super(imgvCover);
+ fallback = new WeakReference<>(fallbackUri);
+ placeholder = new WeakReference<>(txtvPlaceholder);
+ cover = new WeakReference<>(imgvCover);
+ }
+
+ @Override
+ public void onLoadFailed(Exception e, Drawable errorDrawable) {
+ Uri fallbackUri = fallback.get();
+ TextView txtvPlaceholder = placeholder.get();
+ ImageView imgvCover = cover.get();
+ if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
+ Glide.with(context)
+ .load(fallbackUri)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(null, txtvPlaceholder, imgvCover));
+ }
+ }
+
+ @Override
+ public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
+ super.onResourceReady(drawable, anim);
+ TextView txtvPlaceholder = placeholder.get();
+ if(txtvPlaceholder != null) {
+ txtvPlaceholder.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -159,11 +210,12 @@ public class AllEpisodesListAdapter extends BaseAdapter {
static class Holder {
+ TextView placeholder;
TextView title;
TextView pubDate;
View statusUnread;
ImageView queueStatus;
- ImageView imageView;
+ ImageView cover;
ProgressBar progress;
TextView txtvDuration;
ImageButton butSecondary;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index c34d90a30..c3486f2f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -12,11 +12,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -61,7 +58,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!isDownloading && !media.isDownloaded()) {
LongList queueIds = DBReader.getQueueIDList(context);
- if (NetworkUtils.isDownloadAllowed(context) || userAllowedMobileDownloads()) {
+ if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
try {
DBTasks.downloadFeedItems(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 15e0a7a33..0cf761503 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -10,7 +10,8 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -88,9 +89,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(holder.imageView);
return convertView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index 444d08da9..a063ad9b1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -15,7 +15,8 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.ArrayUtils;
@@ -259,9 +260,13 @@ public class NavListAdapter extends BaseAdapter
holder = (FeedHolder) convertView.getTag();
}
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
holder.title.setText(feed.getTitle());
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
index 431b645c6..81208749a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.text.format.DateUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,7 +14,13 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+
+import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -25,6 +34,7 @@ import de.danoeh.antennapod.core.util.Converter;
*/
public class QueueListAdapter extends BaseAdapter {
+ private static final String TAG = QueueListAdapter.class.getSimpleName();
private final Context context;
private final ItemAccess itemAccess;
@@ -76,7 +86,8 @@ public class QueueListAdapter extends BaseAdapter {
convertView = inflater.inflate(R.layout.queue_listitem,
parent, false);
holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
+ holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft);
@@ -86,7 +97,6 @@ public class QueueListAdapter extends BaseAdapter {
.findViewById(R.id.butSecondaryAction);
holder.progress = (ProgressBar) convertView
.findViewById(R.id.progressBar);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@@ -98,10 +108,11 @@ public class QueueListAdapter extends BaseAdapter {
holder.dragHandle.setVisibility(View.VISIBLE);
}
+ holder.placeholder.setText(item.getFeed().getTitle());
+
holder.title.setText(item.getTitle());
FeedMedia media = item.getMedia();
-
holder.title.setText(item.getTitle());
String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
holder.pubDate.setText(pubDate.replace(" ", "\n"));
@@ -144,14 +155,54 @@ public class QueueListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
- .into(holder.imageView);
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover));
return convertView;
}
+ private class CoverTarget extends GlideDrawableImageViewTarget {
+
+ private final WeakReference<Uri> fallback;
+ private final WeakReference<TextView> placeholder;
+ private final WeakReference<ImageView> cover;
+
+ public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
+ super(imgvCover);
+ fallback = new WeakReference<>(fallbackUri);
+ placeholder = new WeakReference<>(txtvPlaceholder);
+ cover = new WeakReference<>(imgvCover);
+ }
+
+ @Override
+ public void onLoadFailed(Exception e, Drawable errorDrawable) {
+ Uri fallbackUri = fallback.get();
+ TextView txtvPlaceholder = placeholder.get();
+ ImageView imgvCover = cover.get();
+ if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
+ Glide.with(context)
+ .load(fallbackUri)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(null, txtvPlaceholder, imgvCover));
+ }
+ }
+
+ @Override
+ public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
+ super.onResourceReady(drawable, anim);
+ TextView txtvPlaceholder = placeholder.get();
+ if(txtvPlaceholder != null) {
+ txtvPlaceholder.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -160,10 +211,10 @@ public class QueueListAdapter extends BaseAdapter {
}
};
-
static class Holder {
ImageView dragHandle;
- ImageView imageView;
+ ImageView cover;
+ TextView placeholder;
TextView title;
TextView pubDate;
TextView progressLeft;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index cedce7903..8c1464e97 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -8,7 +8,8 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
@@ -73,9 +74,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
@@ -86,9 +91,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle());
}
- Picasso.with(context)
+ Glide.with(context)
.load(item.getFeed().getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index b85709c5e..b9993b455 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -8,7 +8,8 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
@@ -49,9 +50,13 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
}
if (StringUtils.isNotBlank(podcast.getLogoUrl())) {
- Picasso.with(convertView.getContext())
+ Glide.with(convertView.getContext())
.load(podcast.getLogoUrl())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index 4fc2838b7..08ffdd197 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
@@ -1,23 +1,18 @@
package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.IOException;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -46,55 +41,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
this.context = context;
}
- /**
- * Updates the given ImageView with the image in the given Podcast's imageUrl
- */
- class FetchImageTask extends AsyncTask<Void,Void,Bitmap>{
- /**
- * Current podcast
- */
- private final Podcast podcast;
-
- /**
- * ImageView to be updated
- */
- private final ImageView imageView;
-
- /**
- * Constructor
- *
- * @param podcast Podcast that has the image
- * @param imageView UI image to be updated
- */
- FetchImageTask(Podcast podcast, ImageView imageView){
- this.podcast = podcast;
- this.imageView = imageView;
- }
-
- //Get the image from the url
- @Override
- protected Bitmap doInBackground(Void... params) {
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(podcast.imageUrl);
- try {
- HttpResponse response = client.execute(get);
- return BitmapFactory.decodeStream(response.getEntity().getContent());
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- //Set the background image for the podcast
- @Override
- protected void onPostExecute(Bitmap img) {
- super.onPostExecute(img);
- if(img!=null) {
- imageView.setImageBitmap(img);
- }
- }
- }
-
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Current podcast
@@ -121,7 +67,13 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
viewHolder.titleView.setText(podcast.title);
//Update the empty imageView with the image from the feed
- new FetchImageTask(podcast,viewHolder.coverView).execute();
+ Glide.with(context)
+ .load(podcast.imageUrl)
+ .placeholder(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate()
+ .into(viewHolder.coverView);
//Feed the grid view
return view;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
index 6561d501e..5c4d4c430 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
@@ -39,9 +39,9 @@ public abstract class TimeDialog extends Dialog {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
- String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds),
- context.getString(R.string.time_unit_minutes),
- context.getString(R.string.time_unit_hours)};
+ String[] spinnerContent = new String[]{context.getString(R.string.time_seconds),
+ context.getString(R.string.time_minutes),
+ context.getString(R.string.time_hours)};
setContentView(R.layout.time_dialog);
etxtTime = (EditText) findViewById(R.id.etxtTime);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 3076f8136..fbd27779d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -10,7 +10,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
@@ -80,8 +81,12 @@ public class CoverFragment extends Fragment implements
public void run() {
Context c = getActivity();
if (c != null) {
- Picasso.with(c)
+ Glide.with(c)
.load(media.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .dontAnimate()
.into(imgvCover);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 1679051f6..454fe35dd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -11,7 +11,8 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -197,9 +198,13 @@ public class ExternalPlayerFragment extends Fragment {
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(media.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index e0ed3d02c..90bf623da 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -35,7 +35,8 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.util.List;
@@ -336,9 +337,15 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
txtvTitle.setText(item.getTitle());
txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
- Picasso.with(getActivity()).load(item.getImageUri())
- .fit()
+ Glide.with(getActivity())
+ .load(item.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
+
progbarDownload.setVisibility(View.INVISIBLE);
if (item.hasMedia() && downloaderList != null) {
for (Downloader downloader : downloaderList) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index 2a1a7aaed..9b2fd0a7c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.LightingColorFilter;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -31,9 +32,10 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.joanzapata.android.iconify.IconDrawable;
import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
import org.apache.commons.lang3.Validate;
@@ -46,7 +48,6 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -56,6 +57,7 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -448,6 +450,10 @@ public class ItemlistFragment extends ListFragment {
}
private void refreshHeaderView() {
+ if (getListView() == null || feed == null) {
+ Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
+ return;
+ }
if(feed.hasLastUpdateFailed()) {
txtvFailure.setVisibility(View.VISIBLE);
} else {
@@ -505,17 +511,26 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
- Picasso.with(getActivity())
+
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+ Glide.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
- .transform(PicassoProvider.blurTransformation)
- .resize(PicassoProvider.BLUR_IMAGE_SIZE, PicassoProvider.BLUR_IMAGE_SIZE)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .transform(new FastBlurTransformation(getActivity()))
+ .dontAnimate()
.into(imgvBackground);
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
butShowInfo.setOnClickListener(new View.OnClickListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index ba4ababf5..24c9fc425 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -476,7 +476,7 @@ public class QueueFragment extends Fragment {
// refresh information bar
String info = queue.size() + getString(R.string.episodes_suffix);
if(queue.size() > 0) {
- int duration = 0;
+ long duration = 0;
for(FeedItem item : queue) {
if(item.getMedia() != null) {
duration += item.getMedia().getDuration();
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index 3a8abcc40..39edfe582 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -36,12 +36,9 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
-import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
@@ -52,7 +49,6 @@ import de.danoeh.antennapod.dialog.VariableSpeedDialog;
*/
public class PreferenceController {
private static final String TAG = "PreferenceController";
- public static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
@@ -108,23 +104,6 @@ public class PreferenceController {
);
}
- ui.findPreference(PreferenceController.PREF_FLATTR_THIS_APP).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- new FlattrClickWorker(activity,
- new SimpleFlattrThing(activity.getString(R.string.app_name),
- FlattrUtils.APP_URL,
- new FlattrStatus(FlattrStatus.STATUS_QUEUE)
- )
- ).executeAsync();
-
- return true;
- }
- }
- );
-
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@@ -364,6 +343,26 @@ public class PreferenceController {
return true;
}
});
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE)
+ .setOnPreferenceChangeListener(
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object o) {
+ if (o instanceof String) {
+ int newValue = Integer.valueOf((String) o) * 1024 * 1024;
+ if(newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+ );
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
@@ -429,7 +428,7 @@ public class PreferenceController {
entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
} else {
Integer v = Integer.parseInt(values[x]);
- entries[x] = v + " " + res.getString(R.string.time_unit_seconds);
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v);
}
}
pref.setEntries(entries);
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index f55a7603f..2615ec5c8 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -9,24 +9,20 @@ import android.util.Log;
import org.apache.commons.lang3.StringUtils;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.NetworkUtils;
public class ConnectivityActionReceiver extends BroadcastReceiver {
- private static final String TAG = "ConnectivityActionReceiver";
+ private static final String TAG = "ConnectivityActionRecvr";
@Override
public void onReceive(final Context context, Intent intent) {
if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received intent");
+ Log.d(TAG, "Received intent");
- if (NetworkUtils.autodownloadNetworkAvailable(context)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "auto-dl network available, starting auto-download");
+ if (NetworkUtils.autodownloadNetworkAvailable()) {
+ Log.d(TAG, "auto-dl network available, starting auto-download");
DBTasks.autodownloadUndownloadedItems(context);
} else { // if new network is Wi-Fi, finish ongoing downloads,
// otherwise cancel all downloads
@@ -34,12 +30,9 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null || ni.getType() != ConnectivityManager.TYPE_WIFI) {
- if (BuildConfig.DEBUG)
- Log.i(TAG,
- "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
+ Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
DownloadRequester.getInstance().cancelAllDownloads(context);
}
-
}
}
}
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index ff1318fc8..ec5ab1e6a 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -7,18 +7,36 @@
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_itemlist"
- android:layout_height="@dimen/thumbnail_length_itemlist"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_itemlist"
+ android:layout_height="@dimen/thumbnail_length_itemlist"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:background="@color/light_gray"
+ android:ellipsize="end"
+ android:maxLines="3"
+ android:gravity="center"/>
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignTop="@id/txtvPlaceholder"
+ android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignBottom="@id/txtvPlaceholder"
+ android:contentDescription="@string/cover_label"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark" />
+
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 39e9c72a5..38076ff51 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -21,17 +21,32 @@
tools:src="@drawable/ic_drag_handle"
tools:background="@android:color/holo_green_dark" />
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_queue_item"
- android:layout_height="@dimen/thumbnail_length_queue_item"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark"/>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_queue_item"
+ android:layout_height="@dimen/thumbnail_length_queue_item"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_gravity="center_vertical"
+ android:gravity="center"
+ android:background="@color/light_gray"
+ android:maxLines="3"
+ android:ellipsize="end"/>
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignTop="@id/txtvPlaceholder"
+ android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignBottom="@id/txtvPlaceholder"
+ android:contentDescription="@string/cover_label"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark"/>
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 9f93662de..35fb60c58 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -204,11 +204,13 @@
<Preference
android:title="@string/choose_data_directory"
android:key="prefChooseDataDir"/>
- <Preference
- android:key="prefFlattrThisApp"
- android:summary="@string/pref_flattr_this_app_sum"
- android:title="@string/pref_flattr_this_app_title">
- </Preference>
+ <ListPreference
+ android:entryValues="@array/image_cache_size_values"
+ android:entries="@array/image_cache_size_options"
+ android:title="@string/pref_image_cache_size_title"
+ android:key="prefImageCacheSize"
+ android:summary="@string/pref_image_cache_size_sum"
+ android:defaultValue="100"/>
<Preference
android:key="prefOpmlExport"
android:title="@string/opml_export_label"/>
@@ -216,7 +218,6 @@
android:key="prefAbout"
android:title="@string/about_pref"/>
-
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 7dd00cd16..2aca7ca4d 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -68,8 +68,11 @@ licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.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>
-<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>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
+licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
+
+<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.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>
diff --git a/core/build.gradle b/core/build.gradle
index ae2c11070..5e721bbc3 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -42,7 +42,8 @@ dependencies {
compile 'commons-io:commons-io:2.4'
compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
compile 'org.jsoup:jsoup:1.7.3'
- compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.github.bumptech.glide:glide:3.6.1'
+ compile 'com.github.bumptech.glide:okhttp-integration:1.3.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
compile 'com.squareup.okio:okio:1.2.0'
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
index 7429b50b3..2727b1447 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
+++ b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
@@ -13,6 +13,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithMicroseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 963);
Date actual = DateUtils.parse("2015-03-28T13:31:04.963870");
assertEquals(expected, actual);
@@ -20,6 +21,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithCentiseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 960);
Date actual = DateUtils.parse("2015-03-28T13:31:04.96");
assertEquals(expected, actual);
@@ -27,6 +29,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithDeciseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 900);
Date actual = DateUtils.parse("2015-03-28T13:31:04.9");
assertEquals(expected.getTime()/1000, actual.getTime()/1000);
@@ -82,4 +85,20 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ public void testAsctime() throws Exception {
+ GregorianCalendar exp = new GregorianCalendar(2011, 4, 25, 12, 33, 00);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date expected = new Date(exp.getTimeInMillis());
+ Date actual = DateUtils.parse("Wed, 25 May 2011 12:33:00");
+ assertEquals(expected, actual);
+ }
+
+ public void testMultipleConsecutiveSpaces() throws Exception {
+ GregorianCalendar exp = new GregorianCalendar(2010, 2, 23, 6, 6, 26);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date expected = new Date(exp.getTimeInMillis());
+ Date actual = DateUtils.parse("Tue, 23 Mar 2010 01:06:26 -0500");
+ assertEquals(expected, actual);
+ }
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
index 5d2d5d441..f91d4557e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
@@ -90,7 +90,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke
return ExitCode.NO_TOKEN;
}
- if (!NetworkUtils.networkAvailable(context)) {
+ if (!NetworkUtils.networkAvailable()) {
return ExitCode.NO_NETWORK;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java
index c0d8049db..edd69f15b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java
@@ -6,7 +6,7 @@ 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 {
+public interface ImageResource {
/**
* This scheme should be used by PicassoImageResources to
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
deleted file mode 100644
index 8e47a5b71..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
+++ /dev/null
@@ -1,533 +0,0 @@
-package de.danoeh.antennapod.core.asynctask;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.squareup.okhttp.Interceptor;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Response;
-import com.squareup.picasso.Cache;
-import com.squareup.picasso.LruCache;
-import com.squareup.picasso.OkHttpDownloader;
-import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Request;
-import com.squareup.picasso.RequestHandler;
-import com.squareup.picasso.Transformation;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import de.danoeh.antennapod.core.service.download.HttpDownloader;
-import de.danoeh.antennapod.core.storage.DBReader;
-
-/**
- * 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 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;
- }
-
- private static volatile boolean picassoSetup = false;
-
- public static synchronized void setupPicassoInstance(Context appContext) {
- if (picassoSetup) {
- return;
- }
- OkHttpClient client = new OkHttpClient();
- client.interceptors().add(new BasicAuthenticationInterceptor(appContext));
- Picasso picasso = new Picasso.Builder(appContext)
- .indicatorsEnabled(DEBUG)
- .loggingEnabled(DEBUG)
- .downloader(new OkHttpDownloader(client))
- .addRequestHandler(new MediaRequestHandler(appContext))
- .executor(getExecutorService())
- .memoryCache(getMemoryCache(appContext))
- .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();
- Picasso.setSingletonInstance(picasso);
- picassoSetup = true;
- }
-
- private static class BasicAuthenticationInterceptor implements Interceptor {
-
- private final Context context;
-
- public BasicAuthenticationInterceptor(Context context) {
- this.context = context;
- }
-
- @Override
- public Response intercept(Chain chain) throws IOException {
- com.squareup.okhttp.Request request = chain.request();
- String url = request.urlString();
- String authentication = DBReader.getImageAuthentication(context, url);
-
- if(TextUtils.isEmpty(authentication)) {
- Log.d(TAG, "no credentials for '" + url + "'");
- return chain.proceed(request);
- }
-
- // add authentication
- String[] auth = authentication.split(":");
- String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1");
- com.squareup.okhttp.Request newRequest = request
- .newBuilder()
- .addHeader("Authorization", credentials)
- .build();
- Log.d(TAG, "Basic authentication with ISO-8859-1 encoding");
- Response response = chain.proceed(newRequest);
- if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
- credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "UTF-8");
- newRequest = request
- .newBuilder()
- .addHeader("Authorization", credentials)
- .build();
- Log.d(TAG, "Basic authentication with UTF-8 encoding");
- return chain.proceed(newRequest);
- } else {
- return response;
- }
- }
- }
-
- private static class MediaRequestHandler extends RequestHandler {
-
- final Context context;
-
- public MediaRequestHandler(Context context) {
- super();
- this.context = context;
- }
-
- @Override
- public boolean canHandleRequest(Request data) {
- return StringUtils.equals(data.uri.getScheme(), PicassoImageResource.SCHEME_MEDIA);
- }
-
- @Override
- public Result load(Request data, int networkPolicy) throws IOException {
- Bitmap bitmap = null;
- MediaMetadataRetriever mmr = null;
- try {
- mmr = new MediaMetadataRetriever();
- mmr.setDataSource(data.uri.getPath());
- byte[] image = mmr.getEmbeddedPicture();
- if (image != null) {
- bitmap = decodeStreamFromByteArray(data, image);
- }
- } catch (RuntimeException e) {
- Log.e(TAG, "Failed to decode image in media file", e);
- } finally {
- if (mmr != null) {
- mmr.release();
- }
- }
-
- if (bitmap == null) {
- // this should never, happen, but sometimes it does, so fallback
- // check for fallback Uri
- String fallbackParam = data.uri.getQueryParameter(PicassoImageResource.PARAM_FALLBACK);
- if (fallbackParam != null) {
- Uri fallback = Uri.parse(fallbackParam);
- bitmap = decodeStreamFromFile(data, fallback);
- }
- }
-
- if (bitmap == null) {
- Log.e(TAG, "Could not load media");
- return null;
- }
-
- return new Result(bitmap, Picasso.LoadedFrom.DISK);
-
- }
-
- /* Copied/Adapted from Picasso RequestHandler classes */
-
- private Bitmap decodeStreamFromByteArray(Request data, byte[] bytes) throws IOException {
-
- final BitmapFactory.Options options = createBitmapOptions(data);
- final ByteArrayInputStream in = new ByteArrayInputStream(bytes);
- in.mark(0);
- if (requiresInSampleSize(options)) {
- try {
- BitmapFactory.decodeStream(in, null, options);
- } finally {
- in.reset();
- }
- calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
- }
- try {
- return BitmapFactory.decodeStream(in, null, options);
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- private Bitmap decodeStreamFromFile(Request data, Uri uri) throws IOException {
- ContentResolver contentResolver = context.getContentResolver();
- final BitmapFactory.Options options = createBitmapOptions(data);
- if (requiresInSampleSize(options)) {
- InputStream is = null;
- try {
- is = contentResolver.openInputStream(uri);
- BitmapFactory.decodeStream(is, null, options);
- } finally {
- IOUtils.closeQuietly(is);
- }
- calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
- }
- InputStream is = contentResolver.openInputStream(uri);
- try {
- return BitmapFactory.decodeStream(is, null, options);
- } finally {
- IOUtils.closeQuietly(is);
- }
- }
-
- private BitmapFactory.Options createBitmapOptions(Request data) {
- final boolean justBounds = data.hasSize();
- final boolean hasConfig = data.config != null;
- BitmapFactory.Options options = null;
- if (justBounds || hasConfig) {
- options = new BitmapFactory.Options();
- options.inJustDecodeBounds = justBounds;
- if (hasConfig) {
- options.inPreferredConfig = data.config;
- }
- }
- return options;
- }
-
- private static boolean requiresInSampleSize(BitmapFactory.Options options) {
- return options != null && options.inJustDecodeBounds;
- }
-
- private static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options,
- Request request) {
- calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options,
- request);
- }
-
- private static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
- BitmapFactory.Options options, Request request) {
- int sampleSize = 1;
- if (height > reqHeight || width > reqWidth) {
- final int heightRatio;
- final int widthRatio;
- if (reqHeight == 0) {
- sampleSize = (int) Math.floor((float) width / (float) reqWidth);
- } else if (reqWidth == 0) {
- sampleSize = (int) Math.floor((float) height / (float) reqHeight);
- } else {
- heightRatio = (int) Math.floor((float) height / (float) reqHeight);
- widthRatio = (int) Math.floor((float) width / (float) reqWidth);
- sampleSize = request.centerInside
- ? Math.max(heightRatio, widthRatio)
- : Math.min(heightRatio, widthRatio);
- }
- }
- options.inSampleSize = sampleSize;
- options.inJustDecodeBounds = false;
- }
- }
-
- public static final int BLUR_RADIUS = 1;
- public static final int BLUR_IMAGE_SIZE = 100;
- public static final String BLUR_KEY = "blur";
-
- public static final Transformation blurTransformation = new Transformation() {
- @Override
- public Bitmap transform(Bitmap source) {
- Bitmap result = fastblur(source, BLUR_RADIUS);
- if (result == null) {
- // just return the original
- // for some reason we couldn't transform it.
- return source;
- }
- source.recycle();
- return result;
- }
-
- @Override
- public String key() {
- return BLUR_KEY;
- }
- };
-
- public static Bitmap fastblur(Bitmap sentBitmap, int radius) {
-
- // Stack Blur v1.0 from
- // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
- //
- // Java Author: Mario Klingemann <mario at quasimondo.com>
- // http://incubator.quasimondo.com
- // created Feburary 29, 2004
- // Android port : Yahel Bouaziz <yahel at kayenko.com>
- // http://www.kayenko.com
- // ported april 5th, 2012
-
- // This is a compromise between Gaussian Blur and Box blur
- // It creates much better looking blurs than Box Blur, but is
- // 7x faster than my Gaussian Blur implementation.
- //
- // I called it Stack Blur because this describes best how this
- // filter works internally: it creates a kind of moving stack
- // of colors whilst scanning through the image. Thereby it
- // just has to add one new block of color to the right side
- // of the stack and remove the leftmost color. The remaining
- // colors on the topmost layer of the stack are either added on
- // or reduced by one, depending on if they are on the right or
- // on the left side of the stack.
- //
- // If you are using this algorithm in your code please add
- // the following line:
- //
- // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
- Bitmap.Config config = sentBitmap.getConfig();
- if (config == null) {
- // Sometimes the config can be null, in those cases
- // we don't do a transform.
- return null;
- }
-
- Bitmap bitmap = sentBitmap.copy(config, true);
-
- if (radius < 1) {
- return (null);
- }
-
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
-
- int[] pix = new int[w * h];
- Log.e("pix", w + " " + h + " " + pix.length);
- bitmap.getPixels(pix, 0, w, 0, 0, w, h);
-
- int wm = w - 1;
- int hm = h - 1;
- int wh = w * h;
- int div = radius + radius + 1;
-
- int r[] = new int[wh];
- int g[] = new int[wh];
- int b[] = new int[wh];
- int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
- int vmin[] = new int[Math.max(w, h)];
-
- int divsum = (div + 1) >> 1;
- divsum *= divsum;
- int dv[] = new int[256 * divsum];
- for (i = 0; i < 256 * divsum; i++) {
- dv[i] = (i / divsum);
- }
-
- yw = yi = 0;
-
- int[][] stack = new int[div][3];
- int stackpointer;
- int stackstart;
- int[] sir;
- int rbs;
- int r1 = radius + 1;
- int routsum, goutsum, boutsum;
- int rinsum, ginsum, binsum;
-
- for (y = 0; y < h; y++) {
- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
- for (i = -radius; i <= radius; i++) {
- p = pix[yi + Math.min(wm, Math.max(i, 0))];
- sir = stack[i + radius];
- sir[0] = (p & 0xff0000) >> 16;
- sir[1] = (p & 0x00ff00) >> 8;
- sir[2] = (p & 0x0000ff);
- rbs = r1 - Math.abs(i);
- rsum += sir[0] * rbs;
- gsum += sir[1] * rbs;
- bsum += sir[2] * rbs;
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- }
- }
- stackpointer = radius;
-
- for (x = 0; x < w; x++) {
-
- r[yi] = dv[rsum];
- g[yi] = dv[gsum];
- b[yi] = dv[bsum];
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
-
- if (y == 0) {
- vmin[x] = Math.min(x + radius + 1, wm);
- }
- p = pix[yw + vmin[x]];
-
- sir[0] = (p & 0xff0000) >> 16;
- sir[1] = (p & 0x00ff00) >> 8;
- sir[2] = (p & 0x0000ff);
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[(stackpointer) % div];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
-
- yi++;
- }
- yw += w;
- }
- for (x = 0; x < w; x++) {
- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
- yp = -radius * w;
- for (i = -radius; i <= radius; i++) {
- yi = Math.max(0, yp) + x;
-
- sir = stack[i + radius];
-
- sir[0] = r[yi];
- sir[1] = g[yi];
- sir[2] = b[yi];
-
- rbs = r1 - Math.abs(i);
-
- rsum += r[yi] * rbs;
- gsum += g[yi] * rbs;
- bsum += b[yi] * rbs;
-
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- }
-
- if (i < hm) {
- yp += w;
- }
- }
- yi = x;
- stackpointer = radius;
- for (y = 0; y < h; y++) {
- // Preserve alpha channel: ( 0xff000000 & pix[yi] )
- pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
-
- if (x == 0) {
- vmin[y] = Math.min(y + r1, hm) * w;
- }
- p = x + vmin[y];
-
- sir[0] = r[p];
- sir[1] = g[p];
- sir[2] = b[p];
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[stackpointer];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
-
- yi += w;
- }
- }
-
- Log.e("pix", w + " " + h + " " + pix.length);
- bitmap.setPixels(pix, 0, w, 0, 0, w, h);
-
- return (bitmap);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
index ba1add895..fea2bbb2b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
@@ -4,7 +4,7 @@ import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
-import de.danoeh.antennapod.core.BuildConfig;
+
import de.danoeh.antennapod.core.R;
/**
@@ -12,12 +12,16 @@ import de.danoeh.antennapod.core.R;
* classes can handle events like confirmation or cancellation.
*/
public abstract class ConfirmationDialog {
- private static final String TAG = "ConfirmationDialog";
- Context context;
+ private static final String TAG = ConfirmationDialog.class.getSimpleName();
+
+ protected Context context;
int titleId;
int messageId;
+ int positiveText;
+ int negativeText;
+
public ConfirmationDialog(Context context, int titleId, int messageId) {
this.context = context;
this.titleId = titleId;
@@ -25,18 +29,26 @@ public abstract class ConfirmationDialog {
}
public void onCancelButtonPressed(DialogInterface dialog) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Dialog was cancelled");
+ Log.d(TAG, "Dialog was cancelled");
dialog.dismiss();
}
+ public void setPositiveText(int id) {
+ this.positiveText = id;
+ }
+
+ public void setNegativeText(int id) {
+ this.negativeText = id;
+ }
+
+
public abstract void onConfirmButtonPressed(DialogInterface dialog);
public final AlertDialog createNewDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(titleId);
builder.setMessage(messageId);
- builder.setPositiveButton(R.string.confirm_label,
+ builder.setPositiveButton(positiveText != 0 ? positiveText : R.string.confirm_label,
new DialogInterface.OnClickListener() {
@Override
@@ -44,7 +56,7 @@ public abstract class ConfirmationDialog {
onConfirmButtonPressed(dialog);
}
});
- builder.setNegativeButton(R.string.cancel_label,
+ builder.setNegativeButton(negativeText != 0 ? negativeText : R.string.cancel_label,
new DialogInterface.OnClickListener() {
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index f6322d6f4..2a483ca9b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
@@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing;
*
* @author daniel
*/
-public class Feed extends FeedFile implements FlattrThing, PicassoImageResource {
+public class Feed extends FeedFile implements FlattrThing, ImageResource {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
public static final String TYPE_RSS091 = "rss";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
index c6f24367e..f77a78721 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
@@ -4,10 +4,10 @@ import android.net.Uri;
import java.io.File;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
-public class FeedImage extends FeedFile implements PicassoImageResource {
+public class FeedImage extends FeedFile implements ImageResource {
public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
index 96a6c2ba1..9229172f0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
@@ -10,7 +10,7 @@ import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
@@ -21,7 +21,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing;
*
* @author daniel
*/
-public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, PicassoImageResource {
+public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, ImageResource {
/**
* The id/guid that can be found in the rss/atom feed. Might not be set.
@@ -274,11 +274,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public void setContentEncoded(String contentEncoded) {
this.contentEncoded = contentEncoded;
}
-
- public void setFlattrStatus(FlattrStatus status) {
- this.flattrStatus = status;
- }
-
+
public FlattrStatus getFlattrStatus() {
return flattrStatus;
}
@@ -337,7 +333,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public Uri getImageUri() {
if(media != null && media.hasEmbeddedPicture()) {
return media.getImageUri();
- } else if (hasItemImageDownloaded()) {
+ } else if (image != null) {
return image.getImageUri();
} else if (feed != null) {
return feed.getImageUri();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
new file mode 100644
index 000000000..0baff9723
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -0,0 +1,33 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.load.DecodeFormat;
+import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+
+import java.io.InputStream;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule}
+ */
+public class ApGlideModule implements GlideModule {
+
+ @Override
+ public void applyOptions(Context context, GlideBuilder builder) {
+ builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
+ builder.setDiskCache(new InternalCacheDiskCacheFactory(context,
+ UserPreferences.getImageCacheSize()));
+ }
+
+ @Override
+ public void registerComponents(Context context, Glide glide) {
+ glide.register(GlideUrl.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
new file mode 100644
index 000000000..c7ac146b5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -0,0 +1,141 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Response;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.service.download.HttpDownloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+
+/**
+ * @see com.bumptech.glide.integration.okhttp.OkHttpUrlLoader
+ */
+public class ApOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
+
+ private static final String TAG = ApOkHttpUrlLoader.class.getSimpleName();
+
+ /**
+ * The default factory for {@link ApOkHttpUrlLoader}s.
+ */
+ public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
+
+ private static volatile OkHttpClient internalClient;
+ private OkHttpClient client;
+
+ private static OkHttpClient getInternalClient() {
+ if (internalClient == null) {
+ synchronized (Factory.class) {
+ if (internalClient == null) {
+ internalClient = AntennapodHttpClient.getHttpClient();
+ internalClient.interceptors().add(new NetworkAllowanceInterceptor());
+ internalClient.interceptors().add(new BasicAuthenticationInterceptor());
+ }
+ }
+ }
+ return internalClient;
+ }
+
+ /**
+ * Constructor for a new Factory that runs requests using a static singleton client.
+ */
+ public Factory() {
+ this(getInternalClient());
+ }
+
+ /**
+ * Constructor for a new Factory that runs requests using given client.
+ */
+ public Factory(OkHttpClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
+ return new ApOkHttpUrlLoader(client);
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing, this instance doesn't own the client.
+ }
+ }
+
+ private final OkHttpClient client;
+
+ public ApOkHttpUrlLoader(OkHttpClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
+ return new OkHttpStreamFetcher(client, model);
+ }
+
+ private static class NetworkAllowanceInterceptor implements Interceptor {
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ if (NetworkUtils.isDownloadAllowed()) {
+ return chain.proceed(chain.request());
+ } else {
+ return null;
+ }
+ }
+
+ }
+
+ private static class BasicAuthenticationInterceptor implements Interceptor {
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ com.squareup.okhttp.Request request = chain.request();
+ String url = request.urlString();
+ Context context = ClientConfig.applicationCallbacks.getApplicationInstance();
+ String authentication = DBReader.getImageAuthentication(context, url);
+
+ if(TextUtils.isEmpty(authentication)) {
+ Log.d(TAG, "no credentials for '" + url + "'");
+ return chain.proceed(request);
+ }
+
+ // add authentication
+ String[] auth = authentication.split(":");
+ String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1");
+ com.squareup.okhttp.Request newRequest = request
+ .newBuilder()
+ .addHeader("Authorization", credentials)
+ .build();
+ Log.d(TAG, "Basic authentication with ISO-8859-1 encoding");
+ Response response = chain.proceed(newRequest);
+ if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "UTF-8");
+ newRequest = request
+ .newBuilder()
+ .addHeader("Authorization", credentials)
+ .build();
+ Log.d(TAG, "Basic authentication with UTF-8 encoding");
+ return chain.proceed(newRequest);
+ } else {
+ return response;
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
new file mode 100644
index 000000000..ee58c2f39
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
@@ -0,0 +1,267 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.ThumbnailUtils;
+import android.util.Log;
+
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+
+public class FastBlurTransformation extends BitmapTransformation {
+
+ private static final String TAG = FastBlurTransformation.class.getSimpleName();
+
+ private static final int STACK_BLUR_RADIUS = 1;
+ private static final int BLUR_IMAGE_WIDTH = 150;
+
+ public FastBlurTransformation(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected Bitmap transform(BitmapPool pool, Bitmap source,
+ int outWidth, int outHeight) {
+ int targetWidth = BLUR_IMAGE_WIDTH;
+ int targetHeight = (int) (1.0 * outHeight * targetWidth / outWidth);
+ Bitmap resized = ThumbnailUtils.extractThumbnail(source, targetWidth, targetHeight);
+ Bitmap result = fastBlur(resized, STACK_BLUR_RADIUS);
+ if (result == null) {
+ Log.w(TAG, "result was null");
+ return source;
+ }
+ return result;
+ }
+
+ @Override
+ public String getId() {
+ return "FastBlurTransformation[width=" + BLUR_IMAGE_WIDTH + "px,radius=" + STACK_BLUR_RADIUS +"]";
+ }
+
+ private static Bitmap fastBlur(Bitmap bitmap, int radius) {
+
+ // Stack Blur v1.0 from
+ // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
+ //
+ // Java Author: Mario Klingemann <mario at quasimondo.com>
+ // http://incubator.quasimondo.com
+ // created Feburary 29, 2004
+ // Android port : Yahel Bouaziz <yahel at kayenko.com>
+ // http://www.kayenko.com
+ // ported april 5th, 2012
+
+ // This is a compromise between Gaussian Blur and Box blur
+ // It creates much better looking blurs than Box Blur, but is
+ // 7x faster than my Gaussian Blur implementation.
+ //
+ // I called it Stack Blur because this describes best how this
+ // filter works internally: it creates a kind of moving stack
+ // of colors whilst scanning through the image. Thereby it
+ // just has to add one new block of color to the right side
+ // of the stack and remove the leftmost color. The remaining
+ // colors on the topmost layer of the stack are either added on
+ // or reduced by one, depending on if they are on the right or
+ // on the left side of the stack.
+ //
+ // If you are using this algorithm in your code please add
+ // the following line:
+ //
+ // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
+
+ if (radius < 1) {
+ return null;
+ }
+
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+
+ int[] pix = new int[w * h];
+ bitmap.getPixels(pix, 0, w, 0, 0, w, h);
+
+ int wm = w - 1;
+ int hm = h - 1;
+ int wh = w * h;
+ int div = radius + radius + 1;
+
+ int r[] = new int[wh];
+ int g[] = new int[wh];
+ int b[] = new int[wh];
+ int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+ int vmin[] = new int[Math.max(w, h)];
+
+ int divsum = (div + 1) >> 1;
+ divsum *= divsum;
+ int dv[] = new int[256 * divsum];
+ for (i = 0; i < 256 * divsum; i++) {
+ dv[i] = (i / divsum);
+ }
+
+ yw = yi = 0;
+
+ int[][] stack = new int[div][3];
+ int stackpointer;
+ int stackstart;
+ int[] sir;
+ int rbs;
+ int r1 = radius + 1;
+ int routsum, goutsum, boutsum;
+ int rinsum, ginsum, binsum;
+
+ for (y = 0; y < h; y++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ for (i = -radius; i <= radius; i++) {
+ p = pix[yi + Math.min(wm, Math.max(i, 0))];
+ sir = stack[i + radius];
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+ rbs = r1 - Math.abs(i);
+ rsum += sir[0] * rbs;
+ gsum += sir[1] * rbs;
+ bsum += sir[2] * rbs;
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+ }
+ stackpointer = radius;
+
+ for (x = 0; x < w; x++) {
+
+ r[yi] = dv[rsum];
+ g[yi] = dv[gsum];
+ b[yi] = dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (y == 0) {
+ vmin[x] = Math.min(x + radius + 1, wm);
+ }
+ p = pix[yw + vmin[x]];
+
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[(stackpointer) % div];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi++;
+ }
+ yw += w;
+ }
+ for (x = 0; x < w; x++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ yp = -radius * w;
+ for (i = -radius; i <= radius; i++) {
+ yi = Math.max(0, yp) + x;
+
+ sir = stack[i + radius];
+
+ sir[0] = r[yi];
+ sir[1] = g[yi];
+ sir[2] = b[yi];
+
+ rbs = r1 - Math.abs(i);
+
+ rsum += r[yi] * rbs;
+ gsum += g[yi] * rbs;
+ bsum += b[yi] * rbs;
+
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+
+ if (i < hm) {
+ yp += w;
+ }
+ }
+ yi = x;
+ stackpointer = radius;
+ for (y = 0; y < h; y++) {
+ // Preserve alpha channel: ( 0xff000000 & pix[yi] )
+ pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (x == 0) {
+ vmin[y] = Math.min(y + r1, hm) * w;
+ }
+ p = x + vmin[y];
+
+ sir[0] = r[p];
+ sir[1] = g[p];
+ sir[2] = b[p];
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[stackpointer];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi += w;
+ }
+ }
+ bitmap.setPixels(pix, 0, w, 0, 0, w, h);
+ return bitmap;
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index c1b913c27..d56829c70 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -78,12 +78,15 @@ public class UserPreferences {
// Other
public static final String PREF_DATA_FOLDER = "prefDataFolder";
+ public static final String PREF_IMAGE_CACHE_SIZE = "prefImageCacheSize";
// Mediaplayer
public static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs";
private static final String PREF_REWIND_SECS = "prefRewindSecs";
public static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
+ public static final String IMAGE_CACHE_DEFAULT_VALUE = "100";
+ public static final int IMAGE_CACHE_SIZE_MINIMUM = 20;
// Constants
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
@@ -213,7 +216,6 @@ public class UserPreferences {
public static String getPlaybackSpeed() {
return prefs.getString(PREF_PLAYBACK_SPEED, "1.0");
-
}
public static String[] getPlaybackSpeedArray() {
@@ -278,6 +280,19 @@ public class UserPreferences {
return prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false);
}
+ public static int getImageCacheSize() {
+ String cacheSizeString = prefs.getString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE);
+ int cacheSizeInt = Integer.valueOf(cacheSizeString);
+ // if the cache size is too small the user won't get any images at all
+ // that's bad, force it back to the default.
+ if (cacheSizeInt < IMAGE_CACHE_SIZE_MINIMUM) {
+ prefs.edit().putString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE).apply();
+ cacheSizeInt = Integer.valueOf(IMAGE_CACHE_DEFAULT_VALUE);
+ }
+ int cacheSizeMB = cacheSizeInt * 1024 * 1024;
+ return cacheSizeMB;
+ }
+
public static int getFastFowardSecs() {
return prefs.getInt(PREF_FAST_FORWARD_SECS, 30);
}
@@ -588,7 +603,7 @@ public class UserPreferences {
Calendar alarm = (Calendar)now.clone();
alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay);
alarm.set(Calendar.MINUTE, minute);
- if(alarm.before(now)) {
+ if (alarm.before(now) || alarm.equals(now)) {
alarm.add(Calendar.DATE, 1);
}
Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis());
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index d98086003..b959c7301 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -19,8 +19,8 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
- if (NetworkUtils.isDownloadAllowed(context)) {
- DBTasks.refreshExpiredFeeds(context);
+ if (NetworkUtils.isDownloadAllowed()) {
+ DBTasks.refreshAllFeeds(context, null);
} else {
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java
index 44fb14f0c..2f6e67a28 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java
@@ -28,13 +28,13 @@ public class FeedMediaSizeService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent()");
- if(false == NetworkUtils.isDownloadAllowed(this)) {
+ if(false == NetworkUtils.isDownloadAllowed()) {
return;
}
List<FeedMedia> list = DBReader.getFeedMediaUnknownSize(this);
for (FeedMedia media : list) {
Log.d(TAG, "Getting size currently " + media.getSize() + " for " + media.getDownload_url());
- if(false == NetworkUtils.isDownloadAllowed(this)) {
+ if(false == NetworkUtils.isDownloadAllowed()) {
return;
}
long size = Integer.MIN_VALUE;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
index 3f2222f42..b80b4303f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
@@ -108,7 +108,7 @@ public class GpodnetSyncService extends Service {
private synchronized void sync() {
- if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable(this) == false) {
+ if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable() == false) {
stopSelf();
return;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index 578bce854..19a710d24 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -326,6 +326,7 @@ public class DownloadService extends Service {
unregisterReceiver(cancelDownloadReceiver);
if (!newMediaFiles.isEmpty()) {
+ Log.d(TAG, "newMediaFiles exist, autodownload them");
DBTasks.autodownloadUndownloadedItems(getApplicationContext(),
ArrayUtils.toPrimitive(newMediaFiles.toArray(new Long[newMediaFiles.size()])));
}
@@ -776,52 +777,18 @@ public class DownloadService extends Service {
for (int i = 0; i < savedFeeds.length; i++) {
Feed savedFeed = savedFeeds[i];
- // Download Feed Image if provided and not downloaded
- if (savedFeed.getImage() != null
- && savedFeed.getImage().isDownloaded() == false) {
- Log.d(TAG, "Feed has image; Downloading....");
- savedFeed.getImage().setOwner(savedFeed);
- final Feed savedFeedRef = savedFeed;
- try {
- requester.downloadImage(DownloadService.this,
- savedFeedRef.getImage());
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DBWriter.addDownloadStatus(
- DownloadService.this,
- new DownloadStatus(
- savedFeedRef.getImage(),
- savedFeedRef
- .getImage()
- .getHumanReadableIdentifier(),
- DownloadError.ERROR_REQUEST_ERROR,
- false, e.getMessage()
- )
- );
- }
- }
// queue new media files for automatic download
for (FeedItem item : savedFeed.getItems()) {
if(item.getPubDate() == null) {
Log.d(TAG, item.toString());
}
- if(item.getImage() != null && item.getImage().isDownloaded() == false) {
- item.getImage().setOwner(item);
- try {
- requester.downloadImage(DownloadService.this,
- item.getImage());
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- }
- }
if (!item.isPlayed() && item.hasMedia() && !item.getMedia().isDownloaded()) {
newMediaFiles.add(item.getMedia().getId());
}
}
// If loadAllPages=true, check if another page is available and queue it for download
-
final boolean loadAllPages = results.get(i).first.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
final Feed feed = results.get(i).second.feed;
if (loadAllPages && feed.getNextPageLink() != null) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 737f0aefd..aa1816e1c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -29,12 +29,16 @@ import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.widget.Toast;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.List;
+import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
@@ -769,7 +773,7 @@ public class PlaybackService extends Service {
/**
* Used by setupNotification to load notification data in another thread.
*/
- private AsyncTask<Void, Void, Void> notificationSetupTask;
+ private Thread notificationSetupThread;
/**
* Prepares notification and starts the service in the foreground.
@@ -780,50 +784,47 @@ public class PlaybackService extends Service {
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT);
- if (notificationSetupTask != null) {
- notificationSetupTask.cancel(true);
+ if (notificationSetupThread != null) {
+ notificationSetupThread.interrupt();
}
- notificationSetupTask = new AsyncTask<Void, Void, Void>() {
+ Runnable notificationSetupTask = new Runnable() {
Bitmap icon = null;
@Override
- protected Void doInBackground(Void... params) {
+ public void run() {
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);
try {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- icon = Picasso.with(PlaybackService.this)
+ icon = Glide.with(PlaybackService.this)
.load(info.playable.getImageUri())
- .resize(iconSize, iconSize)
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .into(-1, -1) // this resizing would not be exact, so we have
+ // scale the bitmap ourselves
.get();
- } catch (IOException e) {
+ icon = Bitmap.createScaledBitmap(icon, iconSize, iconSize, true);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
e.printStackTrace();
}
}
-
}
if (icon == null) {
icon = BitmapFactory.decodeResource(getApplicationContext().getResources(),
ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()));
}
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
if (mediaPlayer == null) {
return;
}
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
- if (!isCancelled() &&
- started &&
- info.playable != null) {
+ if (!Thread.currentThread().isInterrupted() && started && info.playable != null) {
String contentText = info.playable.getFeedTitle();
String contentTitle = info.playable.getEpisodeTitle();
Notification notification = null;
@@ -901,15 +902,9 @@ public class PlaybackService extends Service {
Log.d(TAG, "Notification set up");
}
}
-
};
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- notificationSetupTask
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- notificationSetupTask.execute();
- }
-
+ notificationSetupThread = new Thread(notificationSetupTask);
+ notificationSetupThread.start();
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
index 92de1eee7..0e6b23696 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
@@ -40,7 +40,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
public void run() {
// true if we should auto download based on network status
- boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context)
+ boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable()
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index 53fd8c24b..5ed0a6ee3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -115,35 +115,6 @@ public final class DBReader {
}
/**
- * Returns a list of 'expired Feeds', i.e. Feeds that have not been updated for a certain amount of time.
- *
- * @param context A context that is used for opening a database connection.
- * @param expirationTime Time that is used for determining whether a feed is outdated or not.
- * A Feed is considered expired if 'lastUpdate < (currentTime - expirationTime)' evaluates to true.
- * @return A list of Feeds, sorted alphabetically by their title. A Feed-object
- * of the returned list does NOT have its list of FeedItems yet. The FeedItem-list
- * can be loaded separately with {@link #getFeedItemList(android.content.Context, de.danoeh.antennapod.core.feed.Feed)}.
- */
- public static List<Feed> getExpiredFeedsList(final Context context, final long expirationTime) {
- Log.d(TAG, String.format("getExpiredFeedsList(%d)", expirationTime));
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
-
- Cursor feedlistCursor = adapter.getExpiredFeedsCursor(expirationTime);
- List<Feed> feeds = new ArrayList<Feed>(feedlistCursor.getCount());
-
- if (feedlistCursor.moveToFirst()) {
- do {
- Feed feed = extractFeedFromCursorRow(adapter, feedlistCursor);
- feeds.add(feed);
- } while (feedlistCursor.moveToNext());
- }
- feedlistCursor.close();
- return feeds;
- }
-
- /**
* Takes a list of FeedItems and loads their corresponding Feed-objects from the database.
* The feedID-attribute of a FeedItem must be set to the ID of its feed or the method will
* not find the correct feed of an item.
@@ -900,7 +871,11 @@ public final class DBReader {
if (cursor.moveToFirst()) {
String username = cursor.getString(0);
String password = cursor.getString(1);
- return username + ":" + password;
+ if(username != null && password != null) {
+ return username + ":" + password;
+ } else {
+ return "";
+ }
}
return "";
} finally {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index a90df433e..770c4f7d7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -184,6 +184,7 @@ public final class DBTasks {
if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
GpodnetSyncService.sendSyncIntent(context);
}
+ Log.d(TAG, "refreshAllFeeds autodownload");
autodownloadUndownloadedItems(context);
}
}.start();
@@ -192,44 +193,6 @@ public final class DBTasks {
}
}
- /**
- * Used by refreshExpiredFeeds to determine which feeds should be refreshed.
- * This method will use the value specified in the UserPreferences as the
- * expiration time.
- *
- * @param context Used for DB access.
- * @return A list of expired feeds. An empty list will be returned if there
- * are no expired feeds.
- */
- public static List<Feed> getExpiredFeeds(final Context context) {
- long millis = UserPreferences.getUpdateInterval();
- if (millis > 0) {
- List<Feed> feedList = DBReader.getExpiredFeedsList(context, millis);
- if (feedList.size() > 0) {
- refreshFeeds(context, feedList);
- }
- return feedList;
- } else {
- return new ArrayList<Feed>();
- }
- }
-
- /**
- * Refreshes expired Feeds in the list returned by the getExpiredFeedsList(Context, long) method in DBReader.
- * The expiration date parameter is determined by the update interval specified in {@link UserPreferences}.
- *
- * @param context Used for DB access.
- */
- public static void refreshExpiredFeeds(final Context context) {
- Log.d(TAG, "Refreshing expired feeds");
-
- new Thread() {
- public void run() {
- refreshFeeds(context, getExpiredFeeds(context));
- }
- }.start();
- }
-
private static void refreshFeeds(final Context context,
final List<Feed> feedList) {
@@ -321,24 +284,6 @@ public final class DBTasks {
}
/**
- * Notifies the database about a missing FeedImage file. This method will attempt to re-download the file.
- *
- * @param context Used for requesting the download.
- * @param image The FeedImage object.
- */
- public static void notifyInvalidImageFile(final Context context,
- final FeedImage image) {
- Log.i(TAG,
- "The DB was notified about an invalid image download. It will now try to re-download the image file");
- try {
- DownloadRequester.getInstance().downloadImage(context, image);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Log.w(TAG, "Failed to download invalid feed image");
- }
- }
-
- /**
* Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the
* DB and send a FeedUpdateBroadcast.
*/
@@ -440,6 +385,7 @@ public final class DBTasks {
* @return A Future that can be used for waiting for the methods completion.
*/
public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
+ Log.d(TAG, "autodownloadUndownloadedItems");
return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm()
.autoDownloadUndownloadedItems(context, mediaIds));
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index acdcf5f85..12bc208a6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -1110,9 +1110,34 @@ public class DBWriter {
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
}
});
+ }
+
+ /**
+ * Sets the 'auto_download'-attribute of specific FeedItem.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @param feed This feed's episodes will be processed.
+ * @param autoDownload If true, auto download will be enabled for the feed's episodes. Else,
+ * it will be disabled.
+ */
+ public static Future<?> setFeedsItemsAutoDownload(final Context context, final Feed feed,
+ final boolean autoDownload) {
+ Log.d(TAG, (autoDownload ? "Enabling" : "Disabling") + " auto download for items of feed " + feed.getId());
+ return dbExec.submit(new Runnable() {
+ @Override
+ public void run() {
+ final PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ adapter.setFeedsItemsAutoDownload(feed, autoDownload);
+ adapter.close();
+
+ EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ }
+ });
}
+
/**
* Set filter of the feed
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index ca6aa0178..bc34523cc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -18,7 +18,6 @@ import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFile;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -171,7 +170,7 @@ public class DownloadRequester {
if (feedFileValid(feed)) {
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
- long ifModifiedSince = feed.getLastUpdate().getTime();
+ long ifModifiedSince = feed.isPaged() ? 0 : feed.getLastUpdate().getTime();
Bundle args = new Bundle();
args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
@@ -186,15 +185,6 @@ public class DownloadRequester {
downloadFeed(context, feed, false);
}
- public synchronized void downloadImage(Context context, FeedImage image)
- throws DownloadRequestException {
- if (feedFileValid(image)) {
- FeedFile container = (image.getOwner() instanceof FeedFile) ? (FeedFile) image.getOwner() : null;
- download(context, image, container, new File(getImagefilePath(context),
- getImagefileName(image)), false, null, null, 0, false, null);
- }
- }
-
public synchronized void downloadMedia(Context context, FeedMedia feedmedia)
throws DownloadRequestException {
if (feedFileValid(feedmedia)) {
@@ -332,20 +322,6 @@ public class DownloadRequester {
return "feed-" + FileNameGenerator.generateFileName(filename);
}
- public synchronized String getImagefilePath(Context context)
- throws DownloadRequestException {
- return getExternalFilesDirOrThrowException(context, IMAGE_DOWNLOADPATH)
- .toString() + "/";
- }
-
- public synchronized String getImagefileName(FeedImage image) {
- String filename = image.getDownload_url();
- if (image.getOwner() != null && image.getOwner().getHumanReadableIdentifier() != null) {
- filename = image.getOwner().getHumanReadableIdentifier();
- }
- return "image-" + FileNameGenerator.generateFileName(filename);
- }
-
public synchronized String getMediafilePath(Context context, FeedMedia media)
throws DownloadRequestException {
File externalStorage = getExternalFilesDirOrThrowException(
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index 0153cbc8c..f9ac39349 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -861,6 +861,13 @@ public class PodDBAdapter {
new String[]{String.valueOf(feedItem.getId())});
}
+ public void setFeedsItemsAutoDownload(Feed feed, boolean autoDownload) {
+ final String sql = "UPDATE " + TABLE_NAME_FEED_ITEMS
+ + " SET " + KEY_AUTO_DOWNLOAD + "="+ (autoDownload ? "1" : "0")
+ + " WHERE " + KEY_FEED + "=" + feed.getId();
+ db.execSQL(sql);
+ }
+
public long getDownloadLogSize() {
final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_DOWNLOAD_LOG);
Cursor result = db.rawQuery(query, null);
@@ -981,13 +988,6 @@ public class PodDBAdapter {
return db.query(TABLE_NAME_FEEDS, new String[]{KEY_ID, KEY_DOWNLOAD_URL}, null, null, null, null, null);
}
- public final Cursor getExpiredFeedsCursor(long expirationTime) {
- Cursor c = db.query(TABLE_NAME_FEEDS, FEED_SEL_STD, KEY_LASTUPDATE + " < " + String.valueOf(System.currentTimeMillis() - expirationTime),
- null, null, null,
- null);
- return c;
- }
-
/**
* Returns a cursor with all FeedItems of a Feed. Uses FEEDITEM_SEL_FI_SMALL
*
@@ -1484,7 +1484,7 @@ public class PodDBAdapter {
*/
private static class PodDBHelper extends SQLiteOpenHelper {
- private final static int VERSION = 18;
+ private final static int VERSION = 1030002;
private Context context;
@@ -1709,6 +1709,11 @@ public class PodDBAdapter {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0");
}
+ if(oldVersion < 1030002) {
+ db.execSQL("UPDATE FeedItems SET auto_download=0 WHERE " +
+ "(read=1 OR id IN (SELECT id FROM FeedMedia WHERE position>0 OR downloaded=1)) " +
+ "AND id NOT IN (SELECT feeditem FROM Queue)");
+ }
EventBus.getDefault().post(ProgressEvent.end());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
index 917f99564..1b929b214 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
@@ -26,7 +26,7 @@ public final class Converter {
/** Determines the length of the number for best readability.*/
private static final int NUM_LENGTH = 1024;
-
+ private static final int DAYS_MIL = 86400000;
private static final int HOURS_MIL = 3600000;
private static final int MINUTES_MIL = 60000;
private static final int SECONDS_MIL = 1000;
@@ -104,18 +104,18 @@ public final class Converter {
}
/** Converts milliseconds to a localized string containing hours and minutes */
- public static String getDurationStringLocalized(Context context, int duration) {
- int h = duration / HOURS_MIL;
- int rest = duration - h * HOURS_MIL;
+ public static String getDurationStringLocalized(Context context, long duration) {
+ int h = (int)(duration / HOURS_MIL);
+ int rest = (int)(duration - h * HOURS_MIL);
int m = rest / MINUTES_MIL;
String result = "";
if(h > 0) {
- String hours = context.getString(R.string.time_unit_hours);
- result += h + " " + hours + " ";
+ String hours = context.getResources().getQuantityString(R.plurals.time_hours_quantified, h, h);
+ result += hours + " ";
}
- String minutes = context.getString(R.string.time_unit_minutes);
- result += m + " " + minutes;
+ String minutes = context.getResources().getQuantityString(R.plurals.time_minutes_quantified, m, m);
+ result += minutes;
return result;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
index 7b06128f9..c0233f684 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
@@ -8,6 +8,7 @@ import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
/**
* Parses several date formats.
@@ -17,50 +18,66 @@ public class DateUtils {
private static final String TAG = "DateUtils";
private static final SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
+ private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT");
+
static {
parser.setLenient(false);
+ parser.setTimeZone(defaultTimezone);
}
public static Date parse(final String input) {
if(input == null) {
- throw new IllegalArgumentException("Date most not be null");
+ throw new IllegalArgumentException("Date must not be null");
}
- String date = input.replace('/', '-');
+ String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " ");
+
+ // if datetime is more precise than seconds, make sure the value is in ms
if(date.contains(".")) {
int start = date.indexOf('.');
int current = start+1;
while(current < date.length() && Character.isDigit(date.charAt(current))) {
current++;
}
+ // even more precise than microseconds: discard further decimal places
if(current - start > 4) {
if(current < date.length()-1) {
date = date.substring(0, start + 4) + date.substring(current);
} else {
date = date.substring(0, start + 4);
}
+ // less than 4 decimal places: pad to have a consistent format for the parser
} else if(current - start < 4) {
if(current < date.length()-1) {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current);
} else {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start));
}
-
}
}
String[] patterns = {
"dd MMM yy HH:mm:ss Z",
"dd MMM yy HH:mm Z",
"EEE, dd MMM yyyy HH:mm:ss Z",
+ "EEE, dd MMM yyyy HH:mm:ss",
"EEE, dd MMMM yyyy HH:mm:ss Z",
+ "EEE, dd MMMM yyyy HH:mm:ss",
+ "EEEE, dd MMM yyyy HH:mm:ss Z",
"EEEE, dd MMM yy HH:mm:ss Z",
+ "EEEE, dd MMM yyyy HH:mm:ss",
+ "EEEE, dd MMM yy HH:mm:ss",
"EEE MMM d HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm Z",
+ "EEE, dd MMM yyyy HH:mm",
"EEE, dd MMMM yyyy HH:mm Z",
+ "EEE, dd MMMM yyyy HH:mm",
+ "EEEE, dd MMM yyyy HH:mm Z",
"EEEE, dd MMM yy HH:mm Z",
+ "EEEE, dd MMM yyyy HH:mm",
+ "EEEE, dd MMM yy HH:mm",
"EEE MMM d HH:mm yyyy",
"yyyy-MM-dd'T'HH:mm:ss",
- "yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSS Z",
+ "yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-ddZ",
@@ -77,7 +94,7 @@ public class DateUtils {
}
}
- Log.d(TAG, "Could not parse date string \"" + input + "\"");
+ Log.d(TAG, "Could not parse date string \"" + input + "\" [" + date + "]");
return null;
}
@@ -117,6 +134,7 @@ public class DateUtils {
public static String formatRFC3339UTC(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ format.setTimeZone(defaultTimezone);
return format.format(date);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index 3a349e221..9296039f0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -10,14 +10,16 @@ import android.util.Log;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
public class NetworkUtils {
- private static final String TAG = "NetworkUtils";
- private NetworkUtils() {
+ private static final String TAG = NetworkUtils.class.getSimpleName();
+ private static Context context;
+
+ public static void init(Context context) {
+ NetworkUtils.context = context;
}
/**
@@ -26,18 +28,16 @@ public class NetworkUtils {
* network that is on the 'selected networks' list of the Wi-Fi filter for
* automatic downloads and false otherwise.
* */
- public static boolean autodownloadNetworkAvailable(Context context) {
+ public static boolean autodownloadNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Device is connected to Wi-Fi");
+ Log.d(TAG, "Device is connected to Wi-Fi");
if (networkInfo.isConnected()) {
if (!UserPreferences.isEnableAutodownloadWifiFilter()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Auto-dl filter is disabled");
+ Log.d(TAG, "Auto-dl filter is disabled");
return true;
} else {
WifiManager wm = (WifiManager) context
@@ -48,31 +48,28 @@ public class NetworkUtils {
.getAutodownloadSelectedNetworks());
if (selectedNetworks.contains(Integer.toString(wifiInfo
.getNetworkId()))) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Current network is on the selected networks list");
+ Log.d(TAG, "Current network is on the selected networks list");
return true;
}
}
}
}
}
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Network for auto-dl is not available");
+ Log.d(TAG, "Network for auto-dl is not available");
return false;
}
- public static boolean networkAvailable(Context context) {
+ public static boolean networkAvailable() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && info.isConnected();
}
- public static boolean isDownloadAllowed(Context context) {
- return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi(context);
+ public static boolean isDownloadAllowed() {
+ return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi();
}
- public static boolean connectedToWifi(Context context) {
+ public static boolean connectedToWifi() {
ConnectivityManager connManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index c065a62c3..35916a604 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -21,11 +21,11 @@ public class ShareUtils {
}
public static void shareFeedlink(Context context, Feed feed) {
- shareLink(context, feed.getLink());
+ shareLink(context, feed.getTitle() + ": " + feed.getLink());
}
public static void shareFeedDownloadLink(Context context, Feed feed) {
- shareLink(context, feed.getDownload_url());
+ shareLink(context, feed.getTitle() + ": " + feed.getDownload_url());
}
public static void shareFeedItemLink(Context context, FeedItem item) {
@@ -36,24 +36,24 @@ public class ShareUtils {
shareFeedItemDownloadLink(context, item, false);
}
+ private static String getItemShareText(FeedItem item) {
+ return item.getFeed().getTitle() + ": " + item.getTitle();
+ }
+
public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) {
- String text;
+ String text = getItemShareText(item) + " " + item.getLink();
if(withPosition) {
int pos = item.getMedia().getPosition();
text = item.getLink() + " [" + Converter.getDurationStringLong(pos) + "]";
- } else {
- text = item.getLink();
}
shareLink(context, text);
}
public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) {
- String text;
+ String text = getItemShareText(item) + " " + item.getMedia().getDownload_url();
if(withPosition) {
int pos = item.getMedia().getPosition();
- text = item.getMedia().getDownload_url() + " [" + Converter.getDurationStringLong(pos) + "]";
- } else {
- text = item.getMedia().getDownload_url();
+ text += " [" + Converter.getDurationStringLong(pos) + "]";
}
shareLink(context, text);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
index 50792ae26..3b9e6120c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
@@ -42,12 +42,6 @@ public class FlattrUtils {
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() {
@@ -110,18 +104,6 @@ public class FlattrUtils {
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()) {
@@ -245,37 +227,6 @@ public class FlattrUtils {
}
}
- 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(
- ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context));
- }
-
- }
- );
- 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);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
index 7ebd580f7..752e95985 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
@@ -7,7 +7,7 @@ import android.util.Log;
import java.util.List;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -18,7 +18,7 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
* Interface for objects that can be played by the PlaybackService.
*/
public interface Playable extends Parcelable,
- ShownotesProvider, PicassoImageResource {
+ ShownotesProvider, ImageResource {
/**
* Save information about the playable in a preference so that it can be
diff --git a/core/src/main/res/values-it-rIT/strings.xml b/core/src/main/res/values-it-rIT/strings.xml
index b81e3f2ce..82c7395ff 100644
--- a/core/src/main/res/values-it-rIT/strings.xml
+++ b/core/src/main/res/values-it-rIT/strings.xml
@@ -126,6 +126,7 @@
<string name="download_error_unauthorized">Errore di autenticazione</string>
<string name="cancel_all_downloads_label">Annulla tutti i download</string>
<string name="download_canceled_msg">Download annullato</string>
+ <string name="download_report_title">Download completato con un errore (o errori)</string>
<string name="download_report_content_title">Rapporto del downoad</string>
<string name="download_error_malformed_url">URL malformato</string>
<string name="download_error_io_error">Errore IO</string>
diff --git a/core/src/main/res/values-no/strings.xml b/core/src/main/res/values-no/strings.xml
new file mode 100644
index 000000000..16639048b
--- /dev/null
+++ b/core/src/main/res/values-no/strings.xml
@@ -0,0 +1,30 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!--Activitiy and fragment titles-->
+ <!--New episodes fragment-->
+ <!--Main activity-->
+ <!--Webview actions-->
+ <!--Playback history-->
+ <!--Other-->
+ <!--'Add Feed' Activity labels-->
+ <!--Actions on feeds-->
+ <!--actions on feeditems-->
+ <!--Download messages and labels-->
+ <!--Mediaplayer messages-->
+ <!--Queue operations-->
+ <!--Flattr-->
+ <!--Flattr-->
+ <!--Variable Speed-->
+ <!--Empty list labels-->
+ <!--Preferences-->
+ <!--Auto-Flattr dialog-->
+ <!--Search-->
+ <!--OPML import and export-->
+ <!--Sleep timer-->
+ <!--gpodder.net-->
+ <!--Directory chooser-->
+ <!--Online feed view-->
+ <!--Content descriptions for image buttons-->
+ <!--Feed information screen-->
+ <!--AntennaPodSP-->
+</resources>
diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml
index e83c9b48e..9f80f8532 100644
--- a/core/src/main/res/values-tr/strings.xml
+++ b/core/src/main/res/values-tr/strings.xml
@@ -26,6 +26,7 @@
<!--Main activity-->
<string name="drawer_open">Münüyü aç</string>
<string name="drawer_close">Menüyü kapat</string>
+ <string name="drawer_preferences">Çekmece Seçenekleri</string>
<!--Webview actions-->
<string name="open_in_browser_label">Tarayıcıda aç</string>
<string name="copy_url_label">URL\'yi kopyala</string>
@@ -39,6 +40,7 @@
<string name="cancel_label">İptal</string>
<string name="author_label">Yayıncı</string>
<string name="language_label">Dil</string>
+ <string name="url_label">URL</string>
<string name="podcast_settings_label">Ayarlar</string>
<string name="cover_label">Resim</string>
<string name="error_label">Hata</string>
@@ -67,10 +69,10 @@
<string name="podcastdirectories_descr">gdpodder.net dizininde yeni cep yayınlarını isme, kategoriye veya popülerliğe göre arayabilirsiniz veya iTunes mağazasında arama yapabilirsiniz.</string>
<string name="browse_gpoddernet_label">gpodder.net\'e gözat</string>
<!--Actions on feeds-->
- <string name="mark_all_read_label">Hepsini okundu olarak işaretle</string>
- <string name="mark_all_read_msg">Tüm bölümler okundu olarak işaretlendi</string>
- <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string>
- <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string>
+ <string name="mark_all_read_label">Hepsini oynatıldı olarak işaretle</string>
+ <string name="mark_all_read_msg">Tüm bölümleri oynatıldı olarak işaretle</string>
+ <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string>
<string name="show_info_label">Bilgiyi göster</string>
<string name="remove_feed_label">Cep yayını kaldır</string>
<string name="share_link_label">Web sayfası bağlantısı paylaş</string>
@@ -78,6 +80,16 @@
<string name="feed_delete_confirmation_msg">Lütfen bu beslemeyi ve bu beslemeye ait indirilmiş BÜTÜN bölümleri silme isteğinizi onaylayın.</string>
<string name="feed_remover_msg">Besleme kaldırılıyor</string>
<string name="load_complete_feed">Tüm beslemeyi yenile</string>
+ <string name="hide_episodes_title">Bölümleri gizle</string>
+ <string name="hide_unplayed_episodes_label">Oynatılmadı</string>
+ <string name="hide_paused_episodes_label">Duraklatıldı</string>
+ <string name="hide_played_episodes_label">Oynatıldı</string>
+ <string name="hide_queued_episodes_label">Kuyrukta</string>
+ <string name="hide_not_queued_episodes_label">Kuyrukta değil</string>
+ <string name="hide_downloaded_episodes_label">İndirildi</string>
+ <string name="hide_not_downloaded_episodes_label">İndirilmedi</string>
+ <string name="filtered_label">Filtrelendi</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Son yenileme başarısız oldu</string>
<!--actions on feeditems-->
<string name="download_label">İndir</string>
<string name="play_label">Oynat</string>
@@ -86,16 +98,20 @@
<string name="stream_label">Akış</string>
<string name="remove_label">Kaldır</string>
<string name="remove_episode_lable">Bölümü kaldır</string>
- <string name="mark_read_label">Okundu olarak işaretle</string>
- <string name="mark_unread_label">Okunmadı olarak işaretle</string>
- <string name="marked_as_read_label">Okundu olarak işaretlendi</string>
+ <string name="mark_read_label">Oynatıldı olarak işaretle</string>
+ <string name="mark_unread_label">Oynatılmadı olarak işaretle</string>
+ <string name="marked_as_read_label">Oynatıldı olarak işaretlendi</string>
<string name="add_to_queue_label">Kuyruğa Ekle</string>
+ <string name="added_to_queue_label">Kuyruğa Eklendi</string>
<string name="remove_from_queue_label">Kuyruktan Kaldır</string>
<string name="visit_website_label">Siteyi Ziyaret Et</string>
<string name="support_label">Flattr ile destekle</string>
<string name="enqueue_all_new">Hepsini kuyruğa ekle</string>
<string name="download_all">Hepsini indir</string>
<string name="skip_episode_label">Bölümü atla</string>
+ <string name="activate_auto_download">Otomatik indirmeyi etkinleştir</string>
+ <string name="deactivate_auto_download">Otomatik indirmeyi devre dışı bırak</string>
+ <string name="reset_position">Çalme konumunu sıfırla</string>
<!--Download messages and labels-->
<string name="download_successful">başarılı</string>
<string name="download_failed">başarısız</string>
@@ -113,7 +129,9 @@
<string name="download_error_unauthorized">Yetkilendirme hatası</string>
<string name="cancel_all_downloads_label">Bütün indirmeleri iptal et</string>
<string name="download_canceled_msg">İndirme iptal edildi</string>
- <string name="download_report_title">İndirme tamamlandı</string>
+ <string name="download_canceled_autodownload_enabled_msg">İndirme iptal edildi\nBu öğe için <i>Otomatik İndirme</i> devre dışı</string>
+ <string name="download_report_title">İndirme hata(lar) ile tamamlandı</string>
+ <string name="download_report_content_title">İndirme raporu</string>
<string name="download_error_malformed_url">Bozuk URL</string>
<string name="download_error_io_error">G/Ç Hatası</string>
<string name="download_error_request_error">İstek hatası</string>
@@ -129,6 +147,11 @@
<string name="download_request_error_dialog_message_prefix">Dosyayı indirmeye çalışırken bir hata oluştu:\u0020</string>
<string name="authentication_notification_title">Yetkilendirme gerekiyor</string>
<string name="authentication_notification_msg">İstediğiniz kaynak kullanıcı adı ve şifre istiyor</string>
+ <string name="confirm_mobile_download_dialog_title">Mobil İndirmeyi Onaylayın</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı yoksa sadece kuyruğa mı eklensin?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Sadece Kuyruğa ekle</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Geçici olarak aç</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Hata!</string>
<string name="player_stopped_msg">Çalınan medya yok</string>
@@ -143,6 +166,8 @@
<string name="playbackservice_notification_title">Cep yayını çalınıyor</string>
<string name="unknown_media_key">AntennaPod - Bilinmeyen medya anahtarı: %1$d</string>
<!--Queue operations-->
+ <string name="lock_queue">Kuyruğu kilitle</string>
+ <string name="unlock_queue">Kuyruğun kilidini aç</string>
<string name="clear_queue_label">Kuyruğu temizle</string>
<string name="undo">Geri al</string>
<string name="removed_from_queue">Öge kaldırıldı</string>
@@ -246,8 +271,8 @@
<string name="pref_gpodnet_setlogin_information_sum">gpodder.net hesabınız için giriş bilgisini değiştirin.</string>
<string name="pref_playback_speed_title">Çalma hızları</string>
<string name="pref_playback_speed_sum">Değişken hızlı ses yürütmesi için kullanılabilir hızları özelleştirin</string>
- <string name="pref_seek_delta_title">Arama zamanı</string>
- <string name="pref_seek_delta_sum">Geri veya ileri sararken bu kadar saniye atla</string>
+ <string name="pref_fast_forward">İleri sarma süresi</string>
+ <string name="pref_rewind">Geri sarma süresi</string>
<string name="pref_gpodnet_sethostname_title">Sunucu ismini ayarla</string>
<string name="pref_gpodnet_sethostname_use_default_host">Varsayılan sunucuyu kullan</string>
<string name="pref_expandNotify_title">Bildirimi Genişlet</string>
@@ -257,6 +282,7 @@
<string name="pref_expand_notify_unsupport_toast">Android 4.1 öncesi sürümler genişletilmiş bildirimleri desteklememektedir.</string>
<string name="pref_queueAddToFront_sum">Yeni bölümleri kuyruğun önüne ekle.</string>
<string name="pref_queueAddToFront_title">Kuyruğun önüne ekle.</string>
+ <string name="pref_smart_mark_as_played_disabled">Devre dışı</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Otomatik Flattr\'lamayı etkinleştir</string>
<string name="auto_flattr_after_percent">Bölümün yüzde %d kısmı oynatıldığında Flattr\'la</string>
@@ -341,6 +367,8 @@
<string name="set_to_default_folder">Vaysayılan dizini seç</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Başka bir uygulama ses çalmak istediğinde sesi kısmak yerine yürütmeyi duraklat</string>
<string name="pref_pausePlaybackForFocusLoss_title">Kesintiler için duraklat</string>
+ <string name="pref_resumeAfterCall_sum">Bir telefon konuşması tamamlandıktan sonra çalmaya kaldığı yerden devam et</string>
+ <string name="pref_resumeAfterCall_title">Konuşmadan sonra devam et</string>
<!--Online feed view-->
<string name="subscribe_label">Üye ol</string>
<string name="subscribed_label">Üye olundu</string>
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 3c852360c..8f1268993 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -34,30 +34,24 @@
<item>12</item>
<item>24</item>
</string-array>
+
<string-array name="episode_cache_size_entries">
- <item>@string/pref_episode_cache_unlimited</item>
- <item>1</item>
- <item>2</item>
<item>5</item>
<item>10</item>
- <item>20</item>
- <item>40</item>
- <item>60</item>
- <item>80</item>
+ <item>25</item>
+ <item>50</item>
<item>100</item>
+ <item>@string/pref_episode_cache_unlimited</item>
</string-array>
<string-array name="episode_cache_size_values">
- <item>-1</item>
- <item>1</item>
- <item>2</item>
<item>5</item>
<item>10</item>
- <item>20</item>
- <item>40</item>
- <item>60</item>
- <item>80</item>
+ <item>25</item>
+ <item>50</item>
<item>100</item>
+ <item>-1</item>
</string-array>
+
<string-array name="playback_speed_values">
<item>0.5</item>
<item>0.6</item>
@@ -174,4 +168,20 @@
<item>not_downloaded</item>
</string-array>
+ <string-array name="image_cache_size_options">
+ <item>20 MiB</item>
+ <item>50 MiB</item>
+ <item>100 MiB</item>
+ <item>250 MiB</item>
+ <item>500 MiB</item>
+ </string-array>
+
+ <string-array name="image_cache_size_values">
+ <item>20</item>
+ <item>50</item>
+ <item>100</item>
+ <item>250</item>
+ <item>500</item>
+ </string-array>
+
</resources>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index e558a5c4e..bc0521dcd 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -3,6 +3,7 @@
<color name="white">#FFFFFF</color>
<color name="gray">#808080</color>
+ <color name="light_gray">#bfbfbf</color>
<color name="black">#000000</color>
<color name="bright_blue">#33B5E5</color>
<color name="ics_gray">#858585</color>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 5079950fa..00bc077e8 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -53,6 +53,8 @@
<!-- Other -->
<string name="confirm_label">Confirm</string>
<string name="cancel_label">Cancel</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
<string name="author_label">Author</string>
<string name="language_label">Language</string>
<string name="url_label">URL</string>
@@ -75,6 +77,8 @@
<string name="close_label">Close</string>
<string name="retry_label">Retry</string>
<string name="auto_download_label">Include in auto downloads</string>
+ <string name="auto_download_apply_to_items_title">Apply to Previous Episodes</string>
+ <string name="auto_download_apply_to_items_message">The new <i>Auto Download</i> setting will automatically be applied to new episodes.\nDo you also want to apply it to previous episodes?</string>
<string name="auto_delete_label">Auto Delete Episode\n(override global default)</string>
<string name="parallel_downloads_suffix">\u0020parallel downloads</string>
<string name="feed_auto_download_global">Global</string>
@@ -267,7 +271,7 @@
<string name="network_pref">Network</string>
<string name="pref_autoUpdateIntervallOrTime_title">Update Interval or Time of Day</string>
<string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the feeds automatically</string>
- <string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small>"</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string>
@@ -331,12 +335,12 @@
<string name="pref_persistNotify_sum">Keep notification and lockscreen controls when playback is paused.</string>
<string name="pref_showDownloadReport_title">Show Download Report</string>
<string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</string>
-
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
<string name="pref_queueAddToFront_sum">Add new episodes to the front of the queue.</string>
<string name="pref_queueAddToFront_title">Enqueue at Front</string>
<string name="pref_smart_mark_as_played_disabled">Disabled</string>
-
+ <string name="pref_image_cache_size_title">Image Cache Size</string>
+ <string name="pref_image_cache_size_sum">Size of the disk cache for images.</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>
@@ -380,9 +384,21 @@
<string name="sleep_timer_label">Sleep timer</string>
<string name="time_left_label">Time left:\u0020</string>
<string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string>
- <string name="time_unit_seconds">seconds</string>
- <string name="time_unit_minutes">minutes</string>
- <string name="time_unit_hours">hours</string>
+ <string name="time_seconds">seconds</string>
+ <string name="time_minutes">minutes</string>
+ <string name="time_hours">hours</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 second</item>
+ <item quantity="other">%d seconds</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minute</item>
+ <item quantity="other">%d minutes</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 hour</item>
+ <item quantity="other">%d hours</item>
+ </plurals>
<!-- gpodder.net -->
<string name="gpodnet_taglist_header">CATEGORIES</string>
@@ -431,6 +447,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Pause for Interruptions</string>
<string name="pref_resumeAfterCall_sum">Resume playback after a phone call completes</string>
<string name="pref_resumeAfterCall_title">Resume after Call</string>
+ <string name="pref_restart_required">AntennaPod has to be restarted for this change to take effect.</string>
<!-- Online feed view -->
<string name="subscribe_label">Subscribe</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 8619869c8..85c908f64 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -263,7 +263,9 @@
<style name="BigBlurryBackground">
<item name="android:scaleType">centerCrop</item>
- <item name="android:tint">@color/image_readability_tint</item>
+ <!-- <item name="android:tint">@color/image_readability_tint</item> -->
+ <!-- Reactivate when Glide's tinting has been fixed for Android 5.x
+ Remove color filter from ItemlistFragment -->
</style>