Back to project page BagOfPix.
The source code is released under:
MIT License
If you think the Android project BagOfPix listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/** * Copyright 2010-present Facebook.//from w ww . ja v a 2 s .c om * * 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.facebook.widget; import android.annotation.SuppressLint; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.http.SslError; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.*; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import com.facebook.*; import com.facebook.android.*; import com.facebook.internal.Logger; import com.facebook.internal.ServerProtocol; import com.facebook.internal.Utility; import com.facebook.internal.Validate; /** * This class provides a mechanism for displaying Facebook Web dialogs inside a Dialog. Helper * methods are provided to construct commonly-used dialogs, or a caller can specify arbitrary * parameters to call other dialogs. */ public class WebDialog extends Dialog { private static final String LOG_TAG = Logger.LOG_TAG_BASE + "WebDialog"; private static final String DISPLAY_TOUCH = "touch"; private static final String USER_AGENT = "user_agent"; static final String REDIRECT_URI = "fbconnect://success"; static final String CANCEL_URI = "fbconnect://cancel"; static final boolean DISABLE_SSL_CHECK_FOR_TESTING = false; // width below which there are no extra margins private static final int NO_PADDING_SCREEN_WIDTH = 480; // width beyond which we're always using the MIN_SCALE_FACTOR private static final int MAX_PADDING_SCREEN_WIDTH = 800; // height below which there are no extra margins private static final int NO_PADDING_SCREEN_HEIGHT = 800; // height beyond which we're always using the MIN_SCALE_FACTOR private static final int MAX_PADDING_SCREEN_HEIGHT = 1280; // the minimum scaling factor for the web dialog (50% of screen size) private static final double MIN_SCALE_FACTOR = 0.5; // translucent border around the webview private static final int BACKGROUND_GRAY = 0xCC000000; public static final int DEFAULT_THEME = android.R.style.Theme_Translucent_NoTitleBar; private String url; private OnCompleteListener onCompleteListener; private WebView webView; private ProgressDialog spinner; private ImageView crossImageView; private FrameLayout contentFrameLayout; private boolean listenerCalled = false; private boolean isDetached = false; /** * Interface that implements a listener to be called when the user's interaction with the * dialog completes, whether because the dialog finished successfully, or it was cancelled, * or an error was encountered. */ public interface OnCompleteListener { /** * Called when the dialog completes. * * @param values on success, contains the values returned by the dialog * @param error on an error, contains an exception describing the error */ void onComplete(Bundle values, FacebookException error); } /** * Constructor which can be used to display a dialog with an already-constructed URL. * * @param context the context to use to display the dialog * @param url the URL of the Web Dialog to display; no validation is done on this URL, but it should * be a valid URL pointing to a Facebook Web Dialog */ public WebDialog(Context context, String url) { this(context, url, DEFAULT_THEME); } /** * Constructor which can be used to display a dialog with an already-constructed URL and a custom theme. * * @param context the context to use to display the dialog * @param url the URL of the Web Dialog to display; no validation is done on this URL, but it should * be a valid URL pointing to a Facebook Web Dialog * @param theme identifier of a theme to pass to the Dialog class */ public WebDialog(Context context, String url, int theme) { super(context, theme); this.url = url; } /** * Constructor which will construct the URL of the Web dialog based on the specified parameters. * * @param context the context to use to display the dialog * @param action the portion of the dialog URL following "dialog/" * @param parameters parameters which will be included as part of the URL * @param theme identifier of a theme to pass to the Dialog class * @param listener the listener to notify, or null if no notification is desired */ public WebDialog(Context context, String action, Bundle parameters, int theme, OnCompleteListener listener) { super(context, theme); if (parameters == null) { parameters = new Bundle(); } parameters.putString(ServerProtocol.DIALOG_PARAM_DISPLAY, DISPLAY_TOUCH); parameters.putString(ServerProtocol.DIALOG_PARAM_TYPE, USER_AGENT); Uri uri = Utility.buildUri(ServerProtocol.getDialogAuthority(), ServerProtocol.DIALOG_PATH + action, parameters); this.url = uri.toString(); onCompleteListener = listener; } /** * Sets the listener which will be notified when the dialog finishes. * * @param listener the listener to notify, or null if no notification is desired */ public void setOnCompleteListener(OnCompleteListener listener) { onCompleteListener = listener; } /** * Gets the listener which will be notified when the dialog finishes. * * @return the listener, or null if none has been specified */ public OnCompleteListener getOnCompleteListener() { return onCompleteListener; } @Override public void dismiss() { if (webView != null) { webView.stopLoading(); } if (!isDetached) { if (spinner.isShowing()) { spinner.dismiss(); } super.dismiss(); } } @Override public void onDetachedFromWindow() { isDetached = true; super.onDetachedFromWindow(); } @Override public void onAttachedToWindow() { isDetached = false; super.onAttachedToWindow(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { sendCancelToListener(); } }); spinner = new ProgressDialog(getContext()); spinner.requestWindowFeature(Window.FEATURE_NO_TITLE); spinner.setMessage(getContext().getString(R.string.com_facebook_loading)); spinner.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { sendCancelToListener(); WebDialog.this.dismiss(); } }); requestWindowFeature(Window.FEATURE_NO_TITLE); contentFrameLayout = new FrameLayout(getContext()); // First calculate how big the frame layout should be calculateSize(); getWindow().setGravity(Gravity.CENTER); // resize the dialog if the soft keyboard comes up getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); /* Create the 'x' image, but don't add to the contentFrameLayout layout yet * at this point, we only need to know its drawable width and height * to place the webview */ createCrossImage(); /* Now we know 'x' drawable width and height, * layout the webview and add it the contentFrameLayout layout */ int crossWidth = crossImageView.getDrawable().getIntrinsicWidth(); setUpWebView(crossWidth / 2 + 1); /* Finally add the 'x' image to the contentFrameLayout layout and * add contentFrameLayout to the Dialog view */ contentFrameLayout.addView(crossImageView, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); setContentView(contentFrameLayout); } private void calculateSize() { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); // always use the portrait dimensions to do the scaling calculations so we always get a portrait shaped // web dialog int width = metrics.widthPixels < metrics.heightPixels ? metrics.widthPixels : metrics.heightPixels; int height = metrics.widthPixels < metrics.heightPixels ? metrics.heightPixels : metrics.widthPixels; int dialogWidth = Math.min( getScaledSize(width, metrics.density, NO_PADDING_SCREEN_WIDTH, MAX_PADDING_SCREEN_WIDTH), metrics.widthPixels); int dialogHeight = Math.min( getScaledSize(height, metrics.density, NO_PADDING_SCREEN_HEIGHT, MAX_PADDING_SCREEN_HEIGHT), metrics.heightPixels); getWindow().setLayout(dialogWidth, dialogHeight); } /** * Returns a scaled size (either width or height) based on the parameters passed. * @param screenSize a pixel dimension of the screen (either width or height) * @param density density of the screen * @param noPaddingSize the size at which there's no padding for the dialog * @param maxPaddingSize the size at which to apply maximum padding for the dialog * @return a scaled size. */ private int getScaledSize(int screenSize, float density, int noPaddingSize, int maxPaddingSize) { int scaledSize = (int) ((float) screenSize / density); double scaleFactor; if (scaledSize <= noPaddingSize) { scaleFactor = 1.0; } else if (scaledSize >= maxPaddingSize) { scaleFactor = MIN_SCALE_FACTOR; } else { // between the noPadding and maxPadding widths, we take a linear reduction to go from 100% // of screen size down to MIN_SCALE_FACTOR scaleFactor = MIN_SCALE_FACTOR + ((double) (maxPaddingSize - scaledSize)) / ((double) (maxPaddingSize - noPaddingSize)) * (1.0 - MIN_SCALE_FACTOR); } return (int) (screenSize * scaleFactor); } private void sendSuccessToListener(Bundle values) { if (onCompleteListener != null && !listenerCalled) { listenerCalled = true; onCompleteListener.onComplete(values, null); } } private void sendErrorToListener(Throwable error) { if (onCompleteListener != null && !listenerCalled) { listenerCalled = true; FacebookException facebookException = null; if (error instanceof FacebookException) { facebookException = (FacebookException) error; } else { facebookException = new FacebookException(error); } onCompleteListener.onComplete(null, facebookException); } } private void sendCancelToListener() { sendErrorToListener(new FacebookOperationCanceledException()); } private void createCrossImage() { crossImageView = new ImageView(getContext()); // Dismiss the dialog when user click on the 'x' crossImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendCancelToListener(); WebDialog.this.dismiss(); } }); Drawable crossDrawable = getContext().getResources().getDrawable(R.drawable.com_facebook_close); crossImageView.setImageDrawable(crossDrawable); /* 'x' should not be visible while webview is loading * make it visible only after webview has fully loaded */ crossImageView.setVisibility(View.INVISIBLE); } @SuppressLint("SetJavaScriptEnabled") private void setUpWebView(int margin) { LinearLayout webViewContainer = new LinearLayout(getContext()); webView = new WebView(getContext()); webView.setVerticalScrollBarEnabled(false); webView.setHorizontalScrollBarEnabled(false); webView.setWebViewClient(new DialogWebViewClient()); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl(url); webView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); webView.setVisibility(View.INVISIBLE); webView.getSettings().setSavePassword(false); webViewContainer.setPadding(margin, margin, margin, margin); webViewContainer.addView(webView); webViewContainer.setBackgroundColor(BACKGROUND_GRAY); contentFrameLayout.addView(webViewContainer); } private class DialogWebViewClient extends WebViewClient { @Override @SuppressWarnings("deprecation") public boolean shouldOverrideUrlLoading(WebView view, String url) { Utility.logd(LOG_TAG, "Redirect URL: " + url); if (url.startsWith(WebDialog.REDIRECT_URI)) { Bundle values = Util.parseUrl(url); String error = values.getString("error"); if (error == null) { error = values.getString("error_type"); } String errorMessage = values.getString("error_msg"); if (errorMessage == null) { errorMessage = values.getString("error_description"); } String errorCodeString = values.getString("error_code"); int errorCode = FacebookRequestError.INVALID_ERROR_CODE; if (!Utility.isNullOrEmpty(errorCodeString)) { try { errorCode = Integer.parseInt(errorCodeString); } catch (NumberFormatException ex) { errorCode = FacebookRequestError.INVALID_ERROR_CODE; } } if (Utility.isNullOrEmpty(error) && Utility .isNullOrEmpty(errorMessage) && errorCode == FacebookRequestError.INVALID_ERROR_CODE) { sendSuccessToListener(values); } else if (error != null && (error.equals("access_denied") || error.equals("OAuthAccessDeniedException"))) { sendCancelToListener(); } else { FacebookRequestError requestError = new FacebookRequestError(errorCode, error, errorMessage); sendErrorToListener(new FacebookServiceException(requestError, errorMessage)); } WebDialog.this.dismiss(); return true; } else if (url.startsWith(WebDialog.CANCEL_URI)) { sendCancelToListener(); WebDialog.this.dismiss(); return true; } else if (url.contains(DISPLAY_TOUCH)) { return false; } // launch non-dialog URLs in a full browser getContext().startActivity( new Intent(Intent.ACTION_VIEW, Uri.parse(url))); return true; } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); sendErrorToListener(new FacebookDialogException(description, errorCode, failingUrl)); WebDialog.this.dismiss(); } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { if (DISABLE_SSL_CHECK_FOR_TESTING) { handler.proceed(); } else { super.onReceivedSslError(view, handler, error); sendErrorToListener(new FacebookDialogException(null, ERROR_FAILED_SSL_HANDSHAKE, null)); handler.cancel(); WebDialog.this.dismiss(); } } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { Utility.logd(LOG_TAG, "Webview loading URL: " + url); super.onPageStarted(view, url, favicon); if (!isDetached) { spinner.show(); } } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (!isDetached) { spinner.dismiss(); } /* * Once web view is fully loaded, set the contentFrameLayout background to be transparent * and make visible the 'x' image. */ contentFrameLayout.setBackgroundColor(Color.TRANSPARENT); webView.setVisibility(View.VISIBLE); crossImageView.setVisibility(View.VISIBLE); } } private static class BuilderBase<CONCRETE extends BuilderBase<?>> { private Context context; private Session session; private String applicationId; private String action; private int theme = DEFAULT_THEME; private OnCompleteListener listener; private Bundle parameters; protected BuilderBase(Context context, Session session, String action, Bundle parameters) { Validate.notNull(session, "session"); if (!session.isOpened()) { throw new FacebookException("Attempted to use a Session that was not open."); } this.session = session; finishInit(context, action, parameters); } protected BuilderBase(Context context, String applicationId, String action, Bundle parameters) { Validate.notNullOrEmpty(applicationId, "applicationId"); this.applicationId = applicationId; finishInit(context, action, parameters); } /** * Sets a theme identifier which will be passed to the underlying Dialog. * * @param theme a theme identifier which will be passed to the Dialog class * @return the builder */ public CONCRETE setTheme(int theme) { this.theme = theme; @SuppressWarnings("unchecked") CONCRETE result = (CONCRETE) this; return result; } /** * Sets the listener which will be notified when the dialog finishes. * * @param listener the listener to notify, or null if no notification is desired * @return the builder */ public CONCRETE setOnCompleteListener(OnCompleteListener listener) { this.listener = listener; @SuppressWarnings("unchecked") CONCRETE result = (CONCRETE) this; return result; } /** * Constructs a WebDialog using the parameters provided. The dialog is not shown, * but is ready to be shown by calling Dialog.show(). * * @return the WebDialog */ public WebDialog build() { if (session != null && session.isOpened()) { parameters.putString(ServerProtocol.DIALOG_PARAM_APP_ID, session.getApplicationId()); parameters.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, session.getAccessToken()); } else { parameters.putString(ServerProtocol.DIALOG_PARAM_APP_ID, applicationId); } if (!parameters.containsKey(ServerProtocol.DIALOG_PARAM_REDIRECT_URI)) { parameters.putString(ServerProtocol.DIALOG_PARAM_REDIRECT_URI, REDIRECT_URI); } return new WebDialog(context, action, parameters, theme, listener); } protected String getApplicationId() { return applicationId; } protected Context getContext() { return context; } protected int getTheme() { return theme; } protected Bundle getParameters() { return parameters; } protected WebDialog.OnCompleteListener getListener() { return listener; } private void finishInit(Context context, String action, Bundle parameters) { this.context = context; this.action = action; if (parameters != null) { this.parameters = parameters; } else { this.parameters = new Bundle(); } } } /** * Provides a builder that allows construction of an arbitary Facebook web dialog. */ public static class Builder extends BuilderBase<Builder> { /** * Constructor that builds a dialog for an authenticated user. * * @param context the Context within which the dialog will be shown. * @param session the Session representing an authenticating user to use for * showing the dialog; must not be null, and must be opened. * @param action the portion of the dialog URL following www.facebook.com/dialog/. * See https://developers.facebook.com/docs/reference/dialogs/ for details. * @param parameters a Bundle containing parameters to pass as part of the URL. */ public Builder(Context context, Session session, String action, Bundle parameters) { super(context, session, action, parameters); } /** * Constructor that builds a dialog without an authenticated user. * * @param context the Context within which the dialog will be shown. * @param applicationId the application ID to be included in the dialog URL. * @param action the portion of the dialog URL following www.facebook.com/dialog/. * See https://developers.facebook.com/docs/reference/dialogs/ for details. * @param parameters a Bundle containing parameters to pass as part of the URL. */ public Builder(Context context, String applicationId, String action, Bundle parameters) { super(context, applicationId, action, parameters); } } /** * Provides a builder that allows construction of the parameters for showing * the <a href="https://developers.facebook.com/docs/reference/dialogs/feed">Feed Dialog</a>. */ public static class FeedDialogBuilder extends BuilderBase<FeedDialogBuilder> { private static final String FEED_DIALOG = "feed"; private static final String FROM_PARAM = "from"; private static final String TO_PARAM = "to"; private static final String LINK_PARAM = "link"; private static final String PICTURE_PARAM = "picture"; private static final String SOURCE_PARAM = "source"; private static final String NAME_PARAM = "name"; private static final String CAPTION_PARAM = "caption"; private static final String DESCRIPTION_PARAM = "description"; /** * Constructor. * * @param context the Context within which the dialog will be shown. * @param session the Session representing an authenticating user to use for * showing the dialog; must not be null, and must be opened. */ public FeedDialogBuilder(Context context, Session session) { super(context, session, FEED_DIALOG, null); } /** * Constructor. * * @param context the Context within which the dialog will be shown. * @param parameters a Bundle containing parameters to pass as part of the * dialog URL. No validation is done on these parameters; it is * the caller's responsibility to ensure they are valid. For more information, * see <a href="https://developers.facebook.com/docs/reference/dialogs/feed/"> * https://developers.facebook.com/docs/reference/dialogs/feed/</a>. * @param session the Session representing an authenticating user to use for * showing the dialog; must not be null, and must be opened. */ public FeedDialogBuilder(Context context, Session session, Bundle parameters) { super(context, session, FEED_DIALOG, parameters); } /** * Sets the ID of the profile that is posting to Facebook. If none is specified, * the default is "me". This profile must be either the authenticated user or a * Page that the user is an administrator of. * * @param id Facebook ID of the profile to post from * @return the builder */ public FeedDialogBuilder setFrom(String id) { getParameters().putString(FROM_PARAM, id); return this; } /** * Sets the ID of the profile that the story will be published to. If not specified, it * will default to the same profile that the story is being published from. * * @param id Facebook ID of the profile to post to * @return the builder */ public FeedDialogBuilder setTo(String id) { getParameters().putString(TO_PARAM, id); return this; } /** * Sets the URL of a link to be shared. * * @param link the URL * @return the builder */ public FeedDialogBuilder setLink(String link) { getParameters().putString(LINK_PARAM, link); return this; } /** * Sets the URL of a picture to be shared. * * @param picture the URL of the picture * @return the builder */ public FeedDialogBuilder setPicture(String picture) { getParameters().putString(PICTURE_PARAM, picture); return this; } /** * Sets the URL of a media file attached to this post. If this is set, any picture * set via setPicture will be ignored. * * @param source the URL of the media file * @return the builder */ public FeedDialogBuilder setSource(String source) { getParameters().putString(SOURCE_PARAM, source); return this; } /** * Sets the name of the item being shared. * * @param name the name * @return the builder */ public FeedDialogBuilder setName(String name) { getParameters().putString(NAME_PARAM, name); return this; } /** * Sets the caption to be displayed. * * @param caption the caption * @return the builder */ public FeedDialogBuilder setCaption(String caption) { getParameters().putString(CAPTION_PARAM, caption); return this; } /** * Sets the description to be displayed. * * @param description the description * @return the builder */ public FeedDialogBuilder setDescription(String description) { getParameters().putString(DESCRIPTION_PARAM, description); return this; } } /** * Provides a builder that allows construction of the parameters for showing * the <a href="https://developers.facebook.com/docs/reference/dialogs/requests">Requests Dialog</a>. */ public static class RequestsDialogBuilder extends BuilderBase<RequestsDialogBuilder> { private static final String APPREQUESTS_DIALOG = "apprequests"; private static final String MESSAGE_PARAM = "message"; private static final String TO_PARAM = "to"; private static final String DATA_PARAM = "data"; private static final String TITLE_PARAM = "title"; /** * Constructor. * * @param context the Context within which the dialog will be shown. * @param session the Session representing an authenticating user to use for * showing the dialog; must not be null, and must be opened. */ public RequestsDialogBuilder(Context context, Session session) { super(context, session, APPREQUESTS_DIALOG, null); } /** * Constructor. * * @param context the Context within which the dialog will be shown. * @param parameters a Bundle containing parameters to pass as part of the * dialog URL. No validation is done on these parameters; it is * the caller's responsibility to ensure they are valid. For more information, * see <a href="https://developers.facebook.com/docs/reference/dialogs/requests/"> * https://developers.facebook.com/docs/reference/dialogs/requests/</a>. * @param session the Session representing an authenticating user to use for * showing the dialog; must not be null, and must be opened. */ public RequestsDialogBuilder(Context context, Session session, Bundle parameters) { super(context, session, APPREQUESTS_DIALOG, parameters); } /** * Sets the string users receiving the request will see. The maximum length * is 60 characters. * * @param message the message * @return the builder */ public RequestsDialogBuilder setMessage(String message) { getParameters().putString(MESSAGE_PARAM, message); return this; } /** * Sets the user ID or user name the request will be sent to. If this is not * specified, a friend selector will be displayed and the user can select up * to 50 friends. * * @param id the id or user name to send the request to * @return the builder */ public RequestsDialogBuilder setTo(String id) { getParameters().putString(TO_PARAM, id); return this; } /** * Sets optional data which can be used for tracking; maximum length is 255 * characters. * * @param data the data * @return the builder */ public RequestsDialogBuilder setData(String data) { getParameters().putString(DATA_PARAM, data); return this; } /** * Sets an optional title for the dialog; maximum length is 50 characters. * * @param title the title * @return the builder */ public RequestsDialogBuilder setTitle(String title) { getParameters().putString(TITLE_PARAM, title); return this; } } }