Android Open Source - sdk-android-2 H T M L View






From Project

Back to project page sdk-android-2.

License

The source code is released under:

Apache License

If you think the Android project sdk-android-2 listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2013 Medium Entertainment, Inc.
 *// w ww  .j ava2  s . co m
 * 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.
 */
package com.playhaven.android.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.*;
import com.playhaven.android.Placement;
import com.playhaven.android.PlayHaven;
import com.playhaven.android.PlayHavenException;
import com.playhaven.android.cache.Cache;
import com.playhaven.android.data.*;
import com.playhaven.android.req.UrlRequest;
import com.playhaven.android.util.JsonUtil;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * HTML Content Unit
 */
public class HTMLView extends WebView implements ChildView<HTMLView> {
    private final String DISPATCH_PREFIX = "ph://";
    private Placement mPlacement;
    private List<String> mImages;
    private ArrayList<Reward> mRewards;
    private ArrayList<Purchase> mPurchases;
    private ArrayList<DataCollectionField> mDataFields;

    // These are bits of Javascript we inject into the window.
    /** callback template arguments: callbackId, JSON, error */
    private final String CALLBACK_TEMPLATE = "javascript:PlayHaven.nativeAPI.callback(\"%s\", %s, %s, %s)";
    private final String DISPATCH_PROTOCOL_TEMPLATE = "javascript:window.PlayHavenDispatchProtocolVersion=4";
    private final String COLLECT_FORM_DATA = "javascript:$.ajax({dataType: 'jsonp', jsonp: 'dcDataCallback', data: $('form').serialize(), url: 'ph://dcData'});";

    /**
     * These match the host portion of DISPATCH_PREFIX urls requested by the content
     * templates. They indicate different events in the content template that
     * need attention from the SDK.
     */
    public enum Dispatches {
        /**
         * closeButton hides the native emergency close button, and passes
         * notice of whether it was hidden back to the content template
         */
        closeButton,
        /**
         * dismiss triggers the contentDismissed listener
         */
        dismiss,
        /**
         * launch retrieves a URL from the server to be parsed using
         * Intent.ACTION_VIEW
         */
        launch,
        /**
         * loadContext passes the full "context" JSON blob to the
         * content template
         */
        loadContext,
        /**
         * purchase stores the purchase object (which is generated by the
         * content template) as mPurchases, for use with dismiss dispatch
         */
        purchase,
        /**
         * reward stores the reward object (which is generated by the
         * content template) as mRewards, for use with dismiss dispatch
         */
        reward,
        /**
         * subcontent takes a JSON blob generated by the content template
         * and uses that to get data for a new impression, currently a
         * more_games widget that follows a featured ad
         */
        subcontent,
        /**
         * No longer used
         */
        track,
        /**
         * This is one injected to let the Android SDK harvest data from the
         * opt-in data collection form.
         */
        dcData
    }

    /**
     * Makes the subcontent request, replaces the model in the placement, and
     * then reloads the WebView with the new stuff.
     */
    private class SubcontentRequest extends com.playhaven.android.req.SubcontentRequest {
        public SubcontentRequest(String dispatchContext) throws PlayHavenException {
            super(dispatchContext);
        }

        @Override
        protected void handleResponse(Context context, String json) {
            mPlacement.setModel(json);
            load(JsonUtil.<String>getPath(mPlacement.getModel(), "$.response.url"));
        }
    }

    private WebChromeClient webChromeClient = new WebChromeClient() {
        @Override
        public boolean onConsoleMessage(ConsoleMessage message) {
            PlayHaven.v("ConsoleMessage: %s", message.message());
            return super.onConsoleMessage(message);
        }
    };

    private WebViewClient webViewClient = new WebViewClient() {
        @Override
        public void onLoadResource(WebView view, String url) {
            if (url.startsWith(DISPATCH_PREFIX)) {
                handleDispatch(url);
            }
        }

    @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            HTMLView.this.setVisibility(android.view.View.VISIBLE);
        }

