/* =========================================================================== Copyright (c) 2012 Three Pillar Global Inc. http://threepillarglobal.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =========================================================================== */ package com.common.utils.social; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Picture; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.KeyEvent; import android.view.ViewGroup; import android.view.Window; import android.webkit.WebSettings.ZoomDensity; import android.webkit.WebView; import android.webkit.WebView.PictureListener; import android.webkit.WebViewClient; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import org.brickred.socialauth.AuthProvider; import org.brickred.socialauth.SocialAuthManager; import org.brickred.socialauth.util.AccessGrant; import java.util.Locale; import java.util.Map; /** * Dialog that wraps a Web view for authenticating with the given social * network. All the OAuth redirection happens over here and the success and * failure are handed over to the listener * * @author vineet.aggarwal@3pillarglobal.com * @author abhinav.maheswari@3pillarglobal.com */ class SocialAuthDialog extends Dialog { // Variables private static final int BLUE = 0xFF6D84B4; private static final int MARGIN = 4; private static final int PADDING = 2; private static final String DISPLAY_STRING = "touch"; private static final FrameLayout.LayoutParams FILL = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT); static final FrameLayout.LayoutParams WRAP = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); public static float width = 40; public static float height = 60; private static final float[] DIMENSIONS_DIFF_LANDSCAPE = {width, height}; private static final float[] DIMENSIONS_DIFF_PORTRAIT = {width, height}; public static boolean titleStatus = false; private final String mUrl; private final DialogListener mListener; // SocialAuth Components private final SocialAuthManager mSocialAuthManager; private final SocialAuthAdapter.Provider mProviderName; private int count; // Android Components private TextView mTitle; private ProgressDialog mSpinner; private CustomWebView mWebView; private LinearLayout mContent; private Handler handler; /** * Constructor for the dialog * * @param context Parent component that opened this dialog * @param url URL that will be used for authenticating * @param providerName Name of provider that is being authenticated * @param listener Listener object to handle events * @param socialAuthManager Underlying SocialAuth framework for OAuth */ public SocialAuthDialog(Context context, String url, SocialAuthAdapter.Provider providerName, DialogListener listener, SocialAuthManager socialAuthManager) { super(context); mProviderName = providerName; mUrl = url; mListener = listener; mSocialAuthManager = socialAuthManager; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handler = new Handler(); Util.getDisplayDpi(getContext()); mSpinner = new ProgressDialog(getContext()); mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE); mSpinner.setMessage("Loading..."); mSpinner.setCancelable(true); mContent = new LinearLayout(getContext()); mContent.setOrientation(LinearLayout.VERTICAL); setUpTitle(); setUpWebView(); Display display = getWindow().getWindowManager().getDefaultDisplay(); final float scale = getContext().getResources().getDisplayMetrics().density; int orientation = getContext().getResources().getConfiguration().orientation; float[] dimensions = (orientation == Configuration.ORIENTATION_LANDSCAPE) ? DIMENSIONS_DIFF_LANDSCAPE : DIMENSIONS_DIFF_PORTRAIT; addContentView(mContent, new LinearLayout.LayoutParams(display.getWidth() - ((int) (dimensions[0] * scale + 0.5f)), display.getHeight() - ((int) (dimensions[1] * scale + 0.5f)))); mSpinner.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { mWebView.stopLoading(); mListener.onBack(); SocialAuthDialog.this.dismiss(); } }); this.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { mWebView.stopLoading(); dismiss(); mListener.onBack(); return true; } return false; } }); } /** * Sets title and icon of provider */ private void setUpTitle() { requestWindowFeature(Window.FEATURE_NO_TITLE); mTitle = new TextView(getContext()); int res = getContext().getResources().getIdentifier(mProviderName.toString(), "drawable", getContext().getPackageName()); Drawable icon = getContext().getResources().getDrawable(res); String sb = mProviderName.toString().substring(0, 1).toUpperCase(Locale.getDefault()) + mProviderName.toString().substring(1, mProviderName.toString().length()); mTitle.setText(sb); mTitle.setGravity(Gravity.CENTER_VERTICAL); mTitle.setTextColor(Color.WHITE); mTitle.setTypeface(Typeface.DEFAULT_BOLD); mTitle.setBackgroundColor(BLUE); mTitle.setPadding(MARGIN + PADDING, MARGIN, MARGIN, MARGIN); mTitle.setCompoundDrawablePadding(MARGIN + PADDING); mTitle.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); if (!titleStatus) mContent.addView(mTitle); } /** * Set up WebView to load the provider URL */ private void setUpWebView() { mWebView = new CustomWebView(getContext()); mWebView.setVerticalScrollBarEnabled(false); mWebView.setHorizontalScrollBarEnabled(false); mWebView.setWebViewClient(new SocialAuthWebViewClient()); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.loadUrl(mUrl); mWebView.setLayoutParams(FILL); mContent.addView(mWebView); } /** * Internal Method to create new File in internal memory for each provider * and save accessGrant * * @param auth AuthProvider */ private void writeToken(AuthProvider auth) { AccessGrant accessGrant = auth.getAccessGrant(); String key = accessGrant.getKey(); String secret = accessGrant.getSecret(); String providerid = accessGrant.getProviderId(); Map<String, Object> attributes = accessGrant.getAttributes(); Editor edit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit(); edit.putString(mProviderName.toString() + " key", key); edit.putString(mProviderName.toString() + " secret", secret); edit.putString(mProviderName.toString() + " providerid", providerid); if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue()); } for (String s : attributes.keySet()) { edit.putString(mProviderName.toString() + "attribute " + s, String.valueOf(attributes.get(s))); } } edit.apply(); } /** * Internal Method to create new Create accessGrant * * @param params */ private AccessGrant createAccessGrant(Map<String, String> params) { AccessGrant accessGrant = new AccessGrant(); if (params.get("access_token") != null) { String accessToken = params.get("access_token"); Integer expires = null; if (params.get("expires") != null) { expires = Integer.valueOf(params.get("expires")); } accessGrant.setKey(accessToken); accessGrant.setAttribute("expires", expires); } accessGrant.setProviderId(mProviderName.toString()); return accessGrant; } /** * WebView Client */ private class SocialAuthWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Log.d("SocialAuth-WebView", "Override url: " + url); String newUrl; if (url.startsWith(mProviderName.getCallBackUri()) && (mProviderName.toString().equalsIgnoreCase("facebook") || mProviderName.toString() .equalsIgnoreCase("twitter"))) { if (url.startsWith(mProviderName.getCancelUri())) { // Handles Twitter and Facebook Cancel mListener.onCancel(); } else { // for Facebook and Twitter final Map<String, String> params = Util.parseUrl(url); Runnable runnable = new Runnable() { @Override public void run() { try { if (mProviderName.toString().equalsIgnoreCase("facebook")) { Log.d("SocialAuth Android", "success"); AccessGrant accessGrant = createAccessGrant(params); AuthProvider auth = mSocialAuthManager.connect(accessGrant); writeToken(auth); } else { AuthProvider auth = mSocialAuthManager.connect(params); writeToken(auth); } handler.post(new Runnable() { @Override public void run() { if (mSpinner != null && mSpinner.isShowing()) mSpinner.dismiss(); Bundle bundle = new Bundle(); bundle.putString(SocialAuthAdapter.PROVIDER, mProviderName.toString()); mListener.onComplete(bundle); } }); } catch (Exception e) { e.printStackTrace(); mListener.onError(new SocialAuthError("Unknown Error", e)); } } }; new Thread(runnable).start(); } SocialAuthDialog.this.dismiss(); return true; } // ***Handling Runkeeper Facebook Start************** else if (url.startsWith("https://www.facebook.com/dialog/oauth")) { newUrl = url.replace("https://www.facebook.com/dialog/oauth", "https://m.facebook.com/dialog/oauth"); mWebView.loadUrl(newUrl); return true; } else if (url.startsWith("http://runkeeper.com/jsp/widgets/streetTeamWidgetClose.jsp")) { mWebView.loadUrl("http://runkeeper.com/facebookSignIn"); return true; } else if (url.startsWith("http://runkeeper.com/home")) { Log.d("Again Calling auth URL ", "SocialAuth"); mWebView.loadUrl(mUrl); return false; } // ****Handling Runkeeper Facebook End************* else if (url.startsWith("https://login.yahoo.com/config/login")) { newUrl = url.replace("https://login.yahoo.com/config/login", "https://mlogin.yahoo.com/w/login"); mWebView.loadUrl(newUrl); return false; } else if (url.startsWith(mProviderName.getCancelUri())) { // Handles MySpace and Linkedin Cancel mListener.onCancel(); SocialAuthDialog.this.dismiss(); return true; } else if (url.contains(DISPLAY_STRING)) { return false; } return false; } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { Log.d("SocialAuth-WebView", "Inside OnReceived Error"); Log.d("SocialAuth-WebView", String.valueOf(errorCode)); super.onReceivedError(view, errorCode, description, failingUrl); mListener.onError(new SocialAuthError(description, new Exception(failingUrl))); SocialAuthDialog.this.dismiss(); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); Log.d("SocialAuth-WebView", "onPageStart:" + url); // To set zoom density of runkeeper dialog for various densities if (url.startsWith("https://runkeeper.com/apps/authorize") & count < 1) { if (Util.UI_DENSITY == 120 && Util.UI_SIZE == 4 || Util.UI_DENSITY == 160 && Util.UI_SIZE == 3 || Util.UI_DENSITY == 240) { mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); count = 1; } else { if (Util.UI_DENSITY == 160 && Util.UI_SIZE == 10) mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE); count = 1; } } // To set zoom density of linkedin dialog for ldpi and mdpi if (mProviderName.toString().equalsIgnoreCase("linkedin")) { if (Util.UI_DENSITY == 120 || Util.UI_DENSITY == 240) mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); else if (Util.UI_DENSITY == 160) { if (Util.UI_SIZE == 3) mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); else mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE); } else if (Util.UI_DENSITY == 213) mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE); } // To set zoom density of yammer dialog if (mProviderName.toString().equalsIgnoreCase("yammer")) { if (Util.UI_DENSITY == 160) { if (Util.UI_SIZE == 10) mWebView.getSettings().setDefaultZoom(ZoomDensity.MEDIUM); } else mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); } // To set zoom density of googleplus/google/salesforce/instagram // dialog if (mProviderName.toString().equalsIgnoreCase("google") || mProviderName.toString().equalsIgnoreCase("googleplus") || mProviderName.toString().equalsIgnoreCase("salesforce") || mProviderName.toString().equalsIgnoreCase("instagram")) { if (Util.UI_DENSITY == 160 && Util.UI_SIZE == 3 || Util.UI_DENSITY == 120 && Util.UI_SIZE == 4 || Util.UI_DENSITY == 240) mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); else if (Util.UI_DENSITY == 213) mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE); } mSpinner.show(); // For Linkedin, MySpace, Runkeeper - Calls onPageStart to // authorize. if (url.startsWith(mProviderName.getCallBackUri())) { if (url.startsWith(mProviderName.getCancelUri())) { mListener.onCancel(); } else { final Map<String, String> params = Util.parseUrl(url); Runnable runnable = new Runnable() { @Override public void run() { try { AuthProvider auth = mSocialAuthManager.connect(params); // Don't save token for yahoo, yammer, // salesforce if (!mProviderName.toString().equalsIgnoreCase("yammer") || !mProviderName.toString().equalsIgnoreCase("salesforce")) writeToken(auth); handler.post(new Runnable() { @Override public void run() { if (mSpinner != null && mSpinner.isShowing()) mSpinner.dismiss(); Bundle bundle = new Bundle(); bundle.putString(SocialAuthAdapter.PROVIDER, mProviderName.toString()); mListener.onComplete(bundle); } }); } catch (Exception e) { e.printStackTrace(); mListener.onError(new SocialAuthError("Could not connect using SocialAuth", e)); } } }; new Thread(runnable).start(); } SocialAuthDialog.this.dismiss(); } } @Override public void onPageFinished(WebView view, final String url) { super.onPageFinished(view, url); // workaround for yahoo and runkeeper mWebView.setPictureListener(new PictureListener() { @Override public void onNewPicture(WebView view, Picture arg1) { // To set zoom density of yahoo dialog if (mProviderName.toString().equalsIgnoreCase("yahoo")) { if (url.startsWith("https://api.login.yahoo.com//oauth/v2")) { if (Util.UI_DENSITY == 160 && Util.UI_SIZE == 3) mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR); else mWebView.getSettings().setDefaultZoom(ZoomDensity.MEDIUM); mWebView.scrollTo(Util.UI_YAHOO_ALLOW, 0); } mSpinner.dismiss(); } if (mProviderName.toString().equalsIgnoreCase("runkeeper") && (url.startsWith("http://m.facebook.com/login.php") || url .startsWith("https://m.facebook.com/dialog/oauth"))) { // Set Zoom Density of FaceBook Dialog mWebView.getSettings().setDefaultZoom(ZoomDensity.MEDIUM); } } }); String title = mWebView.getTitle(); if (title != null && title.length() > 0) { mTitle.setText(title); } mSpinner.dismiss(); } } /** * Workaround for Null pointer exception in WebView.onWindowFocusChanged in * droid phones and emulator with android 2.2 os. It prevents first time * WebView crash. */ public class CustomWebView extends WebView { public CustomWebView(Context context) { super(context); } public CustomWebView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public CustomWebView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { try { super.onWindowFocusChanged(hasWindowFocus); } catch (NullPointerException e) { // Catch null pointer exception } } } }