package org.wikipedia.util; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.StringRes; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import org.apache.commons.lang3.StringUtils; import org.wikipedia.WikipediaApp; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.page.PageTitle; import org.wikipedia.settings.Prefs; import org.wikipedia.util.log.L; import org.wikipedia.zero.WikipediaZeroHandler; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import static org.wikipedia.zero.WikipediaZeroHandler.showZeroExitInterstitialDialog; public final class UriUtil { /** * Decodes a URL-encoded string into its UTF-8 equivalent. If the string cannot be decoded, the * original string is returned. * @param url The URL-encoded string that you wish to decode. * @return The decoded string, or the input string if the decoding failed. */ @NonNull public static String decodeURL(@NonNull String url) { try { return URLDecoder.decode(url, "UTF-8"); } catch (IllegalArgumentException e) { // Swallow IllegalArgumentException (can happen with malformed encoding), and just // return the original string. L.d("URL decoding failed. String was: " + url); return url; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } @NonNull public static String encodeURL(@NonNull String url) { try { return URLEncoder.encode(url, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Open the specified URI in an external browser (even if our app's intent filter * matches the given URI) * * @param context Context of the calling app * @param uri URI to open in an external browser */ public static void visitInExternalBrowser(final Context context, Uri uri) { Intent chooserIntent = ShareUtil.createChooserIntent(new Intent(Intent.ACTION_VIEW, uri), null, context); if (chooserIntent == null) { // This means that there was no way to handle this link. // We will just show a toast now. FIXME: Make this more visible? ShareUtil.showUnresolvableIntentMessage(context); } else { chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(chooserIntent); } } @NonNull public static String resolveProtocolRelativeUrl(@NonNull WikiSite wiki, @NonNull String url) { String ret = resolveProtocolRelativeUrl(url); // also handle images like /w/extensions/ImageMap/desc-20.png?15600 on Estados Unidos return (ret.startsWith("./") || ret.startsWith("/w/") || ret.startsWith("/wiki/")) ? wiki.uri().buildUpon().appendEncodedPath(ret.replaceFirst("/", "")).build().toString() : ret; } /** * Resolves a potentially protocol relative URL to a 'full' URL * * @param url Url to check for (and fix) protocol relativeness * @return A fully qualified, protocol specified URL */ @NonNull public static String resolveProtocolRelativeUrl(@NonNull String url) { return (url.startsWith("//") ? WikipediaApp.getInstance().getWikiSite().scheme() + ":" + url : url); } public static boolean isValidPageLink(@NonNull Uri uri) { return (!TextUtils.isEmpty(uri.getAuthority()) && uri.getAuthority().endsWith("wikipedia.org") && !TextUtils.isEmpty(uri.getPath()) && uri.getPath().startsWith("/wiki")); } public static void handleExternalLink(final Context context, final Uri uri) { final WikipediaZeroHandler zeroHandler = WikipediaApp.getInstance() .getWikipediaZeroHandler(); if (!zeroHandler.isZeroEnabled()) { if (!StringUtils.isEmpty(zeroHandler.getXCarrier())) { // User is potentially zero-rated based on IP, but not on a whitelisted wiki (this // is rare) zeroHandler.getZeroFunnel().logExtLink(); } visitInExternalBrowser(context, uri); return; } if (!Prefs.isShowZeroInterstitialEnabled()) { visitInExternalBrowser(context, uri); zeroHandler.getZeroFunnel().logExtLinkAuto(); return; } showZeroExitInterstitialDialog(context, uri); } public static String getUrlWithProvenance(Context context, PageTitle title, @StringRes int provId) { return title.getCanonicalUri() + "?wprov=" + context.getString(provId); } /** * Note that while this method also replaces '_' with spaces it doesn't fully decode the string. */ @NonNull public static String getTitleFromUrl(@NonNull String url) { return removeFragment(removeLinkPrefix(url)).replace("_", " "); } /** For internal links only */ @NonNull public static String removeInternalLinkPrefix(@NonNull String link) { return link.replaceFirst("/wiki/", ""); } /** For links that could be internal or external links */ @NonNull private static String removeLinkPrefix(@NonNull String link) { return link.replaceFirst("^.*?/wiki/", ""); } /** Removes an optional fragment portion of a URL */ @VisibleForTesting @NonNull static String removeFragment(@NonNull String link) { return link.replaceFirst("#.*$", ""); } public static String getFragment(String link) { return Uri.parse(link).getFragment(); } private UriUtil() { } }