        @Override
      public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
          // Load images or the content template from the cache.
            if(mImages.contains(url) || url.equals(JsonUtil.<String>getPath(mPlacement.getModel(), "$.response.url")))
            {
              try {
                // TODO: spaces will break Cache, fix encoding before now.
                url = url.replace(" ", "%20");
              Cache cache = new Cache(getContext());
              File file = cache.getFile(new URL(url));
              cache.close();
              if(file != null && file.exists()){
                  PlayHaven.v("Loading from cache: %s.", file.getAbsolutePath());
                InputStream inputStream = new FileInputStream(file);
                return new WebResourceResponse("", "UTF-8", inputStream);
              }
              } catch (Exception e){
                PlayHaven.e("Could not load from cache: %s.", url);
                PlayHaven.e(e);
              }
          }
          return null;
      }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (url.startsWith(DISPATCH_PREFIX)) {
                handleDispatch(url);
                return true;
            } else {
                return super.shouldOverrideUrlLoading(view, url);
            }
        }
    };

    public HTMLView(Context context) {
        super(context);
    }

    public HTMLView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * This switches on the host portion of a request prefixed with
     * DISPATCH_PREFIX in order to handle events from the content templates.
     *
     * @TODO this would be a good candidate for factoring out to a cleaner custom WebViewClient
     *
     * @param dispatchUrl
     */
    private void handleDispatch(String dispatchUrl) {
        Uri callbackUri = Uri.parse(dispatchUrl);
        String callbackId = callbackUri.getQueryParameter("callback");
        String callbackString = callbackUri.getHost();
        String dispatchContext = callbackUri.getQueryParameter("context");
        PlayHaven.d("Handling dispatch: %s of type %s", dispatchUrl, callbackString);

        switch (Dispatches.valueOf(callbackString)) {
            /**
             * closeButton hides the native emergency close button, and passes
             * notice of whether it was hidden back to the content template
             */
            case closeButton:
                String hidden = "true";
                try {
                    hidden = new JSONObject(dispatchContext).getString("hidden");
                } catch (JSONException jse) {
                    // Default to NOT hiding the emergency close button
                    hidden = "false";
                }

                if("true".equals(hidden)) {
                    ((PlayHavenView) getParent()).setExitVisible(false);
                }

                // Tell the content template that we've hidden the emergency close button.
                this.loadUrl(String.format(CALLBACK_TEMPLATE, callbackId, "{'hidden':'" + hidden + "'}", null, null));
                break;
            /**
             * dismiss triggers the contentDismissed listener
             */
            case dismiss:
                PlayHavenView.DismissType dismiss = PlayHavenView.DismissType.NoThanks;
                if(mRewards != null)
                    dismiss = PlayHavenView.DismissType.Reward;

                if(mDataFields != null)
                    dismiss = PlayHavenView.DismissType.OptIn;

                if(mPurchases != null)
                    dismiss = PlayHavenView.DismissType.Purchase;

                mPlacement.getListener().contentDismissed(mPlacement, dismiss, generateResponseBundle());

                // Unregister the web view client so that any future dispatches will be ignored.
                HTMLView.this.setWebViewClient(null);

                break;
            /**
             * launch retrieves a URL from the server to be parsed using
             * Intent.ACTION_VIEW
             */
            case launch:
                mPlacement.getListener().contentDismissed(mPlacement, PlayHavenView.DismissType.Launch, null);

                /*
                 * We can't get this from the original model because we don't
                 * know which one they picked (if this was a more_games template).
                 */
                String url;
                try {
                    url = new JSONObject(dispatchContext).getString("url");
                } catch (JSONException jse) {
                    PlayHaven.e("Could not parse launch URL.");
                    return;
                }

                UrlRequest urlRequest = new UrlRequest(url);
                ExecutorService pool = Executors.newSingleThreadExecutor();
                final Future<String> uriFuture = pool.submit(urlRequest);
                final String initialUrl = url;

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // Wait for our final link.
                        String url = null;
                        try {
                            url = uriFuture.get();
                        } catch (Exception e) {
                            PlayHaven.v("Could not retrieve launch URL from server. Using initial url.");

                            // If the redirect failed, proceed with the original url.
                            url = initialUrl;
                        }

                        // Launch whatever it is. It might be a Play, web, or other link
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                        try {
                            HTMLView.this.getContext().startActivity(intent);
                        } catch (Exception e) {
                          PlayHaven.e("Unable to launch URI from template.");
                          e.printStackTrace();
                        }
                    }
                }).start();
                break;
            /**
             * loadContext passes the full "context" JSON blob to the
             * content template
             */
            case loadContext:
                this.loadUrl(DISPATCH_PROTOCOL_TEMPLATE);
                net.minidev.json.JSONObject context = JsonUtil.getPath(mPlacement.getModel(), "$.response.context");
                net.minidev.json.JSONObject data = JsonUtil.getPath(mPlacement.getDispatchModel(), "$.response.content_dispatch.data");

                /**
                 * @playhaven.apihack KitKat+ changed how the webview is loaded
                 */
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    this.evaluateJavascript(String.format(CALLBACK_TEMPLATE, callbackId, context, null, data), null);
                } else {
                    this.loadUrl(String.format(CALLBACK_TEMPLATE, callbackId, context, null, data));
                }
                break;
            /**
             * purchase stores the purchase object (which is generated by the
             * content template) as mPurchases, for use with dismiss dispatch
             */
            case purchase:
                collectAttachments(dispatchContext);
                break;
            /**
             * reward stores the reward object (which is generated by the
             * content template) as mRewards, for use with dismiss dispatch
             */
            case reward:
                net.minidev.json.JSONObject rewardParam = JsonUtil.getPath(mPlacement.getModel(), "$.response.context.content.open_dispatch.parameters");
                if(rewardParam == null || rewardParam.size() == 0) {
                    // data_collection template sends a reward dispatch when it submits form data ...
                    // @TODO: have templates return more than key/value pairs (eg class, pattern)
                    this.loadUrl(COLLECT_FORM_DATA);
                }

                collectAttachments(dispatchContext);
                break;
            /**
             * subcontent takes a JSON blob generated by the content template
             * and uses that to get data for a new impression, currently a
             * more_games widget that follows a featured ad
             */
            case subcontent:
        try{
                    SubcontentRequest subcontentRequest = new SubcontentRequest(dispatchContext);
                    subcontentRequest.setResponseHandler(mPlacement);
                    subcontentRequest.send(getContext());
                } catch (PlayHavenException e) {
                    e.printStackTrace();
                }
                break;
            /**  @TODO Find out why this dispatch was abandoned in 1.12 */
            case track:
                PlayHaven.d("track callback not implemented.");
                break;
            /**
             * This is one injected to let the Android SDK harvest data from the
             * opt-in data collection form.
             */
            case dcData:
                try {
                    mDataFields = DataCollectionField.fromUrl(callbackUri);
                } catch (PlayHavenException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }

    /**
     * Parses rewards and purchases out of the model and stores them for
     * disbursal upon dismiss dispatch
    */
    public void collectAttachments(String dispatchContext) {
        if(JsonUtil.hasPath(dispatchContext, "$.purchases"))
            mPurchases = Purchase.fromJson(dispatchContext);

        if(JsonUtil.hasPath(dispatchContext, "$.rewards"))
            mRewards = Reward.fromJson(dispatchContext);
    }

    /**
     * Loads a url into the this webview, ensures
     * that load occurs on UI thread.
     * @param url to load
     */
    public void load(final String url) {
        this.post(new Runnable() {
            @SuppressLint("InlinedApi")
      @Override
            public void run() {
                String fileUrl = null;
                try {
                    Cache cache = new Cache(getContext());
                    File template = cache.getFile(new URL(url));
                    cache.close();
                    if (template != null && template.exists()) {
                        fileUrl = "file:///" + template.getAbsolutePath();
                    }
                } catch (Exception e) {
                    PlayHaven.e(e);
                }

                if (fileUrl != null && fileUrl.length() > 1) {
                    PlayHaven.v("Loading from cache: %s.", fileUrl);
                    HTMLView.this.loadUrl(fileUrl);
                } else {
                    HTMLView.this.loadUrl(url);
                }

                setBackgroundColor(0x00000000);
                /**
                 * @playhaven.apihack WebView has flickering & transparency issues when hardware acceleration is enabled.
                 */
                if (Build.VERSION.SDK_INT >= 11){
                  setLayerType(View.LAYER_TYPE_SOFTWARE, null);
                }
            }
        });
    }

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    public void setPlacement(Placement placement) {
        mPlacement = placement;
        this.getSettings().setJavaScriptEnabled(true);
        this.setWebViewClient(webViewClient);
        this.setWebChromeClient(webChromeClient);

        try {
          // If loading new placement into existing view, start over.
          mImages = null;

            /**
             * @playhaven.apihack After API 11, we can intercept requests for images.
             */
          if(Build.VERSION.SDK_INT >= 11) {
            mImages = JsonUrlExtractor.getImages(mPlacement.getModel());
          }
    } finally {
      if(mImages == null) mImages = new ArrayList<String>();
    }

        load(JsonUtil.<String>getPath(mPlacement.getModel(), "$.response.url"));
    }

    /**
     * Create a response bundle for passing back to the publisher
     *
     * @return bundle containing data
     */
    @Override
    public Bundle generateResponseBundle() {
        Bundle data = null;

        if(mRewards != null) {
            data = new Bundle();
            data.putParcelableArrayList(PlayHavenView.BUNDLE_DATA_REWARD, mRewards);
        }

        if(mDataFields != null) {
            if(data == null) data = new Bundle();
            data.putParcelableArrayList(PlayHavenView.BUNDLE_DATA_OPTIN, mDataFields);
        }

        if(mPurchases != null) {
            if(data == null) data = new Bundle();
            data.putParcelableArrayList(PlayHavenView.BUNDLE_DATA_PURCHASE, mPurchases);
        }

        return data;
    }

}




