Replace Picasso with Glide

This commit is contained in:
Martin Fietz 2015-07-21 20:04:39 +02:00
parent 8721dab1bc
commit af2ded8fe0
48 changed files with 1103 additions and 736 deletions

View File

@ -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'

View File

@ -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 *;
}

View File

@ -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>

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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";
}

View File

@ -4,7 +4,6 @@ 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;
@ -39,7 +38,8 @@ public class PodcastApp extends Application {
singleton = this;
LOGICAL_DENSITY = getResources().getDisplayMetrics().density;
PicassoProvider.setupPicassoInstance(this);
// PicassoProvider.setupPicassoInstance(this);
UpdateManager.init(this);
UserPreferences.init(this);
PlaybackPreferences.init(this);
EventDistributor.getInstance();

View File

@ -0,0 +1,82 @@
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;
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;
}
final int oldVersionCode = getStoredVersionCode();
Log.d(TAG, "old: " + oldVersionCode + ", current: " + currentVersionCode);
if(oldVersionCode < currentVersionCode) {
onUpgrade(oldVersionCode, currentVersionCode);
setCurrentVersionCode();
}
}
public static int getCurrentVersionCode() {
return currentVersionCode;
}
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 < 1030000) {
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);
}
}
}
}
}
}

View File

@ -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();

View File

@ -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);
}

View File

@ -14,19 +14,19 @@ 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.DownloadRequestErrorDialogCreator;
@ -123,9 +123,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);
}
});

View File

@ -11,7 +11,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 +78,7 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.new_episodes_listitem,
parent, false);
holder.feed = (TextView) convertView.findViewById(R.id.txtvImage);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
@ -89,6 +96,8 @@ public class AllEpisodesListAdapter extends BaseAdapter {
holder = (Holder) convertView.getTag();
}
holder.feed.setVisibility(View.VISIBLE);
holder.feed.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 +150,35 @@ 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 CustomTarget(holder.feed, holder.imageView));
return convertView;
}
private class CustomTarget extends GlideDrawableImageViewTarget {
private final WeakReference<TextView> mPlaceholder;
public CustomTarget(TextView placeholder, ImageView imageView) {
super(imageView);
mPlaceholder = new WeakReference<TextView>(placeholder);
}
@Override
public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
super.onResourceReady(drawable, anim);
TextView txtvPlaceholder = mPlaceholder.get();
if(txtvPlaceholder != null) {
txtvPlaceholder.setVisibility(View.INVISIBLE);
}
}
}
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -159,6 +189,7 @@ public class AllEpisodesListAdapter extends BaseAdapter {
static class Holder {
TextView feed;
TextView title;
TextView pubDate;
View statusUnread;

View File

@ -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;

View File

@ -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());

View File

@ -11,7 +11,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;
@ -76,6 +82,7 @@ public class QueueListAdapter extends BaseAdapter {
convertView = inflater.inflate(R.layout.queue_listitem,
parent, false);
holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle);
holder.feed = (TextView) convertView.findViewById(R.id.txtvImage);
holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
@ -98,10 +105,11 @@ public class QueueListAdapter extends BaseAdapter {
holder.dragHandle.setVisibility(View.VISIBLE);
}
holder.feed.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 +152,35 @@ 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 CustomTarget(holder.feed, holder.imageView));
return convertView;
}
private class CustomTarget extends GlideDrawableImageViewTarget {
private final WeakReference<TextView> mPlaceholder;
public CustomTarget(TextView placeholder, ImageView imageView) {
super(imageView);
mPlaceholder = new WeakReference<TextView>(placeholder);
}
@Override
public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
super.onResourceReady(drawable, anim);
TextView txtvPlaceholder = mPlaceholder.get();
if(txtvPlaceholder != null) {
txtvPlaceholder.setVisibility(View.INVISIBLE);
}
}
}
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -164,6 +193,7 @@ public class QueueListAdapter extends BaseAdapter {
static class Holder {
ImageView dragHandle;
ImageView imageView;
TextView feed;
TextView title;
TextView pubDate;
TextView progressLeft;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -31,9 +31,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 +47,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 +56,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;
@ -505,17 +506,25 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
Picasso.with(getActivity())
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())
// https://github.com/bumptech/glide/issues/529
imgvBackground.setColorFilter(R.color.image_readability_tint);
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() {

View File

@ -364,6 +364,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

View File

@ -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/txtvImage"
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/imgvImage"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignLeft="@+id/txtvImage"
android:layout_alignTop="@+id/txtvImage"
android:layout_alignRight="@+id/txtvImage"
android:layout_alignBottom="@+id/txtvImage"
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"

View File

@ -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/txtvImage"
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/imgvImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/txtvImage"
android:layout_alignTop="@+id/txtvImage"
android:layout_alignRight="@+id/txtvImage"
android:layout_alignBottom="@+id/txtvImage"
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"

View File

@ -204,6 +204,13 @@
<Preference
android:title="@string/choose_data_directory"
android:key="prefChooseDataDir"/>
<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="0"/>
<Preference
android:key="prefFlattrThisApp"
android:summary="@string/pref_flattr_this_app_sum"
@ -216,7 +223,6 @@
android:key="prefAbout"
android:title="@string/about_pref"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -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>

View File

@ -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'

View File

@ -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

View File

@ -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);
}
}

