com.scoreflex.ScoreflexView.java Source code

Java tutorial

Introduction

Here is the source code for com.scoreflex.ScoreflexView.java

Source

/*
 * Licensed to Scoreflex (www.scoreflex.com) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. Scoreflex licenses this
 * file to you 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.scoreflex;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.scoreflex.Scoreflex.Response;
import com.scoreflex.facebook.ScoreflexFacebookWrapper;
import com.scoreflex.facebook.ScoreflexFacebookWrapper.FacebookException;
import com.scoreflex.google.ScoreflexGoogleWrapper;
import com.scoreflex.model.JSONParcelable;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.webkit.WebChromeClient;
import android.webkit.ConsoleMessage;

/**
 * An Android view that displays Scoreflex content. A ScoreflexView
 * will hold a WebView pointing to Scoreflex web content.
 *
 *
 */
@SuppressLint("SetJavaScriptEnabled")
public class ScoreflexView extends FrameLayout {
    WebView mWebView;
    Activity mParentActivity;
    ViewGroup mErrorLayout;
    TextView mMessageView;
    ProgressBar mProgressBar;
    ScoreflexWebCallbackHandler mWebCallbackHandler = new ScoreflexWebCallbackHandler();
    ImageButton mCloseButton;
    ScoreflexView mSubview;
    Uri mOutputFileUri;
    UserInterfaceState mUserInterfaceState;
    ValueCallback<Uri> mUploadMessage;
    String mInitialResource;
    ScoreflexViewListener mScoreflexViewHandler;
    Scoreflex.RequestParams mInitialRequestParams;
    boolean mIsPreloading;
    protected boolean isLoginSource;