Java Source Code List

com.playhaven.android.DeviceId.java
com.playhaven.android.PlacementListener.java
com.playhaven.android.Placement.java
com.playhaven.android.PlayHavenException.java
com.playhaven.android.PlayHaven.java
com.playhaven.android.PushPlacement.java
com.playhaven.android.cache.BulkCacheDownloader.java
com.playhaven.android.cache.CacheCleaner.java
com.playhaven.android.cache.CacheDownloader.java
com.playhaven.android.cache.CacheResponseHandler.java
com.playhaven.android.cache.Cache.java
com.playhaven.android.cache.CachedInfo.java
com.playhaven.android.cache.package-info.java
com.playhaven.android.compat.UnityCompat.java
com.playhaven.android.compat.VendorCompat.java
com.playhaven.android.data.ContentUnitType.java
com.playhaven.android.data.CustomEvent.java
com.playhaven.android.data.DataCollectionField.java
com.playhaven.android.data.JsonUrlExtractor.java
com.playhaven.android.data.Purchase.java
com.playhaven.android.data.Reward.java
com.playhaven.android.data.package-info.java
com.playhaven.android.diagnostic.APIValidationIndicators.java
com.playhaven.android.diagnostic.DiagnosticApp.java
com.playhaven.android.diagnostic.DiagnosticPreferences.java
com.playhaven.android.diagnostic.InstrumentationReceiver.java
com.playhaven.android.diagnostic.Launcher.java
com.playhaven.android.diagnostic.OutputBox.java
com.playhaven.android.diagnostic.RequestTypeAdapter.java
com.playhaven.android.diagnostic.RequestType.java
com.playhaven.android.examples.ContentExample2.java
com.playhaven.android.examples.ContentExample.java
com.playhaven.android.examples.DialogExample.java
com.playhaven.android.examples.DisplayOptExample.java
com.playhaven.android.examples.EmbeddedExample.java
com.playhaven.android.examples.IAPTrackingExample.java
com.playhaven.android.examples.LoggingExample.java
com.playhaven.android.examples.MoreGamesExample.java
com.playhaven.android.examples.NoContent1Example.java
com.playhaven.android.examples.NoContent2Example.java
com.playhaven.android.examples.OnResumeExample.java
com.playhaven.android.examples.OpenExample.java
com.playhaven.android.examples.OptInExample.java
com.playhaven.android.examples.PreloadDialogExample.java
com.playhaven.android.examples.PreloadExample.java
com.playhaven.android.examples.PurchaseExample.java
com.playhaven.android.examples.RewardExample.java
com.playhaven.android.examples.WebviewFullscreenExample.java
com.playhaven.android.push.GCMBroadcastReceiver.java
com.playhaven.android.push.GCMRegistrationRequest.java
com.playhaven.android.push.NotificationBuilder.java
com.playhaven.android.push.PushReceiver.java
com.playhaven.android.req.ContentDispatchRequest.java
com.playhaven.android.req.ContentDispatchType.java
com.playhaven.android.req.ContentRequest.java
com.playhaven.android.req.ContentUnitRequest.java
com.playhaven.android.req.CustomEventRequest.java
com.playhaven.android.req.Identifier.java
com.playhaven.android.req.InstallRequest.java
com.playhaven.android.req.MetadataRequest.java
com.playhaven.android.req.NoContentException.java
com.playhaven.android.req.NoIdentifierException.java
com.playhaven.android.req.OpenRequest.java
com.playhaven.android.req.PlayHavenRequest.java
com.playhaven.android.req.PurchaseTrackingRequest.java
com.playhaven.android.req.PushTrackingRequest.java
com.playhaven.android.req.RequestListener.java
com.playhaven.android.req.ServerErrorHandler.java
com.playhaven.android.req.SignatureException.java
com.playhaven.android.req.SubcontentRequest.java
com.playhaven.android.req.UrlRequest.java
com.playhaven.android.req.UserAgent.java
com.playhaven.android.req.package-info.java
com.playhaven.android.util.DisplayUtil.java
com.playhaven.android.util.GoogleAdvertisementUtil.java
com.playhaven.android.util.GooglePlayServicesUtil.java
com.playhaven.android.util.JsonUtil.java
com.playhaven.android.util.KontagentUtil.java
com.playhaven.android.util.MemoryReporter.java
com.playhaven.android.util.TimeZoneFormatter.java
com.playhaven.android.util.package-info.java
com.playhaven.android.view.Badge.java
com.playhaven.android.view.ChildView.java
com.playhaven.android.view.DefaultPlayHavenListener.java
com.playhaven.android.view.FrameManager.java
com.playhaven.android.view.FullScreen.java
com.playhaven.android.view.HTMLView.java
com.playhaven.android.view.MoreGames.java
com.playhaven.android.view.NativeView.java
com.playhaven.android.view.PlayHavenListener.java
com.playhaven.android.view.PlayHavenView.java
com.playhaven.android.view.Windowed.java
com.playhaven.android.view.package-info.java
com.playhaven.android.package-info.java
javax.annotation.Generated.java
javax.annotation.package-info.java
${package}.__artifactId__Activity.java