package com.mopub.mobileads;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.mopub.common.MoPubBrowser;
import com.mopub.common.Preconditions;
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.Intents;
import com.mopub.exceptions.IntentNotResolvableException;
import com.mopub.exceptions.UrlParseException;
import static com.mopub.mobileads.MoPubErrorCode.UNSPECIFIED;
class HtmlWebViewClient extends WebViewClient {
static final String MOPUB_FINISH_LOAD = "mopub://finishLoad";
static final String MOPUB_FAIL_LOAD = "mopub://failLoad";
private final Context mContext;
private HtmlWebViewListener mHtmlWebViewListener;
private BaseHtmlWebView mHtmlWebView;
private final String mClickthroughUrl;
private final String mRedirectUrl;
HtmlWebViewClient(HtmlWebViewListener htmlWebViewListener, BaseHtmlWebView htmlWebView, String clickthrough, String redirect) {
mHtmlWebViewListener = htmlWebViewListener;
mHtmlWebView = htmlWebView;
mClickthroughUrl = clickthrough;
mRedirectUrl = redirect;
mContext = htmlWebView.getContext();
}
/**
* Called upon user click, when the WebView attempts to load a new URL. Attempts to handle mopub
* and phone-specific schemes, open mopubnativebrowser links in the device browser, deep-links
* in the corresponding application, and all other links in the MoPub in-app browser.
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
MoPubLog.d("Ad clicked. Click URL: " + url);
if (handleSpecialMoPubScheme(url) || handlePhoneScheme(url)) {
return true;
}
// MoPubNativeBrowser URLs
if (Intents.isNativeBrowserScheme(url)) {
final String errorMessage = "Unable to load mopub native browser url: " + url;
try {
final Intent intent = Intents.intentForNativeBrowserScheme(url);
launchIntentForUserClick(mContext, intent, errorMessage);
} catch (UrlParseException e) {
MoPubLog.d(errorMessage + ". " + e.getMessage());
}
return true;
}
// Non-http(s) URLs
if (!Intents.isHttpUrl(url) && Intents.canHandleApplicationUrl(mContext, url)) {
launchApplicationUrl(url);
return true;
}
showMoPubBrowserForUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// If the URL being loaded shares the redirectUrl prefix, open it in the browser.
if (mRedirectUrl != null && url.startsWith(mRedirectUrl)) {
view.stopLoading();
showMoPubBrowserForUrl(url);
}
}
private boolean isSpecialMoPubScheme(String url) {
return url.startsWith("mopub://");
}
private boolean handleSpecialMoPubScheme(String url) {
if (!isSpecialMoPubScheme(url)) {
return false;
}
Uri uri = Uri.parse(url);
String host = uri.getHost();
if ("finishLoad".equals(host)) {
mHtmlWebViewListener.onLoaded(mHtmlWebView);
} else if ("close".equals(host)) {
mHtmlWebViewListener.onCollapsed();
} else if ("failLoad".equals(host)) {
mHtmlWebViewListener.onFailed(UNSPECIFIED);
} else if ("custom".equals(host)) {
handleCustomIntentFromUri(uri);
}
return true;
}
private boolean isPhoneScheme(String url) {
return url.startsWith("tel:") || url.startsWith("voicemail:") ||
url.startsWith("sms:") || url.startsWith("mailto:") ||
url.startsWith("geo:") || url.startsWith("google.streetview:");
}
private boolean handlePhoneScheme(String url) {
if (!isPhoneScheme(url)) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
String errorMessage = "Could not handle intent with URI: " + url
+ ". Is this intent supported on your phone?";
launchIntentForUserClick(mContext, intent, errorMessage);
return true;
}
private boolean launchApplicationUrl(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
String errorMessage = "Unable to open intent.";
return launchIntentForUserClick(mContext, intent, errorMessage);
}
private void showMoPubBrowserForUrl(String url) {
if (url == null || url.equals("")) {
url = "about:blank";
}
MoPubLog.d("Final URI to show in browser: " + url);
final Bundle extras = new Bundle();
extras.putString(MoPubBrowser.DESTINATION_URL_KEY, url);
Intent intent = Intents.getStartActivityIntent(mContext, MoPubBrowser.class, extras);
String errorMessage = "Could not handle intent action. "
+ ". Perhaps you forgot to declare com.mopub.common.MoPubBrowser"
+ " in your Android manifest file.";
boolean handledByMoPubBrowser = launchIntentForUserClick(mContext, intent, errorMessage);
if (!handledByMoPubBrowser) {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("about:blank"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntentForUserClick(mContext, intent, null);
}
}
private void handleCustomIntentFromUri(Uri uri) {
String action;
String adData;
try {
action = uri.getQueryParameter("fnc");
adData = uri.getQueryParameter("data");
} catch (UnsupportedOperationException e) {
MoPubLog.w("Could not handle custom intent with uri: " + uri);
return;
}
Intent customIntent = new Intent(action);
customIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
customIntent.putExtra(HtmlBannerWebView.EXTRA_AD_CLICK_DATA, adData);
String errorMessage = "Could not handle custom intent: " + action
+ ". Is your intent spelled correctly?";
launchIntentForUserClick(mContext, customIntent, errorMessage);
}
boolean launchIntentForUserClick(@Nullable final Context context, @NonNull final Intent intent,
@Nullable final String errorMessage) {
Preconditions.NoThrow.checkNotNull(intent);
if (context == null) {
MoPubLog.d(errorMessage);
return false;
}
if (!mHtmlWebView.wasClicked()) {
return false;
}
try {
Intents.startActivity(context, intent);
mHtmlWebViewListener.onClicked();
mHtmlWebView.onResetUserClick();
return true;
} catch (IntentNotResolvableException e) {
MoPubLog.d(errorMessage);
return false;
}
}
}