View File

@ -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";

View File

@ -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;

View File

@ -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.
@ -337,7 +337,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();

View File

@ -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());
}
}

View File

@ -0,0 +1,80 @@
package de.danoeh.antennapod.core.glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* @see com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher
*/
public class ApOkHttpStreamFetcher implements DataFetcher<InputStream> {
private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
public ApOkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
Response response = client.newCall(request).execute();
responseBody = response.body();
if (!response.isSuccessful()) {
throw new IOException("Request failed with code: " + response.code());
}
long contentLength = responseBody.contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignored
}
}
if (responseBody != null) {
try {
responseBody.close();
} catch (IOException e) {
// Ignored.
}
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
// TODO: call cancel on the client when this method is called on a background thread. See #257
}
}

View File

@ -0,0 +1,124 @@
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.HttpDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
/**
* @see com.bumptech.glide.integration.okhttp.OkHttpUrlLoader
*/
public class ApOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private static final String TAG = "ApOkHttpUrlLoader";
/**
* 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 = new OkHttpClient();
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 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;
}
}
}
}

View File

@ -0,0 +1,277 @@
package de.danoeh.antennapod.core.glide;
import android.content.Context;
import android.graphics.Bitmap;
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 = "BlurTransformation";
private static final int RADIUS = 1;
private static final int IMAGE_SIZE = 192;
public FastBlurTransformation(Context context) {
super(context);
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform,
int outWidth, int outHeight) {
Bitmap resizedBitmap = Bitmap.createScaledBitmap(toTransform, IMAGE_SIZE, IMAGE_SIZE, true);
Bitmap transformed = fastBlur(resizedBitmap, RADIUS);
if (transformed == null) {
Log.w(TAG, "transformed was null");
return toTransform;
}
return transformed;
}
@Override
public String getId() {
return "FastBlurTransformation-" + IMAGE_SIZE + "x" + IMAGE_SIZE + "-" + RADIUS;
}
private static Bitmap fastBlur(Bitmap original, 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 = original.getConfig();
if (config == null) {
// Sometimes the config can be null, in those cases
// we don't do a transform.
return null;
}
Bitmap bitmap = original.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;
}
}

View File

@ -78,6 +78,7 @@ 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";
@ -213,7 +214,6 @@ public class UserPreferences {
public static String getPlaybackSpeed() {
return prefs.getString(PREF_PLAYBACK_SPEED, "1.0");
}
public static String[] getPlaybackSpeedArray() {
@ -278,6 +278,12 @@ public class UserPreferences {
return prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false);
}
public static int getImageCacheSize() {
String cacheSizeString = prefs.getString(PREF_IMAGE_CACHE_SIZE, "100");
int cacheSize = Integer.valueOf(cacheSizeString) * 1024 * 1024;
return cacheSize;
}
public static int getFastFowardSecs() {
return prefs.getInt(PREF_FAST_FORWARD_SECS, 30);
}

View File

@ -777,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) {

View File

@ -29,7 +29,10 @@ 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;
@ -791,16 +794,18 @@ public class PlaybackService extends Service {
Log.d(TAG, "Starting background work");
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (info.playable != null) {
try {
int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width);
icon = Picasso.with(PlaybackService.this)
.load(info.playable.getImageUri())
.resize(iconSize, iconSize)
.get();
} catch (IOException e) {
e.printStackTrace();
}
int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width);
Glide.with(PlaybackService.this)
.load(info.playable.getImageUri())
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(new SimpleTarget<Bitmap>(iconSize, iconSize) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
icon = bitmap;
}
});
}
}

View File

@ -871,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 {

View File

@ -283,24 +283,6 @@ public final class DBTasks {
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages);
}
/**
* 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.

View File

@ -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;
@ -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(

View File

@ -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

View File

@ -174,4 +174,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>

View File

@ -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>

View File

@ -331,12 +331,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>
@ -431,6 +431,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">AnntennaPod has to be restarted for this change to take effect.</string>
<!-- Online feed view -->
<string name="subscribe_label">Subscribe</string>
@ -464,6 +465,7 @@
<!-- Progress information -->
<string name="progress_upgrading_database">Upgrading the database</string>
<string name="progress_clearing_image_cache">Clearing old image cache</string>
<!-- AntennaPodSP -->

View File

@ -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>