Related
public void signInWithLinkedIn(View view) {
//First check if user is already authenticated or not and session is valid or not
if (!LISessionManager.getInstance(this).getSession().isValid()) {
//if not valid then start authentication
LISessionManager.getInstance(getApplicationContext()).init(LinkedInActivity.this, buildScope()//pass the build scope here
, new AuthListener() {
#Override
public void onAuthSuccess() {
// Authentication was successful. You can now do
// other calls with the SDK.
Toast.makeText(LinkedInActivity.this, "Successfully authenticated with LinkedIn.", Toast.LENGTH_SHORT).show();
//on successful authentication fetch basic profile data of user
//LISessionManager.getInstance(getApplicationContext()).clearSession();
fetchBasicProfileData();
}
#Override
public void onAuthError(LIAuthError error) {
// Handle authentication errors
//LISessionManager.getInstance(getApplicationContext()).clearSession();
Log.e("AUTH ERROR", "Auth Error :" + error.toString());
Toast.makeText(LinkedInActivity.this, "Failed to authenticate with LinkedIn. Please try again.", Toast.LENGTH_SHORT).show();
}
}, true);//if TRUE then it will show dialog if
// any device has no LinkedIn app installed to download app else won't show anything
} else {
//LISessionManager.getInstance(getApplicationContext()).clearSession();
Toast.makeText(this, "You have already been authenticated.", Toast.LENGTH_SHORT).show();
//if user is already authenticated fetch basic profile data for user
fetchBasicProfileData();
}
}
private static Scope buildScope() {
//Check Scopes in Application Settings before passing here else you won't able to read that data
// Scope.R_CONTACTINFO
return Scope.build(Scope.R_BASICPROFILE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
LISessionManager.getInstance(getApplicationContext()).onActivityResult(this, requestCode, resultCode, data);
Log.d("Access token->", LISessionManager.getInstance(getApplicationContext()).getSession().getAccessToken().getValue());
}
/**
* method to fetch basic profile data
*/
private void fetchBasicProfileData() {
//In URL pass whatever data from user you want for more values check below link
//LINK : https://developer.linkedin.com/docs/fields/basic-profile
String url = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,public-profile-url,picture-url,email-address,picture-urls::(original))";
APIHelper apiHelper = APIHelper.getInstance(getApplicationContext());
apiHelper.getRequest(this, url, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse apiResponse) {
// Success!
JSONObject responseObject = apiResponse.getResponseDataAsJson();
try {
profileURL = responseObject.getString("publicProfileUrl");
imgURL = responseObject.getString("pictureUrl");
fetchConnectionsData();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onApiError(LIApiError liApiError) {
// Error making GET request!
Log.e("FETCH PROFILE ERROR", "Fetch profile Error :" + liApiError.getLocalizedMessage());
Toast.makeText(LinkedInActivity.this, "Failed. Please try again.", Toast.LENGTH_SHORT).show();
}
});
}
Linkedin returns "No value for accessTokenValue" and "access toke is not set". It was working like last month but suddenly it does not work and I could not find anything wrong with the code. After much digging on Google, I am still unable to find a solution. Or am I using the v1 api which I should not? Any help will be greatly appreciated.
This is because the token generated by Linkedin SDK can not be verified by backend. The best solution is to open a webview and use Linkedin web apis- Step 1 - Create a new Class LinkedinActivity
public class LinkedinActivity {
/****FILL THIS WITH YOUR INFORMATION*********/
//This is the public api key of our application
private static final String API_KEY = "apikey";
//This is the private api key of our application
private static final String SECRET_KEY = "secretcode";
//This is any string we want to use. This will be used for avoiding CSRF attacks. You can generate one here: http://strongpasswordgenerator.com/
private static final String STATE = "123456789";
//This is the url that LinkedIn Auth process will redirect to. We can put whatever we want that starts with http:// or https:// .
//We use a made up url that we will intercept when redirecting. Avoid Uppercases.
private static final String REDIRECT_URI = "https://example.com";
/*********************************************/
//These are constants used for build the urls
private static final String AUTHORIZATION_URL = "https://www.linkedin.com/oauth/v2/authorization";
private static final String ACCESS_TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken";
private static final String SECRET_KEY_PARAM = "client_secret";
private static final String RESPONSE_TYPE_PARAM = "response_type";
private static final String GRANT_TYPE_PARAM = "grant_type";
private static final String GRANT_TYPE = "authorization_code";
private static final String RESPONSE_TYPE_VALUE = "code";
private static final String CLIENT_ID_PARAM = "client_id";
private static final String STATE_PARAM = "state";
private static final String REDIRECT_URI_PARAM = "redirect_uri";
/*---------------------------------------*/
private static final String QUESTION_MARK = "?";
private static final String AMPERSAND = "&";
private static final String EQUALS = "=";
private WebView webView;
private ImageView close_icon;
private ProgressDialog pd;
//private OauthInterface oauthInterface;
String accessToken;
Context context;
Dialog dialog;
public LinkedinActivity(#NonNull Context context) {
this.context = context;
}
public void showLinkedin() {
dialog = new Dialog(context, R.style.AppTheme);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
// dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
dialog.setContentView(R.layout.linkedin_activity);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
pd = ProgressDialog.show(context, "", "Loading...", true);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.linkedCancel();
dialog.dismiss();
}
return true;
}
});
//oauthInterface = new OauthPresenter(this);
//get the webView from the layout
webView = (WebView) dialog.findViewById(R.id.activity_web_view);
close_icon = (ImageView) dialog.findViewById(R.id.close_icon);
close_icon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.linkedCancel();
dialog.dismiss();
}
});
//Request focus for the webview
webView.requestFocus(View.FOCUS_DOWN);
//Show a progress dialog to the user
//Set a custom web view client
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
//This method will be executed each time a page finished loading.
//The only we do is dismiss the progressDialog, in case we are showing any.
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String authorizationUrl) {
//This method will be called when the Auth proccess redirect to our RedirectUri.
//We will check the url looking for our RedirectUri.
if (authorizationUrl.startsWith(REDIRECT_URI)) {
Log.i("Authorize", "");
Uri uri = Uri.parse(authorizationUrl);
//We take from the url the authorizationToken and the state token. We have to check that the state token returned by the Service is the same we sent.
//If not, that means the request may be a result of CSRF and must be rejected.
String stateToken = uri.getQueryParameter(STATE_PARAM);
if (stateToken == null || !stateToken.equals(STATE)) {
Log.e("Authorize", "State token doesn't match");
return true;
}
//If the user doesn't allow authorization to our application, the authorizationToken Will be null.
String authorizationToken = uri.getQueryParameter(RESPONSE_TYPE_VALUE);
if (authorizationToken == null) {
Log.i("Authorize", "The user doesn't allow authorization.");
return true;
}
Log.i("Authorize", "Auth token received: " + authorizationToken);
//Generate URL for requesting Access Token
String accessTokenUrl = getAccessTokenUrl(authorizationToken);
//We make the request in a AsyncTask
new PostRequestAsyncTask().execute(accessTokenUrl);
} else {
//Default behaviour
Log.i("Authorize", "Redirecting to: " + authorizationUrl);
webView.loadUrl(authorizationUrl);
}
return true;
}
});
//Get the authorization Url
String authUrl = getAuthorizationUrl();
Log.i("Authorize", "Loading Auth Url: " + authUrl);
//Load the authorization URL into the webView
webView.loadUrl(authUrl);
dialog.show();
}
private static String getAccessTokenUrl(String authorizationToken) {
return ACCESS_TOKEN_URL
+ QUESTION_MARK
+ GRANT_TYPE_PARAM + EQUALS + GRANT_TYPE
+ AMPERSAND
+ RESPONSE_TYPE_VALUE + EQUALS + authorizationToken
+ AMPERSAND
+ CLIENT_ID_PARAM + EQUALS + API_KEY
+ AMPERSAND
+ REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI
+ AMPERSAND
+ SECRET_KEY_PARAM + EQUALS + SECRET_KEY;
}
/**
* Method that generates the url for get the authorization token from the Service
*
* #return Url
*/
private static String getAuthorizationUrl() {
return AUTHORIZATION_URL
+ QUESTION_MARK + RESPONSE_TYPE_PARAM + EQUALS + RESPONSE_TYPE_VALUE
+ AMPERSAND + CLIENT_ID_PARAM + EQUALS + API_KEY
+ AMPERSAND + REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI
+ AMPERSAND + STATE_PARAM + EQUALS + STATE
+ AMPERSAND + "scope=r_emailaddress";
}
private class PostRequestAsyncTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(context, "", "loading", true);
}
#Override
protected String doInBackground(String... urls) {
if (urls.length > 0) {
String url = urls[0];
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpost = new HttpPost(url);
try {
HttpResponse response = httpClient.execute(httpost);
if (response != null) {
//If status is OK 200
if (response.getStatusLine().getStatusCode() == 200) {
String result = EntityUtils.toString(response.getEntity());
//Convert the string result to a JSON Object
JSONObject resultJson = new JSONObject(result);
//Extract data from JSON Response
int expiresIn = resultJson.has("expires_in") ? resultJson.getInt("expires_in") : 0;
accessToken = resultJson.has("access_token") ? resultJson.getString("access_token") : null;
Log.e("Tokenm", "" + accessToken);
if (expiresIn > 0 && accessToken != null) {
Log.i("Authorize", "This is the access Token: " + accessToken + ". It will expires in " + expiresIn + " secs");
//Calculate date of expiration
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, expiresIn);
long expireDate = calendar.getTimeInMillis();
////Store both expires in and access token in shared preferences
SharedPreferences preferences = context.getSharedPreferences("user_info", 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("expires", expireDate);
editor.putString("accessToken", accessToken);
//oauthInterface.oauthAuthentication(accessToken, "linkedin", new HackedPrefence(getApplicationContext()).getDevice_token());
editor.commit();
return accessToken;
}
}
}
} catch (IOException e) {
Log.e("Authorize", "Error Http response " + e.getLocalizedMessage());
} catch (ParseException e) {
Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
} catch (JSONException e) {
Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
}
}
return accessToken;
}
#Override
protected void onPostExecute(String status) {
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.LinkedinSuccess(status);
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
interface LinkedinData {
void linkedCancel();
void LinkedinSuccess(String Token);
}
}
Step 2 - Call LinkdinActivity.java class using the following code
new LinkedinActivity(this).showLinkedin();
You can call the above code on the click of a button. Example
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new LinkedinActivity(this).showLinkedin();
}
});
Here is linkedin_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="#+id/activity_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/close_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="10dp"
android:layout_marginTop="22dp"
android:src="#drawable/ic_close_black"
android:layout_alignParentRight="true"/>
</RelativeLayout>
I implement LinkedIn Register in to my android app... I use LinkedIn Android SDK:
link and i follow these tutorial: tut
However I stuck in one big problem. I successful get token by method:
LISessionManager.getInstance(getContext()).init(mLoginActivity, buildScope(), new AuthListener() {
And after it i try to make APIHelper request to get profile data:
APIHelper apiHelper = APIHelper.getInstance(getContext());
apiHelper.getRequest(mLoginActivity, topCardUrl, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse s) {
But every time i try to get profile data i get following result:
exceptionMsg: access toke is not set
How can i fix it? I can't understand where is problem. Please help.
Linkedin currently stopped support for Mobile SDK that's why all started getting access token is not set.
For more information https://engineering.linkedin.com/blog/2018/12/developer-program-updates
Alternate to achieve the same in Android by this https://stackoverflow.com/a/22219383
public void login(){
LISessionManager.getInstance(getActivity()).init(getActivity(),
buildScope(), new AuthListener()
{
#Override
public void onAuthSuccess()
{
progress.show();
getUserData();
}
#Override
public void onAuthError(LIAuthError error) {
if(progress.isShowing())
progress.dismiss();
}
}, false);
}
private static Scope buildScope() {
return Scope.build(Scope.R_BASICPROFILE, Scope.R_EMAILADDRESS);
}
String u="https://api.linkedin.com/v1/people/~:(id,first-name,last- name,headline,picture-url,public_profile_url,industry,summary,specialties,picture-urls::(original),positions:(id,title,summary,start-date,end-date,is-current,company:(id,name,type,size,industry,ticker)),educations:(id,school-name,field-of-study,start-date,end-date,degree,activities,notes),associations,interests,num-recommenders,date-of-birth,publications:(id,title,publisher:(name),authors:(id,name),date,url,summary),patents:(id,title,summary,number,status:(id,name),office:(name),inventors:(id,name),date,url),languages:(id,language:(name),proficiency:(level,name)),skills:(id,skill:(name)),certifications:(id,name,authority:(name),number,start-date,end-date),courses:(id,name,number),recommendations-received:(id,recommendation-type,recommendation-text,recommender),honors-awards,three-current-positions,three-past-positions,volunteer)";
public void getUserData()
{
APIHelper apiHelper = APIHelper.getInstance(getActivity());
apiHelper.getRequest(getActivity(), u, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse result) {
try {
//progress.dismiss();
socialData = new SocialLoginBean();
JSONObject json = result.getResponseDataAsJson();
String fname = json.getString("firstName");
String lname = json.getString("lastName");
socialData.setFname(fname);
socialData.setLname(lname);
JSONObject pic = json.getJSONObject("pictureUrls");
JSONArray picarr = pic.getJSONArray("values");
profile_pic = picarr.getString(0);
socialData.setPictureUrl(profile_pic);
JSONObject obj = json.getJSONObject("positions").getJSONArray("values").getJSONObject(0);
JSONObject positions = obj.getJSONObject("company");
String companyDesc="",companyName="",industry="",jobTitle="";
if(obj.has("title"))
jobTitle = obj.getString("title");
if(obj.has("summary"))
companyDesc = obj.getString("summary");
if(positions.has("name"))
companyName = positions.getString("name");
if(json.has("industry"))
industry = json.getString("industry");
String publicProfileUrl = json.getString("publicProfileUrl");
socialData.setProfileUrl(publicProfileUrl);
socialData.setIndustry(industry);
socialData.setCompany(companyName);
socialData.setDesignation(jobTitle);
socialData.setCompanyDescription(companyDesc);
moveTo(socialData);
} catch (Exception e) {
e.printStackTrace();
if(progress.isShowing())
progress.dismiss();
moveTo(socialData);
}
}
#Override
public void onApiError(LIApiError error) {
if(progress.isShowing())
progress.dismiss();
}
});
}
I want when server sends some response in form of WebView then immediately my activity gets refreshed and so WebView in form of banner ad.
I write code for display banner ad but ad is showing only when my activity recreated i.e. when I rotate my screen then banner is showing but when it is in same static mode then banner is not showing.
So, please let me know what I will do so that when server gave some response immediately it will be shown on my activity.
void startDemo() {
//Set Http Client Options
final OptimusHTTP client = new OptimusHTTP();
client.enableDebugging();
client.setMethod(OptimusHTTP.METHOD_POST);
client.setMode(OptimusHTTP.MODE_SEQ);
FreqDetector_Goertzel.getInstance().startRecording(new FreqDetector_Goertzel.RecordTaskListener() {
private String urlRedirect = "";
private String imgSmallBanner = "";
#Override
public void onSuccess(int val)
{
String pSet = pVal.getPatternSet(val, 5);
if (pSet != null) {
FreqDetector_Goertzel.getInstance().stopRecording();
EasyDeviceInfo deviceInfo = new EasyDeviceInfo(MainActivity.this);
final HashMap<String, String> device_params = new HashMap<>();
device_params.put("aid", deviceInfo.getAndroidID());
device_params.put("pattern", pSet);
if (isNetworkAvailable(MainActivity.this)) {
try {
client.makeRequest(MainActivity.this, new HttpReq(), Defaults.MATCHINGSERVER, device_params, new OptimusHTTP.ResponseListener() {
#Override
public void onSuccess(String s) {
try {
if (s != null && !s.contains("No Match Found"))
{
JSONObject jsonObject = null;
jsonObject = new JSONObject(s);
imgSmallBanner = Uri.decode(jsonObject.optString("smallImgUrl", "NA"));
urlRedirect = Uri.decode(jsonObject.optString("redirectUrl", "NA"));
loadAdvertisement(urlRedirect, imgSmallBanner);
} else {
//Did not match
startDemo();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(String s) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
} else {
//Internet not available. Do not do anything.
}
}
}
#Override
public void onFailure(String s) {
}
});
}
void loadAdvertisement(String clickUrl, String imgSmallName) {
String click_url;
String img_small_url;
stopDemo();
click_url = Uri.decode(Uri.encode(clickUrl));
img_small_url = imgSmallName;
StringBuilder htmlData2 = new StringBuilder();
htmlData2.append("<html><body style='margin:0;padding:0;background-color:black;'><a href='").append(click_url).append("' ><img src='").append(img_small_url).append("' height=50 style='margin:0 auto;display:block;' /></a></body></html>");
webView_img_small.loadDataWithBaseURL("file:///android_asset/", htmlData2.toString(), "text/html", "utf-8", null);
webView_img_small.setVisibility(View.VISIBLE);
/* What I will do here so when server sends response it will immediately being refreshed and shown on activity without recreating it.*/ }
here you can find some response: http://developer.android.com/guide/topics/ui/how-android-draws.html
for me a call to invalidate() only refresh the view and a call to requestLayout() refresh the view and compute the size of the view in the screen.
You can try to use Activity.recreate(). This method will destroy your current Activity and create a new Activity same way when you rotate device.
Hope this helps.
I have been trying to login to google talk using the asmack library without success. I don't really know what is happening behind the scenes, just gathered some code snippets from here and there. This is what I have currently for the android activity:
public class MainActivity extends Activity {
public static final String HOST = "talk.google.com";
public static final int PORT = 5222;
public static final String SERVICE = "gmail.com";
public static final String USER = "user#gmail.com";
public static final String PASSWORD = "password";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Context context = getApplicationContext();
SmackAndroid asmk = SmackAndroid.init(context);
SASLAuthentication.registerSASLMechanism("X-OAUTH2", SASLGoogleOAuth2Mechanism.class);
SASLAuthentication.supportSASLMechanism("X-OAUTH2", 0);
ConnectionConfiguration connConfig = new ConnectionConfiguration(HOST, PORT, SERVICE);
connConfig.setSecurityMode(SecurityMode.enabled);
connConfig.setReconnectionAllowed(true);
XMPPTCPConnection connection = new XMPPTCPConnection(connConfig);
try {
connection.connect();
try {
connection.login(USER, PASSWORD);
} catch (XMPPException ex) {
Log.w("XMPPChatDemoActivity", "Failed to log in");
Log.w("XMPPChatDemoActivity", ex.getMessage());
}
} catch (...) {
...
}
}
}
and this is the SASLMechanism:
public class SASLGoogleOAuth2Mechanism extends SASLMechanism {
private static final Logger log = Logger.getLogger("XMPPChatDemoActivity");
public static final String NAME = "X-OAUTH2";
public SASLGoogleOAuth2Mechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
log.info("Creating SASL mechanism for GTalk (X-OAUTH2)");
}
#Override
public void authenticate(String username, String host, String serviceName, String password) throws IOException, SaslException, NotConnectedException {
this.authenticationId = username;
this.hostname = host;
this.password = password;
String[] mechanisms = { "PLAIN" };
Map<String, String> props = new HashMap<String, String>();
this.sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
log.info("sc " + sc);
authenticate();
}
#Override
public void authenticate(String host, CallbackHandler cbh) throws IOException, SaslException, NotConnectedException {
String[] mechanisms = { "PLAIN" };
Map<String, String> props = new HashMap<String, String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
authenticate();
}
#Override
protected void authenticate() throws IOException, SaslException, NotConnectedException {
String authenticationText = null;
try {
if (sc.hasInitialResponse()) {
byte[] response = sc.evaluateChallenge(new byte[0]);
authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
} catch (SaslException e) {
throw new SaslException("SASL authentication failed", e);
}
// Send the authentication to the server
getSASLAuthentication().send(new GoogleOAuthMechanism(authenticationText));
}
#Override
protected String getName() {
return NAME;
}
/**
* Initiating SASL authentication by select a mechanism.
*/
public static class GoogleOAuthMechanism extends Packet {
private final String authenticationText;
/**
* Create a GoogleOAuthMechanism.
*
* #param authenticationText the authentification token
*
*/
public GoogleOAuthMechanism(final String authenticationText) {
this.authenticationText = authenticationText;
}
#Override
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("<auth mechanism=\"").append(NAME);
stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
+ "auth:service=\"oauth2\" "
+ "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">");
if (authenticationText != null
&& authenticationText.trim().length() > 0) {
stanza.append(authenticationText);
}
stanza.append("</auth>");
return stanza.toString();
}
}
}
The code is ok and i don't get any exception, but I get a <not-authorized> response. The user name and password are correct. I couldn't find any reference code for this library. Any help will be appreciated.
After struggling for a few days and trying every imaginable combination of the snippets that I found on the Internet, I've come across a solution that I'm glad to share with the community.
It appears that instead of passing the password to the XMPPTCPConnection.login() method, we should use an auth token from google. I found a post explaining a way to generate such a token. A similar question to mine exists, but it uses X-GOOGLE-TOKEN mechanism also for authentication, which is a different approach to mine of using X-OAUTH2 mechanism for authentication. Furthermore, all other posts I could find relating to the problem of authenticating to google talk using OAUTH2 are old. I am using the asmack build of smack 4.0.4.
So the only modification required for the code shown in the question to work is this:
AccountManager am = AccountManager.get(this);
Account accounts[] = am.getAccountsByType("com.google");
conn.login(USER, amf.blockingGetAuthToken(accounts[0], GOOGLE_TOKEN_TYPE, true));
As you see, I used an account stored on the device to proof the solution, but you can generate the token by other means, as I commented above.
Finally, as I found the solution by trial and error, I would appreciate anyone explaining what is really happening or any misinformation I could have given, so this answer can be further improved.
Running 3.1 (Honeycomb) on Galaxy Tab 10.1
Regardless of several different methods, I have been unable to reset the basic auth username and password for a WebView. The only way I can reset these values is to restart the app. I have searched around and have yet to find a solution and even dug into the Android source.
This code is from an activity that is created every time I want to display a webpage that requires basic auth. Some parts shouldn't have any effect but were tried out of frustration. Even when I exit this activity (which is then destroyed) and relaunch it with an intent from my main activity, the basic auth information remains and onReceivedHttpAuthRequest in the WebViewClient is never executed again.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base_simple_v01);
findViewById(R.id.lyt_bsv01_layout).setBackgroundColor(0xFF000000);
baseContainer = (ViewGroup) findViewById(R.id.lyt_bsv01_baseContainer);
statusProgressBar = (ProgressBar) findViewById(R.id.lyt_bsv01_statusProgress);
resultNotificationTextView = (TextView) findViewById(R.id.lyt_bsv01_resultNotification);
// -- Attempt to prevent and clear WebView cookies
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieManager.setAcceptCookie(false);
// -- Attempt to clear WebViewDatabase
WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();
WebViewDatabase.getInstance(this).clearUsernamePassword();
WebViewDatabase.getInstance(this).clearFormData();
// -- Brute force attempt to clear WebViewDatabase - didn't work
//deleteDatabase("webview.db");
//deleteDatabase("webviewCache.db");
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
networkWebView = (WebView)vi.inflate(R.layout.social_connect, baseContainer, false);
// -- Removes white flickering in Honeycomb WebView page loading.
networkWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
networkWebView.getSettings().setJavaScriptEnabled(true);
networkWebView.getSettings().setSavePassword(false);
networkWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
networkWebView.clearSslPreferences();
networkWebView.setWebViewClient(mLocalDataRequester.endorseBackendAuthWebViewClient(
new BackendAuthWebViewClient() {
#Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
Toast.makeText(getApplicationContext(), "AUTH REQUESTED", Toast.LENGTH_SHORT).show();
super.onReceivedHttpAuthRequest (view, handler, host, realm);
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
Toast.makeText(getApplicationContext(), "SSL ERROR", Toast.LENGTH_SHORT).show();
super.onReceivedSslError(view, handler, error);
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
statusProgressBar.setVisibility(View.VISIBLE);
networkWebView.setVisibility(View.INVISIBLE);
}
#Override
public void onPageFinished(WebView view, String url) {
statusProgressBar.setVisibility(View.INVISIBLE);
networkWebView.setVisibility(View.VISIBLE);
}
})
);
baseContainer.addView(networkWebView);
networkWebView.setVisibility(View.INVISIBLE);
networkWebView.setBackgroundColor(0x00000000);
clearWebView();
}
private void clearWebView() {
networkWebView.loadData("", "text/html", "utf-8");
//networkWebView.clearView();
networkWebView.clearCache(false);
networkWebView.clearCache(true);
networkWebView.clearFormData();
networkWebView.clearHistory();
networkWebView.clearCache(true);
networkWebView.clearMatches();
networkWebView.freeMemory();
}
#Override
public void onResume() {
super.onResume();
networkWebView.loadUrl(mBackendNetworkConnectUrl);
WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getApplicationContext(), "Destruction", Toast.LENGTH_SHORT).show();
networkWebView.destroy();
}
This is a WebViewClient subclass that is initialized with the basic auth credentials. I have verified that the username and password change when an authentication should occur.
public class BackendAuthWebViewClient extends WebViewClient {
private AuthenticateData mAuthenticateData = null;
public BackendAuthWebViewClient() {
}
public BackendAuthWebViewClient(AuthenticateData authenticateData) {
this.mAuthenticateData = authenticateData;
}
#Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm){
handler.proceed(mAuthenticateData.mUserId, mAuthenticateData.mUserPassword);
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
public void setAuthenticatedData(AuthenticateData authenticateData) {
this.mAuthenticateData = authenticateData;
}
}
I have tried the following to no avail:
Android WebView - reset HTTP session
Clearing user's Facebook session in Webview
Delete data in the browser
Make Android WebView not store cookies or passwords
Android WebView Cookie Problem
This is interesting but necessity of the brute force would be disappointing. Though I'll try it next.
EDIT: Didn't work.
Android Webview - Completely Clear the Cache
I am pretty sure its a bug in WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();,
because
deleteDatabase("webview.db");
does the trick for me.
Issue 25507: WebViewDatabase.clearHttpAuthUsernamePassword() does not work
Although it doesn't address the original reset WebView basic auth issue, I'm using this as a workaround. Using this SO as a reference:
Android Webview POST
This solution uses a HttpClient request (preferably in another thread or AsyncTask to avoid ANR - application not responding) and then loading that response into a WebView. Since I need to interact with links on the loaded page, I need to use loadDataWithBaseURL.
For this answer, I license all code below under Apache License 2.0.
HttpClient code - best used in another thread or AsyncTask. Variables authenticateData, method, url, and nameValuePairs will need to be defined or removed.
public String send() {
try {
// -- Create client.
HttpParams httpParameters = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is established.
int timeoutConnection = 10000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 10000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
HttpGet httpGet;
HttpPost httpPost;
HttpDelete httpDelete;
HttpResponse httpResponse;
String authHeader;
if( authenticateData != null ) {
// -- Set basic authentication in header.
String base64EncodedCredentials = Base64.encodeToString(
(authenticateData.username + ":" + authenticateData.password).getBytes("US-ASCII"), Base64.URL_SAFE|Base64.NO_WRAP);
authHeader = "Basic " + base64EncodedCredentials;
} else {
authHeader = null;
}
// -- Send to server.
if( method == GET ) {
httpGet = new HttpGet(url);
if( authHeader != null ) {
httpGet.setHeader("Authorization", authHeader);
}
httpResponse = httpClient.execute(httpGet);
}
else if( method == POST) {
httpPost = new HttpPost(url);
if( authHeader != null ) {
httpPost.setHeader("Authorization", authHeader);
}
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
httpResponse = httpClient.execute(httpPost);
}
else if( method == DELETE) {
httpDelete = new HttpDelete(url);
httpDelete.setHeader("Content-Length", "0");
if( authHeader != null ) {
httpDelete.setHeader("Authorization", authHeader);
}
httpResponse = httpClient.execute(httpDelete);
}
else {
return null;
}
// -- Method 1 for obtaining response.
/*
InputStream is = httpResponse.getEntity().getContent();
// -- Convert response.
Scanner scanner = new Scanner(is);
// -- TODO: specify charset
String response = scanner.useDelimiter("\\A").next();
*/
// -- Method 2 for obtaining response.
String response = new BasicResponseHandler().handleResponse(httpResponse);
return response;
}
catch(SocketTimeoutException exception) {
exception.printStackTrace();
}
catch(ConnectTimeoutException exception) {
exception.printStackTrace();
}
catch(NoHttpResponseException exception) {
exception.printStackTrace();
}
catch(UnknownHostException exception) {
exception.printStackTrace();
}
catch(ClientProtocolException exception) {
exception.printStackTrace();
}
catch(IOException exception) {
exception.printStackTrace();
}
return null;
}
WebView code - should be in the activity that contains the WebView.
WebView webView = new WebView(Activity.this);
webView.loadDataWithBaseURL(url, response, "text/html", "utf-8", null);
I suggest not actually calling setHttpAuthUsernamePassword() at all.
Rather, only use onReceivedHttpAuthRequest() to handle the auth challenge dynamically every time.
This, coupled with
WebViewDatabase.getInstance(getContext()).clearHttpAuthUsernamePassword();
WebViewDatabase.getInstance(getContext()).clearUsernamePassword();
WebViewDatabase.getInstance(getContext()).clearFormData();
calls on load to clear out legacy entries, and my problem with this went away.
It turned out, there are two potential issues here.
One being the WebViewDatabase.clearHttpAuthUsernamePassword() seems not working correctly on some devices/versions of Android, in a way that calling WebView.getHttpAuthUsernamePassword() still yields the stored password after clearing the database.
This one can be addressed by implementing these methods yourself.
The second issue is, that the auth data also seems to be stored in memory, which is basically a good thing, because the WebView has not to query the database for every subsequent HTTP request. However this cache seems to be shared between all WebViews and there is no obvious method to clear it. It turns out though, that WebViews created with privateBrowsing = true, share a different cache which also behaves slightly different: After the last private browsing WebView is being destroyed, this cache seems to be cleared out completely, and the next request will actually trigger onReceivedHttpAuthRequest.
Below a complete working example of those two workarounds. If you have to deal with multiple WebViews it might get more complex, because you need to make sure to destroy them all, before recreating them.
public class HttpAuthTestActivity extends Activity {
ViewGroup webViewContainer;
Button logoutButton;
Button reloadButton;
WebView webView;
AuthStoreInterface authStore;
public interface AuthStoreInterface {
public void clear();
public void setHttpAuthUsernamePassword(String host, String realm, String username, String password);
public Pair<String, String> getHttpAuthUsernamePassword(String host, String realm);
}
//if you want to make the auth store persistent, you have implement a persistent version of this interface
public class MemoryAuthStore implements AuthStoreInterface {
Map<Pair<String, String>, Pair<String, String>> credentials;
public MemoryAuthStore() {
credentials = new HashMap<Pair<String, String>, Pair<String, String>>();
}
public void clear() {
credentials.clear();
}
public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) {
credentials.put(new Pair<String, String>(host, realm), new Pair<String, String>(username, password));
}
public Pair<String, String> getHttpAuthUsernamePassword(String host, String realm) {
return credentials.get(new Pair<String, String>(host, realm));
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
authStore = new MemoryAuthStore();
webViewContainer = (ViewGroup)findViewById(R.id.webview_container);
logoutButton = (Button)findViewById(R.id.logout_button);
reloadButton = (Button)findViewById(R.id.reload_button);
createWebView();
logoutButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
authStore.clear();
destroyWebView();
createWebView();
}
});
reloadButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
webView.reload();
}
});
}
#Override
protected void onDestroy() {
webView.destroy();
super.onDestroy();
}
private void destroyWebView() {
webView.destroy();
webViewContainer.removeView(webView);
}
private void createWebView() {
//this is the important line: if you use this ctor with privateBrowsing: true, the internal auth cache will
//acutally be deleted in WebView.destroy, if there is no other privateBrowsing enabled WebView left only
webView = new WebView(this, null, android.R.attr.webViewStyle, true);
webView.setWebViewClient(new WebViewClient() {
#Override
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {
Pair<String, String> credentials = authStore.getHttpAuthUsernamePassword(host, realm);
if (credentials != null && handler.useHttpAuthUsernamePassword()) {
handler.proceed(credentials.first, credentials.second);
} else {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View form = inflater.inflate(R.layout.http_auth_request, null);
new AlertDialog.Builder(HttpAuthTestActivity.this).setTitle(String.format("HttpAuthRequest (realm: %s, host %s)", realm, host))
.setView(form).setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
EditText usernameEdt = (EditText) form.findViewById(R.id.username);
EditText passwordEdt = (EditText) form.findViewById(R.id.password);
String u = usernameEdt.getText().toString();
String p = passwordEdt.getText().toString();
authStore.setHttpAuthUsernamePassword(host, realm, u, p);
handler.proceed(u, p);
}
}).setCancelable(true).setNegativeButton(android.R.string.cancel, new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
handler.cancel();
}
}).create().show();
}
}
});
webView.loadUrl("http://httpbin.org/basic-auth/test/test");
webViewContainer.addView(webView);
}
}
I also had this issue. And found the solution, hope this can help you.
First of all, the method onReceivedHttpAuthRequest() is only called one time in an application except use cookies.
I had write the method:
public void syncCookie(Context context, String url) {
HttpClient httpClient = HttpClientContext.getInstance();
Cookie[] cookies = httpClient.getState().getCookies();
Cookie sessionCookie = null;
if (cookies.length > 0) {
sessionCookie = cookies[cookies.length - 1];
}
CookieManager cookieManager = CookieManager.getInstance();
if (sessionCookie != null) {
String cookieString = sessionCookie.getName() + "="
+ sessionCookie.getValue() + ";domain="
+ sessionCookie.getDomain();
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context);
cookieSyncManager.startSync();
cookieManager.setCookie(url, cookieString);
CookieSyncManager.getInstance().sync();
}
}
use like this:
WebView webView = ...;
webView.getSettings().setJavaScriptEnabled(true);
syncCookie(this,url);
webView.loadUri(url);
webView.setWebViewClient();
I used multi-process to solve this problem.
As WebView in your Activity/Fragment need to handle Http Basic Authentication, onRecievedHttpAuthRequest() will be trigger. Creating a dialog for user to input login info.
onRecievedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, String realm){
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.dialog_layout);
dialog.findViewById(R.id.confirmBtn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String account = ((EditText)dialog.findViewById(R.id.accountET)).getText().toString();
final String pwd = ((EditText)dialog.findViewById(R.id.pwdET)).getText().toString();
serviceIntent = new Intent(context, SSOAuthService.class);
serviceIntent.putExtra("url", authUrl);
serviceIntent.putExtra("account", account);
serviceIntent.putExtra("pwd", pwd);
context.startService(serviceIntent);
dialog.dismiss();
}
});
dialog.show();
}
Launch a service contains a webview to handle http basic auth, and pass account and pwd get from the dialog above.
public class AuthService extends Service {
private String account;
private String pwd;
private String url;
private String webView;
private boolean isProcess = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
url = (String) intent.getExtras().get("url");
account = (String) intent.getExtras().get("account");
pwd = (String) intent.getExtras().get("pwd");
webView = new WebView(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
//todo Do whatever u want to do.
closeServiceAndProcess();
}
#Override
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, String realm) {
if (!isProcess) {
isProcess = true;
handler.proceed(account, pwd);
} else {
isProcess = false;
closeServiceAndProcess();
}
}
});
webView.loadUrl(url);
return Service.START_REDELIVER_INTENT;
}
private void closeServiceAndProcess() {
stopSelf();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
As complete http basic authentication in the AuthService, kill the process which the AuthService is lived. And http basic authentication could be reset.
I have another solution that seems to work fairly well. Basically you load the URL using WebView.loadUrl(url, additionalHeaders) and pass a blank Authorization header in. This seems to reset the webview properly. The only issue is that you'll the onReceivedHttpAuthRequest will get called in a loop if you don't reload the original URL. So once you get the onReceivedHttpAuthRequest you will need to collect the username/password from the user, and then reload the original URL without passing a blank Authorization header.
Essentially it works like this below (specific code is untested).
MyLoginActivity extends AppCompatActivity {
private boolean clearAuth = true;
private String mUsername, mPassword, mHost, mRealm;
private static final String URL = "https://yourdomain.com";
public void onCreate(bundle ss) {
super.onCreate(ss);
webview = findViewById(R.id.webview);
webview.setWebViewClient(new WebViewClient() {
#Override
public void onReceivedHttpAuthRequest(View webview, HttpAuthHandler authHandler, String host, String realm) {
mAuthHandler = authHandler;
mHost = host;
mRealm = realm;
if (mUsername != null && mPassword != null && authHandler.useHttpAuthUsernamePassword()) {
proceed(mUsername, mPassword);
} else {
showLoginDialog();
}
});
}
public void onDestroy() {
super.onDestroy();
//ensure an auth handler that was displayed but never finished is cancelled
//if you don't do this the app will start hanging and acting up after pressing back when a dialog was visible
if (mAuthHandler != null) {
mAuthHandler.cancel();
}
mAuthHandler = null;
}
public void onCredentialsProvided(String username, String password) {
mUsername = username;
mPassword = password;
if (clearAuth) {
//reload the original URL but this time without adding the blank
//auth header
clearAuth = false;
loadUrl(URL);
} else {
proceed(username, password);
}
}
private void loadUrl(String url) {
if (clearAuth) {
Map<String, String> clearAuthMap = new HashMap();
map.put("Authorization", "");
webView.loadUrl(url, clearAuthMap);
} else {
webView.loadUrl(url);
}
}
private void proceed(String username, String password) {
mAuthHandler.proceed(username, password);
mAuthHandler = null;
}
private void showLoginDialog() {
//show your login dialog here and when user presses submit, call
//activity.onCredentialsProvided(username, password);
}
}