Java tutorial
/** This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. **/ /** This file is part of Save For Offline, an Android app which saves / downloads complete webpages for offine reading. **/ /** If you modify, redistribute, or write something based on this or parts of it, you MUST, I repeat, you MUST comply with the GPLv2+ license. This means that if you use or modify my code, you MUST release the source code of your modified version, if / when this is required under the terms of the license. If you cannot / do not want to do this, DO NOT USE MY CODE. Thanks. (I've added this message to to the source because it's been used in severeral proprietary closed source apps, which I don't want, and which is also a violation of the liense.) **/ /** Written by Jonas Czech (JonasCz, stackoverflow.com/users/4428462/JonasCz and github.com/JonasCz). (4428462jonascz/eafc4d1afq) **/ package jonas.tool.saveForOffline; import org.jsoup.nodes.Document; import java.io.File; import com.squareup.okhttp.*; import org.jsoup.select.*; import java.util.*; import org.jsoup.nodes.*; import java.net.*; import java.io.*; import java.util.regex.*; import android.graphics.*; import org.jsoup.*; import android.os.*; public class FaviconFetcher { private static FaviconFetcher INSTANCE = new FaviconFetcher(); private OkHttpClient client = new OkHttpClient(); private final String[] htmlIconCssQueries = { "meta[property=\"og:image\"]", "meta[name=\"msapplication-TileImage\"]", "link[rel=\"icon\"]", "link[rel=\"shortcut icon\"]", "link[rel=\"apple-touch-icon\"]", "link[rel=\"apple-touch-icon-precomposed\"]", "img[alt=\"Logo\"]", "img[alt=\"logo\"]" }; private final String[] hardcodedIconPaths = { "/favicon.ico", "/apple-touch-icon.png", "/apple-touch-icon-precomposed.png", }; private FaviconFetcher() { } public static FaviconFetcher getInstance() { return INSTANCE; } public String getFaviconUrl(Document document) { List<String> potentialIcons = getPotentialFaviconUrls(document); return pickBestIconUrl(potentialIcons); } public List<String> getPotentialFaviconUrls(Document document) { List<String> iconUrls = new ArrayList<String>(); HttpUrl base = HttpUrl.parse(document.baseUri()); for (String cssQuery : htmlIconCssQueries) { for (Element e : document.select(cssQuery)) { if (e.hasAttr("href")) { iconUrls.add(e.attr("href")); } if (e.hasAttr("content")) { iconUrls.add(e.attr("content")); } if (e.hasAttr("src")) { iconUrls.add(e.attr("src")); } } } for (String path : hardcodedIconPaths) { HttpUrl url = HttpUrl.parse("http://" + HttpUrl.parse(document.baseUri()).host() + path); iconUrls.add(url.toString()); } for (ListIterator<String> i = iconUrls.listIterator(); i.hasNext();) { HttpUrl httpUrl = base.resolve(i.next()); if (httpUrl != null) { i.set(httpUrl.toString()); } else { i.remove(); } } return iconUrls; } public String pickBestIconUrl(List<String> urls) { String bestIconUrl = null; int currentBestWidth = 0; for (String url : urls) { BitmapFactory.Options options = getBitmapDimensFromUrl(url); if (options != null && options.outHeight == options.outHeight) { if ((bestIconUrl != null) && (currentBestWidth <= options.outWidth)) { bestIconUrl = url; currentBestWidth = options.outWidth; } else if (bestIconUrl == null) { bestIconUrl = url; currentBestWidth = options.outWidth; } } } return bestIconUrl; } private BitmapFactory.Options getBitmapDimensFromUrl(String url) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Request request = new Request.Builder().url(url).build(); try { Response response = client.newCall(request).execute(); InputStream is = response.body().byteStream(); BitmapFactory.decodeStream(is, null, options); response.body().close(); is.close(); return options; } catch (IllegalArgumentException | IOException e) { e.printStackTrace(); return null; } } }