    /**
     * The constructor of the view.
     * @param activity The activity holding the view.
     * @param attrs
     * @param defStyle
     */
    @SuppressWarnings("deprecation")
    public ScoreflexView(Activity activity, AttributeSet attrs, int defStyle) {
        super(activity, attrs, defStyle);

        // Keep a reference on the activity
        mParentActivity = activity;

        // Default layout params
        if (null == getLayoutParams()) {
            setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    Scoreflex.getDensityIndependantPixel(R.dimen.scoreflex_panel_height)));
        }

        // Set our background color
        setBackgroundColor(getContext().getResources().getColor(R.color.scoreflex_background_color));

        // Create the top bar
        View topBar = new View(getContext());
        topBar.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                getResources().getDimensionPixelSize(R.dimen.scoreflex_topbar_height), Gravity.TOP));
        topBar.setBackgroundDrawable(getResources().getDrawable(R.drawable.scoreflex_gradient_background));
        addView(topBar);

        // Create the retry button
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mErrorLayout = (ViewGroup) inflater.inflate(R.layout.scoreflex_error_layout, null);
        if (mErrorLayout != null) {

            // Configure refresh button
            Button refreshButton = (Button) mErrorLayout.findViewById(R.id.scoreflex_retry_button);
            if (null != refreshButton) {
                refreshButton.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        if (null == Scoreflex.getPlayerId()) {
                            setUserInterfaceState(new LoadingState());
                            loadUrlAfterLoggedIn(mInitialResource, mInitialRequestParams);
                        } else if (null == mWebView.getUrl()) {
                            setResource(mInitialResource);
                        } else {
                            mWebView.reload();
                        }
                    }
                });
            }

            // Configure cancel button
            Button cancelButton = (Button) mErrorLayout.findViewById(R.id.scoreflex_cancel_button);
            if (null != cancelButton) {
                cancelButton.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        close();
                    }
                });
            }

            // Get hold of the message view
            mMessageView = (TextView) mErrorLayout.findViewById(R.id.scoreflex_error_message_view);
            addView(mErrorLayout);

        }

        // Create the close button
        mCloseButton = new ImageButton(getContext());
        Drawable closeButtonDrawable = getResources().getDrawable(R.drawable.scoreflex_close_button);
        int closeButtonMargin = (int) ((getResources().getDimensionPixelSize(R.dimen.scoreflex_topbar_height)
                - closeButtonDrawable.getIntrinsicHeight()) / 2.0f);
        FrameLayout.LayoutParams closeButtonLayoutParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
                Gravity.TOP | Gravity.RIGHT);
        closeButtonLayoutParams.setMargins(0, closeButtonMargin, closeButtonMargin, 0);
        mCloseButton.setLayoutParams(closeButtonLayoutParams);
        mCloseButton.setImageDrawable(closeButtonDrawable);
        mCloseButton.setBackgroundDrawable(null);
        mCloseButton.setPadding(0, 0, 0, 0);
        mCloseButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {
                close();
            }
        });
        addView(mCloseButton);

        // Create the web view
        mWebView = new WebView(mParentActivity);
        mWebView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        // mWebView.setBackgroundColor(Color.RED);
        mWebView.setWebViewClient(new ScoreflexWebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient() {

            @Override
            public boolean onConsoleMessage(ConsoleMessage cm) {
                Log.d("Scoreflex", "javascript Error: "
                        + String.format("%s @ %d: %s", cm.message(), cm.lineNumber(), cm.sourceId()));
                return true;
            }

            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                // mtbActivity.mUploadMessage = uploadMsg;
                mUploadMessage = uploadMsg;

                String fileName = "picture.jpg";
                ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.TITLE, fileName);
                // mOutputFileUri = mParentActivity.getContentResolver().insert(
                // MediaStore.Images.Media.DATA, values);
                final List<Intent> cameraIntents = new ArrayList<Intent>();
                final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                final PackageManager packageManager = mParentActivity.getPackageManager();
                final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
                for (ResolveInfo res : listCam) {
                    final String packageName = res.activityInfo.packageName;
                    final Intent intent = new Intent(captureIntent);
                    intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
                    intent.setPackage(packageName);
                    cameraIntents.add(intent);
                }

                // Filesystem.
                final Intent galleryIntent = new Intent();
                galleryIntent.setType("image/*");
                galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

                // Chooser of filesystem options.
                final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source please");

                // Add the camera options.
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {}));

                mParentActivity.startActivityForResult(chooserIntent, Scoreflex.FILECHOOSER_RESULTCODE);
            }

            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
                openFileChooser(uploadMsg);
            }

            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                openFileChooser(uploadMsg);
            }
        });
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.getSettings().setDomStorageEnabled(true);
        mWebView.getSettings().setDatabasePath(
                getContext().getFilesDir().getPath() + "/data/" + getContext().getPackageName() + "/databases/");
        addView(mWebView);

        TypedArray a = mParentActivity.obtainStyledAttributes(attrs, R.styleable.ScoreflexView, defStyle, 0);
        String resource = a.getString(R.styleable.ScoreflexView_resource);
        if (null != resource)
            setResource(resource);

        a.recycle();

        // Create the animated spinner

        mProgressBar = (ProgressBar) ((LayoutInflater) getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.scoreflex_progress_bar, null);
        mProgressBar.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
        addView(mProgressBar);
        LocalBroadcastManager.getInstance(activity).registerReceiver(mLoginReceiver,
                new IntentFilter(Scoreflex.INTENT_USER_LOGED_IN));
        setUserInterfaceState(new InitialState());
    }

    /**
     * The constructor of the view.
     * @param activity The activity hodling the view.
     * @param attrs
     */
    public ScoreflexView(Activity activity, AttributeSet attrs) {
        this(activity, attrs, 0);
    }

    /**
     * The constructor of the view.
     * @param activity The activity hodling the view.
     */
    public ScoreflexView(Activity activity) {
        this(activity, null);
    }

    private void setUserInterfaceState(UserInterfaceState state) {
        if (null != mUserInterfaceState)
            mUserInterfaceState.onLeaveState();
        mUserInterfaceState = state;
        state.onEnterState();
        requestLayout();
    }

    /**
     * Can the webview go back (is its history stack non-empty) ?
     *
     * @return Wether the view can go back or not.
     */
    public boolean canGoBack() {
        return mWebView.canGoBack();
    }

    /**
     * Makes the webview go back.
     */
    public void goBack() {
        mWebView.goBack();
    }

    private void loadUrlAfterLoggedIn(final String resource, final Scoreflex.RequestParams params) {

        ScoreflexRestClient.fetchAnonymousAccessToken(new Scoreflex.ResponseHandler() {

            @Override
            public void onFailure(Throwable e, Response errorResponse) {
                mMessageView.setText(R.string.scoreflex_network_error);
                setUserInterfaceState(new ErrorState());
            }

            @Override
            public void onSuccess(Response response) {
                ScoreflexRequestParamsDecorator.decorate(resource, params);
                String url = String.format(Locale.getDefault(), "%s?%s",
                        ScoreflexUriHelper.getNonSecureAbsoluteUrl(resource), params.getURLEncodedString());
                mWebView.loadUrl(url);
            }
        }, 0);
    }

    /**
     * Preloads all the static resources of the selected url.
     *
     * @param resource The url to preload..
     */
    public void preloadResource(String resource) {

        setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        mIsPreloading = true;
        mInitialResource = resource;
        Scoreflex.RequestParams params = new Scoreflex.RequestParams();
        if (Scoreflex.getPlayerId() != null) {
            ScoreflexRequestParamsDecorator.decorate(resource, params);
            String url = String.format(Locale.getDefault(), "%s?%s",
                    ScoreflexUriHelper.getNonSecureAbsoluteUrl(resource), params.getURLEncodedString());

            mWebView.loadUrl(url);
        } else {
            loadUrlAfterLoggedIn(resource, new Scoreflex.RequestParams());
        }
    }

    public void startOpeningAnimation() {
        int gravity = getLayoutGravity();
        int anim = (Gravity.TOP == (gravity & Gravity.VERTICAL_GRAVITY_MASK)) ? R.anim.scoreflex_enter_slide_down
                : R.anim.scoreflex_enter_slide_up;
        Animation animation = AnimationUtils.loadAnimation(mParentActivity, anim);
        this.startAnimation(animation);
    }

    /**
     * Sets the listener that will be called during the lifecycle of the view
     * @param listener the listener
     */
    public void setScoreflexViewListener(ScoreflexViewListener listener) {
        mScoreflexViewHandler = listener;
    }

    /**
     * Closes this ScoreflexView. If the view is attached to the developer
     * hierarchy it will be detached. If the view is attached to a
     * ScoreflexActivity, this activity will be terminated.
     */
    public void close() {
        // If we're displayed in the developer view hierarchy,
        // remove ourselves.
        final ViewParent parent = this.getParent();
        ViewGroup.LayoutParams currentLayoutParams = getLayoutParams();
        boolean isFullscreen = currentLayoutParams.height == LayoutParams.MATCH_PARENT;

        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mLoginReceiver);

        if (isFullscreen && null != parent && parent instanceof ViewGroup) {
            int gravity = getLayoutGravity();
            int anim = (Gravity.TOP == (gravity & Gravity.VERTICAL_GRAVITY_MASK)) ? R.anim.scoreflex_exit_slide_up
                    : R.anim.scoreflex_exit_slide_down;
            Animation animation = AnimationUtils.loadAnimation(mParentActivity, anim);
            animation.setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationEnd(Animation arg0) {
                    ViewGroup parentGroup = (ViewGroup) parent;
                    parentGroup.removeView(ScoreflexView.this);
                    if (mScoreflexViewHandler != null) {
                        mScoreflexViewHandler.onViewClosed();
                        mScoreflexViewHandler = null;
                    }
                }

                @Override
                public void onAnimationRepeat(Animation arg0) {

                }

                @Override
                public void onAnimationStart(Animation arg0) {

                }

            });
            startAnimation(animation);
        } else if (null != parent && parent instanceof ViewGroup) {
            ViewGroup parentGroup = (ViewGroup) parent;
            parentGroup.removeView(this);
            if (mScoreflexViewHandler != null) {
                mScoreflexViewHandler.onViewClosed();
                mScoreflexViewHandler = null;
            }
        }
    }

    /**
     * Sets the resource for the web content displayed in this ScoreflexView's
     * webview.
     *
     * @param resource
     */
    public void setResource(String resource) {
        setResource(resource, null, false);
    }

    protected int getLayoutGravity() {
        ViewGroup.LayoutParams currentLayoutParams = getLayoutParams();
        int gravity = Scoreflex.getDefaultGravity() & Gravity.VERTICAL_GRAVITY_MASK;
        if (currentLayoutParams instanceof FrameLayout.LayoutParams) {
            FrameLayout.LayoutParams frameLayoutParams = (FrameLayout.LayoutParams) currentLayoutParams;
            gravity = (Gravity.TOP == (frameLayoutParams.gravity & Gravity.VERTICAL_GRAVITY_MASK)) ? Gravity.TOP
                    : Gravity.BOTTOM;
        }
        return gravity;
    }

    protected View openNewView(String resource, Scoreflex.RequestParams params, boolean forceFullScreen) {
        int gravity = getLayoutGravity();
        int anim = (Gravity.TOP == (gravity & Gravity.VERTICAL_GRAVITY_MASK)) ? R.anim.scoreflex_enter_slide_down
                : R.anim.scoreflex_enter_slide_up;
        Animation animation = AnimationUtils.loadAnimation(mParentActivity, anim);

        ScoreflexView webview = new ScoreflexView(mParentActivity);
        webview.setResource(resource, params, forceFullScreen);
        ViewGroup contentView = (ViewGroup) mParentActivity.getWindow().getDecorView()
                .findViewById(android.R.id.content);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT, gravity);
        webview.setLayoutParams(layoutParams);
        contentView.addView(webview);
        webview.startAnimation(animation);
        Scoreflex.setCurrentScoreflexView(this);
        return webview;
    }

    /**
     * Sets the resource for the web content displayed in this ScoreflexView's
     * webview.
     *
     * @param resource
     * @param params
     * @param forceFullScreen
     */
    public void setResource(String resource, Scoreflex.RequestParams params, boolean forceFullScreen) {
        if (null == resource) {
            Log.e("Scoreflex", "null resource provided to ScoreflexView");
            return;
        }
        mInitialResource = resource;
        mInitialRequestParams = params;
        mIsPreloading = false;
        if (null == params)
            params = new Scoreflex.RequestParams();

        ScoreflexRequestParamsDecorator.decorate(resource, params);

        ViewGroup.LayoutParams currentLayoutParams = getLayoutParams();
        boolean isFullscreen = currentLayoutParams.height == LayoutParams.MATCH_PARENT;

        if (isFullscreen && forceFullScreen) {
            Scoreflex.setCurrentScoreflexView(this);
        }

        if (isFullscreen || forceFullScreen == false) {
            if (Scoreflex.getPlayerId() != null) {
                String url = String.format(Locale.getDefault(), "%s?%s",
                        ScoreflexUriHelper.getNonSecureAbsoluteUrl(resource), params.getURLEncodedString());
                mWebView.loadUrl(url);
            } else {
                loadUrlAfterLoggedIn(resource, params);
            }

            return;
        }

        if (Scoreflex.getPlayerId() != null) {
            String url = String.format(Locale.getDefault(), "%s?%s#start",
                    ScoreflexUriHelper.getNonSecureAbsoluteUrl(resource), params.getURLEncodedString());
            mWebView.loadUrl(url);
        } else {
            loadUrlAfterLoggedIn(resource, params);
        }
        Scoreflex.setCurrentScoreflexView(this);
        // String fullUrl = String.format("%s?%s",
        // ScoreflexUriHelper.getNonSecureAbsoluteUrl(resource),
        // params != null ? params.getURLEncodedString(): "");
        int gravity = getLayoutGravity();
        FrameLayout.LayoutParams newLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT, gravity);
        this.setLayoutParams(newLayoutParams);

        this.startOpeningAnimation();
        return;

    }

    /**
     * Sets the resource for the web content displayed in this ScoreflexView's
     * webview.
     *
     * @param resource
     * @param params
     */
    public void setResource(String resource, Scoreflex.RequestParams params) {
        setResource(resource, params, false);
    }

    /**
     * Sets the full URL for the web content displayed in this ScoreflexView's
     * webview.
     *
     * @param fullUrl
     *          A full Scoreflex URL.
     * @throws IllegalArgumentException
     *           When the provided URL is not a Scoreflex URL.
     */
    public void setFullUrl(String fullUrl) throws IllegalArgumentException {
        if (fullUrl == null)
            return;

        Uri parsedUri = Uri.parse(fullUrl);

        if (!ScoreflexUriHelper.isAPIUri(parsedUri))
            throw new IllegalArgumentException(String.format("Not a Scoreflex URL: %s", fullUrl));

        setResource(ScoreflexUriHelper.getResource(parsedUri), ScoreflexUriHelper.getParams(parsedUri));
    }

    /**
     * Sets the full URL for the web content displayed in this ScoreflexView's
     * webview.
     *
     * @param fullUrl
     *          A full Scoreflex URL
     * @throws IllegalArgumentException
     *           When the provided URL is not a Scoreflex URL
     */
    public void setFullUrl(String fullUrl, boolean forceFullScreen, boolean newView)
            throws IllegalArgumentException {
        Uri parsedUri = Uri.parse(fullUrl);

        if (!ScoreflexUriHelper.isAPIUri(parsedUri))
            throw new IllegalArgumentException(String.format("Not a Scoreflex URL: %s", fullUrl));
        if (!newView) {
            setResource(ScoreflexUriHelper.getResource(parsedUri), ScoreflexUriHelper.getParams(parsedUri),
                    forceFullScreen);
            return;
        }
        openNewView(ScoreflexUriHelper.getResource(parsedUri), ScoreflexUriHelper.getParams(parsedUri),
                forceFullScreen);
    }

    /**
     * Removes the drop shadow.
     *
     */
    @SuppressWarnings("deprecation")
    public void removeDropShadow() {
        setBackgroundDrawable(null);
        setPadding(0, 0, 0, 0);
    }

    /**
     * Adds a drop shadow to this view.
     *
     * @param gravity
     *          The gravity of the Scoreflex view (not the drop shadow's).
     */
    @SuppressWarnings("deprecation")
    public void addDropShadow(int gravity) {
        boolean top = Gravity.TOP != (gravity & Gravity.VERTICAL_GRAVITY_MASK);
        setBackgroundDrawable(getContext().getResources()
                .getDrawable(top ? R.drawable.scoreflex_top_shadow : R.drawable.scoreflex_bottom_shadow));
        int height = getResources().getDimensionPixelSize(R.dimen.scoreflex_shadow_height);
        setPadding(0, top ? height : 0, 0, top ? 0 : height);
    }

    /**
     * An implementation of {@link android.webkit.WebViewClient} that queries the
     * {@link ScoreflexWebCallbackHandler} before loading any URL.
     *
     *
     *
     */
    private class ScoreflexWebViewClient extends WebViewClient {

        private boolean mError;
        private boolean mIsLoading;
        private Timer mWebviewTimer;
        private Handler mMainThreadHandler;

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Uri uri = Uri.parse(url);

            // Handle market urls
            // https://play.google.com/store/apps/details?id=com.yakaz.mtb
            if ("market".equals(uri.getScheme()) || "play.google.com".equals(uri.getHost())) {

                Intent intent = new Intent(Intent.ACTION_VIEW,
                        Uri.parse(url.replace("https://play.google.com/store/apps", "market:/")));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                Scoreflex.getApplicationContext().startActivity(intent);
                return true;
            }
            // Handle web callback
            if (mWebCallbackHandler.handleWebCallback(uri)) {
                return true;
            }

            return false;
        }

        @Override
        public void onPageStarted(final WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            // OnPageStarted is called twice on error
            if (!mIsLoading) {
                mError = false;
                setUserInterfaceState(new LoadingState());
            }
            if (null != mWebviewTimer) {
                mWebviewTimer.cancel();
                mWebviewTimer = null;
            }
            mMainThreadHandler = new Handler();
            mWebviewTimer = new Timer("webviewTimeout", true);
            mWebviewTimer.schedule(new TimerTask() {

                @Override
                public void run() {
                    mMainThreadHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            view.stopLoading();
                            mMessageView.setText(R.string.scoreflex_network_error);
                            setUserInterfaceState(new ErrorState());
                            // if we are preloading we must remove the preloaded view from the preloaded view pool
                            if (ScoreflexView.this.mIsPreloading) {
                                Scoreflex.freePreloadedResources(ScoreflexView.this.mInitialResource);
                            }
                        }
                    });
                }
            }, Scoreflex.WEBVIEW_REQUEST_TOTAL_TIMEOUT);

            mIsLoading = true;
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);

            if (null != failingUrl && failingUrl.equals(view.getUrl())) {
                mError = true;
                if (null != mMessageView)
                    mMessageView.setText(description);
                setUserInterfaceState(new ErrorState());
            }
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (mWebviewTimer != null) {
                mWebviewTimer.cancel();
            }
            mMainThreadHandler = null;
            mWebviewTimer = null;
            mIsLoading = false;
            if (mIsPreloading) {
                Uri parsedUri = Uri.parse(url);
                String resource = ScoreflexUriHelper.getResource(parsedUri);
                Intent intent = new Intent(Scoreflex.INTENT_RESOURCE_PRELOADED);
                intent.putExtra(Scoreflex.INTENT_RESOURCE_PRELOADED_EXTRA_PATH, resource);

                LocalBroadcastManager.getInstance(Scoreflex.getApplicationContext()).sendBroadcast(intent);
            }
            if (null != url && url.equals(view.getUrl())) {
                if (!mError) {
                    setUserInterfaceState(new WebContentState());
                }
            }
        }

    }

    /**
     * A class that optionally handles a web callback uri. The callback uri should
     * be "/web/callback" and provide a code and a status. Depending on these 2
     * parameters, this object will trigger various behaviors.
     *
     *
     *
     */
    private class ScoreflexWebCallbackHandler {
        private static final String sCallbackResource = "/web/callback";

        private String mAuthGrantedNextUrlString;
        private String mAuthState;

        protected boolean handleWebCallback(Uri uri) {
            // Only handle when uri is the callback resource
            if (!sCallbackResource.equals(ScoreflexUriHelper.getResource(uri)))
                return false;

            // Parse status and code
            String statusString = uri.getQueryParameter("status");
            String codeString = uri.getQueryParameter("code");
            int status, code;
            try {
                status = Integer.parseInt(statusString);
                code = Integer.parseInt(codeString);
            } catch (NumberFormatException e) {
                Log.e("Scoreflex",
                        String.format("Invalid status or code (should be an int): %s %s", statusString, codeString),
                        e);
                return false;
            }

            // Successes
            if (300 > status) {

                if (handleMoveToNewUrl(uri, status, code))
                    return true;

                if (handleCloseWebView(uri, status, code))
                    return true;

                if (handleNeedsAuth(uri, status, code))
                    return true;

                if (handleAuthGranted(uri, status, code))
                    return true;

                if (handleLogout(uri, status, code))
                    return true;

                if (handleNeedsClientAuth(uri, status, code))
                    return true;

                if (handleStartChallenge(uri, status, code))
                    return true;

                if (handlePlayLevel(uri, status, code))
                    return true;

                if (handleLinkService(uri, status, code))
                    return true;

                if (handleSocialInvite(uri, status, code))
                    return true;

                if (handleSocialShare(uri, status, code))
                    return true;

            } else {

                // 404 errors: let the user see them
                if (404 == status)
                    return false;

                // Generic errors
                if (handleGenericError(uri, status, code)) {
                    return true;
                }

                // Invalid SID
                if (handleInvalidSIDError(uri, status, code))
                    return true;

                // HTTPS required
                if (handleSecureConnectionRequiredError(uri, status, code))
                    return true;

                // Inactive game
                if (handleInactiveGameError(uri, status, code))
                    return true;
            }

            return false;
        }

        /*
         * case Scoreflex.ERROR_SECURE_CONNECTION_REQUIRED: res =
         * R.string.SCOREFLEX_ERROR_MISSING_MANDATORY_PARAMETER; break;
         *
         * case Scoreflex.ERROR_INACTIVE_GAME: res =
         * R.string.SCOREFLEX_ERROR_INACTIVE_GAME; break;
         */

        private boolean handlePlayLevel(Uri uri, int status, int code) {
            if (Scoreflex.SUCCESS_PLAY_LEVEL != code)
                return false;

            try {
                JSONObject data = new JSONObject(uri.getQueryParameter("data"));
                Intent intent = new Intent(Scoreflex.INTENT_PLAY_LEVEL);
                intent.putExtra(Scoreflex.INTENT_PLAY_LEVEL_EXTRA_LEADERBOARD_ID, data.getString("leaderboardId"));
                LocalBroadcastManager.getInstance(Scoreflex.getApplicationContext()).sendBroadcast(intent);
                close();
            } catch (Exception e) {

            }
            return false;
        }

        private boolean handleStartChallenge(Uri uri, int status, int code) {
            if (Scoreflex.SUCCESS_START_CHALLENGE != code)
                return false;

            try {
                JSONObject data = new JSONObject(uri.getQueryParameter("data"));
                Scoreflex.RequestParams params = new Scoreflex.RequestParams();
                params.put("fields", "core,turn,outcome,config");
                Scoreflex.get("/challenges/instances/" + data.getString("challengeInstanceId"), params,
                        new Scoreflex.ResponseHandler() {
                            public void onFailure(Throwable e, Response errorResponse) {

                            }

                            public void onSuccess(Response response) {
                                JSONParcelable challengeInstance = new JSONParcelable(response.getJSONObject());
                                Intent intent = new Intent(Scoreflex.INTENT_START_CHALLENGE);
                                intent.putExtra(Scoreflex.INTENT_START_CHALLENGE_EXTRA_INSTANCE, challengeInstance);
                                LocalBroadcastManager.getInstance(Scoreflex.getApplicationContext())
                                        .sendBroadcast(intent);
                                Scoreflex.startPlayingSession();
                                close();
                            }
                        });
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return false;
        }

        private boolean handleInactiveGameError(Uri uri, int status, int code) {
            if (Scoreflex.ERROR_INACTIVE_GAME != code)
                return false;

            close();

            return true;

        }

        private boolean handleSecureConnectionRequiredError(Uri uri, int status, int code) {
            if (Scoreflex.ERROR_SECURE_CONNECTION_REQUIRED != code)
                return false;

            mWebView.loadUrl(uri.toString().replace("http:", "https:"));
            return true;
        }

        private boolean handleInvalidSIDError(Uri uri, int status, int code) {

            if (Scoreflex.ERROR_INVALID_SID != code)
                return false;

            // Invalidate access token
            ScoreflexRestClient.setAccessToken(null, true);

            // Invalidate SID
            ScoreflexRestClient.setSID(null);

            // Request a new anonymous access token
            ScoreflexRestClient.fetchAnonymousAccessTokenIfNeeded();

            return true;
        }

        private boolean handleGenericError(Uri uri, int status, int code) {
            int res;
            switch (code) {
            case Scoreflex.ERROR_INVALID_PARAMETER:
                res = R.string.SCOREFLEX_ERROR_INVALID_PARAMETER;
                break;
            case Scoreflex.ERROR_MISSING_MANDATORY_PARAMETER:
                res = R.string.SCOREFLEX_ERROR_INVALID_PARAMETER;
                break;
            case Scoreflex.ERROR_INVALID_PREV_NEXT_PARAMETER:
                res = R.string.SCOREFLEX_ERROR_INVALID_PREV_NEXT_PARAMETER;
                break;
            case Scoreflex.ERROR_SANDBOX_URL_REQUIRED:
                res = R.string.SCOREFLEX_ERROR_INVALID_PREV_NEXT_PARAMETER;
                break;
            case Scoreflex.ERROR_MISSING_PERMISSIONS:
                res = R.string.SCOREFLEX_ERROR_MISSING_PERMISSIONS;
                break;
            case Scoreflex.ERROR_PLAYER_DOES_NOT_EXIST:
                res = R.string.SCOREFLEX_ERROR_PLAYER_DOES_NOT_EXIST;
                break;
            case Scoreflex.ERROR_DEVELOPER_DOES_NOT_EXIST:
                res = R.string.SCOREFLEX_ERROR_DEVELOPER_DOES_NOT_EXIST;
                break;
            case Scoreflex.ERROR_GAME_DOES_NOT_EXIST:
                res = R.string.SCOREFLEX_ERROR_GAME_DOES_NOT_EXIST;
                break;
            case Scoreflex.ERROR_LEADERBOARD_CONFIG_DOES_NOT_EXIST:
                res = R.string.SCOREFLEX_ERROR_LEADERBOARD_CONFIG_DOES_NOT_EXIST;
                break;
            case Scoreflex.ERROR_SERVICE_EXCEPTION:
                res = R.string.SCOREFLEX_ERROR_SERVICE_EXCEPTION;
                break;
            default:
                return false;
            }

            Log.e("Scoreflex", getContext().getString(res));

            // In sandbox mode, we let the developer see the error page.
            if (Scoreflex.usesSandbox())
                return false;

            // In production, the error is handled by doing nothing
            return true;
        }

        private boolean handleLogout(Uri uri, int status, int code) {

            if (code != Scoreflex.SUCCESS_LOGOUT)
                return false;

            // Invalidate access token
            ScoreflexRestClient.setAccessToken(null, true);

            // Invalidate SID
            ScoreflexRestClient.setSID(null);

            // if (null != mParentActivity)
            // ScoreflexGoogleWrapper.logout(mParentActivity);

            if (null != mParentActivity)
                try {
                    ScoreflexFacebookWrapper.logout(mParentActivity);
                } catch (FacebookException e) {
                    Log.e("Scoreflex", "Error signing out of Facebook SDK", e);
                }

            // Remove all cookies
            CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(getContext());
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.removeAllCookie();
            cookieManager.removeSessionCookie();
            cookieSyncManager.sync();

            // Request a new anonymous access token
            ScoreflexRestClient.fetchAnonymousAccessTokenIfNeeded();

            close();

            return true;

        }

        private void handleLoggedInResponse(JSONObject json) {
            if (null == json) {
                Log.e("Scoreflex", "Error authenticating, server didn't return a JSON response");
                return;
            }

            JSONObject accessToken = json.optJSONObject("accessToken");
            if (null == accessToken || !accessToken.has("token")) {
                Log.e("Scoreflex", "Error authenticating, server didn't return an access token");
                return;
            }

            if (!json.has("sid")) {
                Log.e("Scoreflex", "Error authenticating, server didn't return an sid");
                return;
            }

            String token = accessToken.optString("token");
            String sid = json.optString("sid");
            JSONObject meObject = json.optJSONObject("me");
            String playerId = meObject.optString("id");
            ScoreflexRestClient.setAccessToken(token, false);
            ScoreflexRestClient.setSID(sid);
            ScoreflexRestClient.setPlayerId(playerId);

            Intent intent = new Intent(Scoreflex.INTENT_USER_LOGED_IN);
            intent.putExtra(Scoreflex.INTENT_USER_LOGED_IN_EXTRA_SID, sid);
            intent.putExtra(Scoreflex.INTENT_USER_LOGED_IN_EXTRA_ACCESS_TOKEN, token);
            isLoginSource = true;
            LocalBroadcastManager.getInstance(Scoreflex.getApplicationContext()).sendBroadcast(intent);

            if (null != mAuthGrantedNextUrlString) {
                openFullUrl(mAuthGrantedNextUrlString, false, false);
                mWebView.clearHistory();
            }
        }

        private boolean handleAuthGranted(Uri uri, int status, int code) {

            if (code != Scoreflex.SUCCESS_AUTH_GRANTED)
                return false;

            String dataString = uri.getQueryParameter("data");

            JSONObject dataJson;
            try {
                dataJson = new JSONObject(dataString);
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }

            // Check returned state against our recorded state
            String state = dataJson.optString("state");
            if (null == mAuthState) {
                Log.e("Scoreflex", "Error authenticating as we have no recorded state to check against");
                return true;
            }

            if (!mAuthState.equals(state)) {
                Log.e("Scoreflex", String.format(
                        "Error authenticating as the returned state doesn't match the one we recorded: %s != %s",
                        mAuthState, state));
                return true;
            }

            // Get the oauth code
            String codeString = dataJson.optString("code");

            if (null == codeString) {
                Log.e("Scoreflex", "Error authenticating, no oauth code returned");
                return true;
            }

            // Transform that code into a token
            Scoreflex.RequestParams params = new Scoreflex.RequestParams();
            params.put("code", codeString);
            params.put("clientId", Scoreflex.getClientId());
            params.put("devicePlatform", "Android");
            params.put("deviceModel", Scoreflex.getDeviceModel());
            String udid = Scoreflex.getUDID();
            if (null != udid)
                params.put("deviceId", udid);

            ScoreflexRestClient.post("/oauth/accessToken", params, new Scoreflex.ResponseHandler() {
                public void onFailure(Throwable e, Response errorResponse) {
                    Log.e("Scoreflex",
                            String.format("Error authenticating, could not transform code into accessToken: %s",
                                    errorResponse.getErrorMessage()),
                            e);
                }

                public void onSuccess(Response response) {
                    handleLoggedInResponse(response.getJSONObject());
                }
            });

            return true;

        }

        private boolean handleNeedsClientAuth(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_NEEDS_CLIENT_AUTH)
                return false;

            Scoreflex.RequestParams params = new Scoreflex.RequestParams();
            // Pass the anonymous access token
            String accessToken = ScoreflexRestClient.getAccessToken();
            if (ScoreflexRestClient.getAccessTokenIsAnonymous() && null != accessToken)
                params.put("anonymousAccessToken", accessToken);

            return nativeLogin(uri, false, params);
        }

        private boolean handleLinkService(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_LINK_SERVICE)
                return false;

            JSONObject dataJson;
            String dataString = uri.getQueryParameter("data");
            try {
                dataJson = new JSONObject(dataString);
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }
            String service = dataJson.optString("service");
            if (null == service) {
                Log.w("Scoreflex", "handleLinkService: No service specified");
                return false;
            }

            return nativeLogin(uri, true, new Scoreflex.RequestParams());
        }

        private boolean nativeLogin(Uri uri, final boolean isLink, final Scoreflex.RequestParams params) {

            String dataString = uri.getQueryParameter("data");

            JSONObject dataJson;
            try {
                dataJson = new JSONObject(dataString);
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }

            // Manage next url
            final String nextUrlString = dataJson.optString("nextUrl");

            if (null != nextUrlString && 0 < nextUrlString.length()) {
                Uri nextUri = Uri.parse(nextUrlString);
                mAuthGrantedNextUrlString = String.format("%s?%s",
                        ScoreflexUriHelper.getNonSecureAbsoluteUrl(nextUri.getPath()), nextUri.getQuery());
            }

            final String service = dataJson.optString("service");

            if (!"Facebook".equals(service) && !"Google".equals(service)) {
                Log.d("Scoreflex", "Unknown service: " + (service != null ? service : "null"));
                return false;
            }

            if ("Facebook".equals(service) && !ScoreflexFacebookWrapper.isFacebookAvailable(getContext()))
                return false;

            if ("Google".equals(service) && !ScoreflexGoogleWrapper.isGoogleAvailable(getContext()))
                return false;

            // Handle facebook when we live in a scoreflex activity only
            if (null == mParentActivity) {
                return false;
            }

            try {
                SocialCallback callback = new SocialCallback() {

                    @Override
                    public void call(String accessToken, Exception exception) {

                        if (exception != null) {
                            Log.e("Scoreflex", "Error with native login", exception);
                        }

                        if (null != accessToken && 0 < accessToken.length()) {

                            params.put("clientId", Scoreflex.getClientId());
                            params.put("service", service);
                            params.put("serviceAccessToken", accessToken);
                            params.put("state", mAuthState);
                            params.put("devicePlatform", "Android");
                            params.put("deviceModel", Scoreflex.getDeviceModel());
                            params.put("deviceId", Scoreflex.getUDID());
                            if (nextUrlString != null) {
                                params.put("next", nextUrlString);
                            }

                            if (isLink) {
                                setResource("/web/linkExternallyAuthenticated/" + service, params);
                                return;
                            }

                            // Generate a new auth state
                            mAuthState = UUID.randomUUID().toString();
                            ScoreflexRestClient.post("/oauth/accessTokenExternallyAuthenticated", params,
                                    new Scoreflex.ResponseHandler() {
                                        public void onFailure(Throwable e, Response errorResponse) {
                                            Log.e("Scoreflex", String.format(
                                                    "Error authenticating, could not transform code into accessToken: %s",
                                                    errorResponse.getErrorMessage()), e);
                                        }

                                        public void onSuccess(Response response) {
                                            handleLoggedInResponse(response.getJSONObject());
                                        }
                                    });
                        }
                    }
                };
                if ("Facebook".equals(service))
                    ScoreflexFacebookWrapper.login(mParentActivity, callback);

                if ("Google".equals(service))
                    ScoreflexGoogleWrapper.login(mParentActivity, callback);
            } catch (Exception e) {
                Log.e("Scoreflex", "Native login exception", e);
                webLogin(true, true, service);
            }
            return true;
        }

        private List<String> JSONArrayToList(JSONArray array) throws JSONException {
            ArrayList<String> targetsList = new ArrayList<String>();
            if (array != null) {
                for (int i = 0; i < array.length(); i++) {
                    targetsList.add(array.get(i).toString());
                }
            }
            return targetsList;
        }

        private boolean handleSocialInvite(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_INVITE_WITH_SERVICE)
                return false;

            JSONObject dataJson;
            String dataString = uri.getQueryParameter("data");
            try {
                dataJson = new JSONObject(dataString);
                String service = dataJson.optString("service");
                if ("Facebook".equals(service)) {
                    List<String> suggestedList = JSONArrayToList(dataJson.optJSONArray("targetIds"));
                    String data = dataJson.optString("data");
                    Scoreflex.sendFacebookInvitation(mParentActivity, dataJson.getString("text"), null,
                            suggestedList, data);
                    //               ScoreflexFacebookWrapper.sendInvitation(
                }
                if ("Google".equals(service)) {
                    List<String> friendIds = JSONArrayToList(dataJson.optJSONArray("targetIds"));
                    Scoreflex.sendGoogleInvitation(mParentActivity, dataJson.getString("text"), friendIds,
                            dataJson.optString("url"), "/invite");
                }
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);

                return true;
            }

            return true;
        }

        private boolean handleSocialShare(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_SHARE_WITH_SERVICE)
                return false;

            JSONObject dataJson;
            String dataString = uri.getQueryParameter("data");
            try {
                dataJson = new JSONObject(dataString);
                String text = dataJson.optString("text");
                String url = dataJson.optString("url");
                if ("Facebook".equals(dataJson.optString("service"))) {
                    String title = dataJson.optString("title");
                    Scoreflex.shareOnFacebook(mParentActivity, title, text, url);
                }
                if ("Google".equals(dataJson.optString("service"))) {
                    Scoreflex.shareOnGoogle(mParentActivity, text, url);
                }
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }

            return true;
        }

        private boolean handleNeedsAuth(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_NEEDS_AUTH)
                return false;

            String dataString = uri.getQueryParameter("data");

            JSONObject dataJson;
            try {
                dataJson = new JSONObject(dataString);
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }

            // Remember where we ought to go once auth is successful
            // Note that we can't use the "redirectUri" parameter of the
            // authorize call
            // as it is intended to be registered on the server side (see oauth
            // spec).
            String nextUrlString = dataJson.optString("nextUrl");
            if (null != nextUrlString) {
                Uri nextUri = Uri.parse(nextUrlString);
                mAuthGrantedNextUrlString = String.format("%s?%s",
                        ScoreflexUriHelper.getNonSecureAbsoluteUrl(nextUri.getPath()), nextUri.getQuery());
            } else {
                mAuthGrantedNextUrlString = null;
            }

            // full mode ?
            String modeString = dataJson.optString("mode");

            String service = dataJson.optString("service");
            webLogin("full".equals(modeString), false, service);

            return true;

        }

        private void webLogin(boolean fullScreen, boolean disableNativeLogin, String service) {
            // Build the uri
            Scoreflex.RequestParams params = new Scoreflex.RequestParams();
            params.put("clientId", Scoreflex.getClientId());
            params.put("devicePlatform", "Android");
            params.put("deviceModel", Scoreflex.getDeviceModel());

            // If native login disabled, override the default handledServices
            // parameter
            if (disableNativeLogin)
                params.put("handledServices", "");

            // Pass the anonymous access token
            String accessToken = ScoreflexRestClient.getAccessToken();
            if (ScoreflexRestClient.getAccessTokenIsAnonymous() && null != accessToken)
                params.put("anonymousAccessToken", accessToken);

            if (null != service)
                params.put("service", service);

            // A random state
            mAuthState = UUID.randomUUID().toString();
            params.put("state", mAuthState);

            // Open authorize resource in the web view
            openResource("/oauth/web/authorize", params, fullScreen, false);
        }

        private boolean handleCloseWebView(Uri uri, int status, int code) {
            if (code != Scoreflex.SUCCESS_CLOSE_WEBVIEW)
                return false;

            close();

            return true;

        }

        private boolean handleMoveToNewUrl(Uri uri, int status, int code) {

            if (code != Scoreflex.SUCCESS_MOVE_TO_NEW_URL)
                return false;

            String dataString = uri.getQueryParameter("data");

            JSONObject dataJson;
            try {
                dataJson = new JSONObject(dataString);
            } catch (JSONException e) {
                Log.e("Scoreflex", "Invalid json received in the data parameter", e);
                // Handle by doing nothing
                return true;
            }

            String urlString = dataJson.optString("url");

            // No URL provided, handle by doing nothing
            if (null == urlString) {
                Log.e("Scoreflex", "Move to new URL requested but no url provided");
                return true;
            }

            String modeString = dataJson.optString("mode");
            boolean isFullscreen = "full".equals(modeString);
            if (isFullscreen && mScoreflexViewHandler != null
                    && mScoreflexViewHandler.handleOpenNewFullscreenView(urlString)) {
                return true;
            }

            openFullUrl(urlString, "full".equals(modeString), true);

            return true;
        }

        private boolean openResource(String resource, Scoreflex.RequestParams params, boolean forceFullScreen,
                boolean newView) {

            return openFullUrl(String.format("%s%s?%s", Scoreflex.getBaseURL(), resource,
                    params != null ? params.getURLEncodedString() : ""), forceFullScreen, newView);
        }

        private boolean openFullUrl(String fullUrl, boolean forceFullScreen, boolean newView) {
            // If we don't absolutely need full screen, open the url in the
            // current screen configuration
            setFullUrl(fullUrl, forceFullScreen, newView);
            return true;
        }
    }

    private abstract class UserInterfaceState {
        public abstract void onEnterState();

        public void onLeaveState() {
        }
    }

    private class InitialState extends UserInterfaceState {
        @Override
        public void onEnterState() {
            mProgressBar.setVisibility(View.VISIBLE);
            mWebView.setVisibility(View.GONE);
            mErrorLayout.setVisibility(View.GONE);
            mCloseButton.setVisibility(View.VISIBLE);
        }

        @Override
        public void onLeaveState() {
            mProgressBar.setVisibility(View.GONE);
        }
    }

    private class LoadingState extends UserInterfaceState {
        @Override
        public void onEnterState() {
            mProgressBar.setVisibility(View.VISIBLE);
        }

        @Override
        public void onLeaveState() {
            mProgressBar.setVisibility(View.GONE);
        }
    }

    /**
     * A simple interface to get notified when a scoreflex view is closed
     */
    public interface ScoreflexViewListener {
        /**
         * this method is called when the ScoreflexView is closed.
         */
        public void onViewClosed();

        /**
         * If you want to overload the opening of a new view
         * @param fullUrlString the full string of the url to open
         * @return true if you want to overload the opening of a new view false otherwise
         */

        public boolean handleOpenNewFullscreenView(String fullUrlString);
    }

    private class WebContentState extends UserInterfaceState {
        @Override
        public void onEnterState() {
            mWebView.setVisibility(View.VISIBLE);
            mProgressBar.setVisibility(View.GONE);

            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.TOP);
            if (ScoreflexUriHelper.isAPIUri(Uri.parse(mWebView.getUrl()))) {
                layoutParams.setMargins(0, 0, 0, 0);
            } else {
                layoutParams.setMargins(0, getResources().getDimensionPixelSize(R.dimen.scoreflex_topbar_height), 0,
                        0);
            }
            mWebView.setLayoutParams(layoutParams);

            // If the scoreflex view is gone, fade it in
            if (View.GONE == getVisibility()) {
                setVisibility(View.VISIBLE);

                AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
                alphaAnimation.setFillBefore(true);
                alphaAnimation.setFillAfter(true);
                alphaAnimation.setDuration(1000);
                startAnimation(alphaAnimation);
            }
        }
    }

    private class ErrorState extends UserInterfaceState {
        @Override
        public void onEnterState() {
            mWebView.setVisibility(View.GONE);
            mErrorLayout.setVisibility(View.VISIBLE);
            if (!ScoreflexView.this.mIsPreloading) {
                Toast.makeText(getContext(), R.string.scoreflex_network_error, Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onLeaveState() {
            mErrorLayout.setVisibility(View.GONE);
        }

    }

    /**
     * Method called when a picture has been chosen for a input type file / image.
     *
     * @param activity
     * @param requestCode
     * @param resultCode
     * @param intent
     */
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
        Uri result = intent == null || resultCode != activity.RESULT_OK ? null : intent.getData();
        if (result == null) {
            return;
        }
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
        mOutputFileUri = null;

    }

    private BroadcastReceiver mLoginReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            if (ScoreflexView.this.isLoginSource) {
                ScoreflexView.this.isLoginSource = false;
                return;
            }
            setFullUrl(mWebView.getUrl());
        }
    };
}