I would like to store my server response as a string in Shared Preferences so that I can use this response later without fetching from server. But when I saved the data in Shared Preferences and later I use I miss the whole response. I have no security issue and any other cause like uninstall app etc. My question is whether the response will be lost in Shared Preferences so that the response contain multiple JSONObject and JSONArray.
private void productListApi(String url){
final ProgressDialog pDialog = new ProgressDialog(mContext);
pDialog.setMessage(mContext.getResources().getString(R.string.loading_message));
pDialog.show();
System.out.println("product list urlllllllllllllllllllll:" + url);
RequestQueue mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext());
StringRequest postRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
System.out.println("response of product list data is:"
+ response);
apiResponse = response ;
pDialog.dismiss();
try {
JSONObject json = new JSONObject(response);
if (json.has("code")) {
if (code.equalsIgnoreCase("200")) {
//set response to shared preference
SharedPreference.setStringValue(mContext, SharedPreference.PRODUCT_LIST_RESPONSE, response);
parseData(SharedPreference.getStringValue(mContext,SharedPreference.PRODUCT_LIST_RESPONSE));
}else {
UserDialog
.showUserAlert(mContext,
mContext.getResources().getString(R.string.product_list_failed));
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
pDialog.dismiss();
UserDialog.showUserAlert(mContext,
mContext.getResources().getString(R.string.no_response));
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// the POST parameters:
params.put("user_id", SharedPreference.getStringValue(getActivity(), SharedPreference.USER_ID));
params.put("temp_user_id", SharedPreference.getStringValue(getActivity(), SharedPreference.TEMP_USER_ID));
params.put("version", Utilities.getVersionCode(mContext));
params.put("device_token", SharedPreference.getStringValue(getActivity(), SharedPreference.DEVICE_TOKEN));
return params;
}
};
int socketTimeout = Constant.socketTimeout
;//30 seconds - change to what you want
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
//RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
postRequest.setRetryPolicy(policy);
mRequestQueue.add(postRequest);
}
public class SharedPreference {
SharedPreferences preferences;
SharedPreferences.Editor editor;
private static final String PREFS_NAME = "nevada_food";
public static final String PRODUCT_LIST_RESPONSE = "product_list" ;
public SharedPreference() {
super();
// TODO Auto-generated constructor stub
}
public static String getStringValue(final Context context, String key) {
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.getString(key, "");
}
public static void setStringValue(final Context context, String key,
String value) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFS_NAME, Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = prefs.edit();
editor.putString(key, value);
editor.commit();
}
}
this is my server response. I saved it in Shared Preferences and parse data in UI from Shared Preferences. This method
parseData(SharedPreference.getStringValue(mContext,SharedPreference.PRODUCT_LIST_RESPONSE));
works properly first time when directly from server but when we use this method in another it does not work.
Data stored in SharedPreference does not loss until unless:
You clear it by manually or programmatically.
Any cache clearing application won't clear application cache.
You uninstall your application.
Depending on scenario you can opt from following approach to save/cache your json data/response:
SharedPreference more convenient to use.
Writing it text file as private or public in external/internal storage. Data stored in external store won't loss even if you uninstall application.
If json is big and there is trade off associated with calling server every time you can go creating Sqlite database or can choose ORM tools like ORMLite, GreeDao etc.
Caching library can be used to cache server response.
My question is whether the response will be lost in Shared Preferences
Shared preferences won't be removed/lost, unless:
you remove them
user clears app data
So, you are safe to store there necessary data.
You are using a wrong way to save into SharedPreference.
Your Code:
//set response to shared preference
SharedPreference.setStringValue(mContext, SharedPreference.PRODUCT_LIST_RESPONSE, response);
parseData(SharedPreference.getStringValue(mContext,SharedPreference.PRODUCT_LIST_RESPONSE));
I don't have any clue what you written here. Try my solution below.
You should have to follow the simple SharedPreference format followed by its Editor.
Just save your data like below:
SharedPreferences.Editor editor = getSharedPreferences("PREFS_NAME", 0).edit();
editor.putString("PRODUCT_LIST_RESPONSE", response);
editor.commit();
And get it by the KEY (PRODUCT_LIST_RESPONSE here)
SharedPreferences prefs = getSharedPreferences("PREFS_NAME", 0);
String response = prefs.getString("PRODUCT_LIST_RESPONSE", any_default_Value);
I have two non-activity class and I need to send one string from one class to another, my approach is to use shared preference but I cannot get the shared string in the second class. First class will run every time the app is opened and will generate a token
First class:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIDService";
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
// [START refresh_token]
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
// [END refresh_token]
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM InstanceID token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
public void sendRegistrationToServer(String token) {
// TODO: Implement this method to send token to your app server.
SharedPreferences sp = getSharedPreferences("PUSHToken", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("PUSHToken", token);
editor.commit();
}
}
I am trying to pass the string token and store in my second non-activity class for my volley request:
public class VolleyPut {
private static final String TAG = "VolleyPut";
private static VolleyPut instance = null;
public static final String KEY_TOKEN = "token";
//for Volley API
public RequestQueue requestQueue;
SharedPreferences sp2=getSharedPreferences("PUSHToken", Context.MODE_PRIVATE);
String token = sp2.getString("PUSHToken", "");
private VolleyPut(Context context)
{
this.token = token;
requestQueue = Volley.newRequestQueue(context.getApplicationContext());
return;
}
public static synchronized VolleyPut getInstance(Context context)
{
if (null == instance)
instance = new VolleyPut(context);
return instance;
}
public static synchronized VolleyPut getInstance()
{
if (null == instance)
{
throw new IllegalStateException(VolleyPut.class.getSimpleName() +
" is not initialized, call getInstance(...) first");
}
return instance;
}
public void VolleyPUT(String domain, String api, final String finalToken, final CustomListener<String> listener){
JSONArray jsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
try{
jsonObject.put("token", token);
jsonArray.put(jsonObject);
Log.i("JsonString :", jsonObject.toString());
}catch (Exception e){
System.out.println("Error:" + e);
}
StringRequest sr = new StringRequest(Request.Method.PUT, domain + api,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.e("HttpClient", "success! response: " + response);
if(null != response)
listener.getResult(response);
return;
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (null != error.networkResponse)
{
Log.d(TAG + ": ", "Error Response code: " + error.networkResponse.statusCode);
//listener.getResult(false);
}
}
})
{
#Override
public Map<String,String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers= new HashMap<>();
headers.put("Authorization",finalToken);
headers.put("Content-Type","application/json");
headers.put(KEY_TOKEN,token);
return headers;
}
};
requestQueue.add(sr);
}
}
However I am getting error unable to resolve method getsharedpreference in my second class. How is the way to solve this and are there anyway to pass strings between non-activity class?
The problem is that FirebaseInstanceIdService innherits from class android.content.Context so, in MyFirebaseInstanceIDService you have a Context and can use getSharedPreferences.
The solution is (to modify the minimum code) pass a context to the second class, and use it to get the shared preferences.
Better solution (my own think) is create a custom Application and use it always as a common context for all your app. Lower params and lower dependencies, because you always have the "CustomApplication" and can use it.
Here how to implement it:
https://stackoverflow.com/a/27416998/585540
Remember, if you use this approach, must put it in Manifest:
<application ....
android:name="com.you.yourapp.CustomApplication"
......>
You can create a global variable as:-
public Context context;
public VolleyPut(Context context)
{
this.token = token;
this.context=context
requestQueue = Volley.newRequestQueue(context.getApplicationContext());
return;
}
SharedPreferences sp2=**context**.getSharedPreferences("PUSHToken", Context.MODE_PRIVATE);
This will resolve error unable to resolve method getsharedpreference
This my class for network calls. Here executing a method networkCallByVolley then saving the information on shared preferences.
public class NetworkCall extends Activity {
Context context;
String res = "something";
SharedPreferences userDetails;
ArrayList<String> type = new ArrayList<String>();
ArrayList<String> value = new ArrayList<String>();
public NetworkCall(Context context){
this.context = context;
}
public void networkCallByVolley(final ArrayList<String> type, final ArrayList<String> value){
this.type = type;
this.value = value;
Log.i("type", type.toString());
Log.i("value", value.toString());
RequestQueue queue = Volley.newRequestQueue(context);
StringRequest myReq = new StringRequest(Method.POST,
"http://My URL",
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.i("rrrrr", response);
res = response;
userDetails = context.getSharedPreferences("userdetails", MODE_PRIVATE);
Editor edit = userDetails.edit();
edit.clear();
edit.putString("response", response);
edit.commit();
//Log.i("rrrrr", response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
protected Map<String, String> getParams() throws com.android.volley.AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
//for(int i = 0; i<= params1.size(); i++)
for(int i =0 ; i< type.size(); i++){
params.put(type.get(i), value.get(i));
//params.put("password", "aaaaaa");
Log.i("typpppp", type.get(i));
}
return params;
};
};
queue.add(myReq);
}
public String getCharlie(){
userDetails = context.getSharedPreferences("userdetails", MODE_PRIVATE);
return userDetails.getString("response", "no value found");
}
public void clearCharlie(){
SharedPreferences.Editor edit = userDetails.edit();
edit.clear();
edit.commit();
}
}
when i am trying to use this class from login activity i am getting message for the below log "pref response" is "no value found". if run it again i am getting proper response which i am expecting. I don't know how to fix this bug. any help is appreciated.
this is my main activity
public class Login extends Activity {
Button signup,submit;
EditText email,password;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
final NetworkCall net = new NetworkCall(getApplicationContext());
final ArrayList<String> type = new ArrayList<String>();
final ArrayList<String> value = new ArrayList<String>();
submit = (Button) findViewById(R.id.submit);
email = (EditText) findViewById(R.id.edittext_email);
password = (EditText) findViewById(R.id.edittext_pwd);
type.add("user_email");
type.add("user_password");
value.add(email.getText().toString().trim());
value.add(password.getText().toString().trim());
submit.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
net.networkCallByVolley(type, value);
String response = net.getCharlie();
Log.i("pref resonse", response);
}
});
}
}
You need to use ASYNC task or handler.
What you are doing is calling getCharlie() method right after you made the call net.networkCallByVolley(type, value); It takes some time to get response from the sever after which only it will write the response to the shared prefrences. You are getting no result found because at that time there is no response. Respone is being calculated on some another parallel thread. As soon as that thread gets the response it show that to you but until then you will be having null. So wait for the thread to get response using ASYNC task.
#Override
public void onClick(View v) {
net.networkCallByVolley(type, value);
String response = net.getCharlie();
while(response.equalsIgnorecase("no result found")){}
Log.i("pref resonse", response);
}
Actually you need to wait until getting response from Volley.
You can use a BroadcastReceiver in your Login Activity
and send a broadcast from NetworkCall Activity after you get a response from the server, and when you receive a broadcast do what you want to do e.g checking SharedPreferences.
Alternative way you can use is Observer Pattern to get notice when you get response from server.
I am trying to make social network application for Android. My question is how to maintain user session when user logs in?
Please help me to find the solution for the above question.
try
public class Session {
private static String sessionId;
private static String userRole;
public static void setSessionId(String sessionId) {
Session.sessionId = sessionId;
}
public static String getSessionId() {
return sessionId;
}
}
Use this class and import it in every other activity. You can define your own functions to maintain your specific session data
http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/
Please look at the above link. It is detailed pretty well.
Use a singleton to maintain the user session.
I use DefaultHttpClient with HttpRequestInterceptor and HttpResponseInterceptor.
Something similar to this:
public class HTTPClients {
private static DefaultHttpClient _defaultClient;
private static String session_id;
private static HTTPClients _me;
private HTTPClients() {
}
public static DefaultHttpClient getDefaultHttpClient(){
if ( _defaultClient == null ) {
_defaultClient = new DefaultHttpClient();
_me = new HTTPClients();
_defaultClient.addResponseInterceptor(_me.new SessionKeeper());
_defaultClient.addRequestInterceptor(_me.new SessionAdder());
}
return _defaultClient;
}
private class SessionAdder implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context)
throws HttpException, IOException {
Log.d("SessionKeeper", "Adding session with the following string: " + session_id);
if ( session_id != null ) {
request.setHeader("Cookie", session_id);
}
}
}
private class SessionKeeper implements HttpResponseInterceptor {
#Override
public void process(HttpResponse response, HttpContext context)
throws HttpException, IOException {
Header[] headers = response.getHeaders("Set-Cookie");
if ( headers != null && headers.length == 1 ){
Log.d("SessionKeeper", "Keeping session with the following string: " + headers[0].getValue());
session_id = headers[0].getValue();
}
}
}
}
I have the similar problem on my android client side when I am trying to send that session id ,the server side is creating a new session...but what you check at android client side that you are not creating the DefaulthttpClient twice... create the httpclient just once say main activity and pass the objects in other activity ...... dont create second HttpClient
Create session using SharedPreferences.
public class Session {
private SharedPreferences prefs;
public Session(Context cntx) {
// TODO Auto-generated constructor stub
prefs = PreferenceManager.getDefaultSharedPreferences(cntx);
}
public void setusename(String usename) {
prefs.edit().putString("usename", usename).commit();
}
public String getusename() {
String usename = prefs.getString("usename","");
return usename;
}
}
now after making this class when u want to use this use like this make object og this class like
private Session session;//global variable
session = new Session(cntx); //in oncreate
//and now we set sharedpreference then use this like
session.setusename("USERNAME");
now when ever u want to get username then same work for session object and call this
session.getusename();
best of luck :) same for password
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);
}
}