I'm developing an Android app and I'm kind of new to this. What I'm currently trying to do is to maintain a user logged in. To login I have to make a request to an API and send the user email and password, that returns a JSONObject which I manage just fine. I have a CookieStore variable to get the cookies and store them into my SharedPreferences.
I just realised my cookies get lost if I close the application and I need to keep making requests to the API even if the app gets resumed, because the user has already logged in. I tried to "restore" the cookies in the onResume() method of one of my activities but that doesn't work the way I want it to. If I try to get the cookies using CookieStore.getCookies() after I resume my app that list is null.
I've been told I can use loopj's AsynHttpClient and manage my cookies with PersistentCookieStore but wouldn't that get me to the same problem? I'd be losing the value of the PersistentCookieStore instance every time I resume my app, right?
so my question is:
How can I restore the cookies in order to keep them persistent and make me able to keep making requests to the API?
Hope anyone can help me with this.
Thanks in advance.
You have two options:
Option 1 - If using SharedPreferences and HttpUrlConnection: you need to manually retrieve the cookie from the SharedPreferences and add the cookie to each request when using HttpUrlConnection.
Option 2 - If using loopj AsyncHttpClient: According to the loopj documentation, you must create an instance of PersistentCookieStore and add it to your AsyncHttpClient every time your app is restarted, like so
AsyncHttpClient myClient = new AsyncHttpClient();
PersistentCookieStore myCookieStore = new PersistentCookieStore(this);
myClient.setCookieStore(myCookieStore);
where 'this' is a Context.
How do you store/restore your cookies to/from the SharedPreferences? Can you share some code?
Also can you try debugging it and tell us what's wrong with it? Does it not get stored to the SharedPreferences? Does it fail to restore them?
Before storing/restoring the CookieStore to the SharedPreferences, you would need to create a Serializable object with it first, such as an ArrayList<HashMap<String, String>
Something like:
public static void storeCookies(Context context, CookieStore cookieStore) {
ArrayList<HashMap<String, String> cookies = new ArrayList<HashMap<String, String>();
for (Cookie cookie : cookieStore.getCookies()) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", cookie.getName());
map.put("path", cookie.getPath());
map.put("domain", cookie.getDomain());
map.put("expiry_date", cookie.getExpiryDate());
map.put("ports", cookie.getPorts());
map.put("value", cookie.getValue());
map.put("version", cookie.getVersion());
...
// Add all the fields you want to save
cookies.add(map);
}
SharedPreferences.Editor editor = context.getSharedPreferences().edit();
editor.putSerializable("cookies", cookies).apply();
}
public static void addCookiesToStore(Context context, CookieStore cookieStore) {
List<Map<String, String>> cookies = (ArrayList<HashMap<String, String>>) context.getSharedPreferences().getSerializable("cookies");
for (Cookie map : cookieStore.getCookies()) {
Cookie cookie = new BasicClientCookie(map.getName(), map.getValue());
cookie.setPath(map.get("path));
...
// Add all the fields you want to restore
cookieStore.add(cookie);
}
}
Related
I want to store data retrieved from API using volley, but if I made 2 different requests followed by each other , it shows the first request only before updating .
I have to request the second one again to store the new data.
Is there a solution?
SharedPreferences m=PreferenceManager.getDefaultSharedPreferences(context);
String resp=m.getString("Response","");
return resp;
private void share (String x)
{
SharedPreferences
m=PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor=m.edit();
editor.putString("Response",x);
editor.commit();
}
I am implementing an Android app that is responsible for some data exchange with other services such as credentials. I then want to use that information to automatically fill in the input fields of other applications on the device such as Spotify.
Is there any way to fill the input fields of another app, like the username and password to remove the chore for the user to manually input it?
Also I noticed that at least on iOS, Spotify recognizes 1Password to be installed and displays a small icon next to the input fields with which I can fill the fields from the data stored in 1Password - how is this done as it seems to be another solution to my problem?
Thanks in advance
You might want to implement Autofill Service https://developer.android.com/guide/topics/text/autofill-services.html
There is a ready to use sample app which will get you started https://github.com/googlesamples/android-AutofillFramework
Android will invoke onFillRequest() method giving your service a chance to show autofill suggestions. Here is a sample code from above link:
#Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
// Get the structure from the request
List<FillContext> context = request.getFillContexts();
AssistStructure structure = context.get(context.size() - 1).getStructure();
// Traverse the structure looking for nodes to fill out.
ParsedStructure parsedStructure = parseStructure(structure);
// Fetch user data that matches the fields.
UserData userData = fetchUserData(parsedStructure);
// Build the presentation of the datasets
RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");
// Add a dataset to the response
FillResponse fillResponse = new FillResponse.Builder()
.addDataset(new Dataset.Builder()
.setValue(parsedStructure.usernameId,
AutofillValue.forText(userData.username), usernamePresentation)
.setValue(parsedStructure.passwordId,
AutofillValue.forText(userData.password), passwordPresentation)
.build())
.build();
// If there are no errors, call onSuccess() and pass the response
callback.onSuccess(fillResponse);
}
class ParsedStructure {
AutofillId usernameId;
AutofillId passwordId;
}
class UserData {
String username;
String password;
}
As discussed in How to return hashmap in an aidl file thread, I am returning "Map" and not generics map like "Map" or an implementation of Map like a Hashmap. When I check the bound service side output, I get the required key-values in the map, i.e. the map is populated correctly. But the same function at the service side, when called from the client side, returns an empty Map because I initialized my Map in service as:
Map myMap = new HashMap();
Please note that the client service contract is good as the service has other methods which returns boolean, and some of them are void. All of them are working correctly. The problem is just with this method which is returning a Map, and I suppose I am doing some syntactical mistake.
Below is my service side of implementation:
public Map getStatus() {
List<MyObject> MyList = new ArrayList<>();
if (myDaggerInjectedObject != null) {
MyList = myDaggerInjectedObject.getMyList();
}
Map myMap = new HashMap();
for (MyObject myObject : MyList) {
myMap.put(myObject.getScannableId(), myObject.getStatus().name());
}
return myMap;
}
Below is my client code which calls the above bound service method:
public Map getStatus() throws RemoteException {
try {
Map status = myService.getStatus();
// the status object is set to blank hashmap, it should have some data i.e key-value pair
return status;
} catch (RemoteException e) {
Log.e(TAG, "Service connection not available");
throw e;
}
}
Can somebody help pointing out the mistake here?
I did just two things and that resolved this fishy behaviour (Fishy because the same server-client code was working on my dummy client and server android apps but not working on my production client and server apps):
Different AIDL files on server and client: I noticed that the AIDL file on client side was a superset of that on the server side (the interface at the client side had 3 methods whereas at the server side 2 methods)
Cleaning all environments: Did a clean build before rebuilding all my AIDL, Client, Server modules/Components.
Although the first point is not at all logical to me still, but I wanted to jot down in this answer for people to comment, if it COULD be of any issue in this scenario.
I want to implement twitter like functionality in my app as when you logged in for the first time you get the data from the server and show it inside listview but if you again open the app without logging out i do not want the app to request the server again for getting the same data so my question is where to store the data that fetched previously so app wont request the server again and for getting the new data i have implemented the refreshable list view so user will get the new data by refreshing the list.
And one more thing is after refreshing i want to store the new data as well to the same place where previous data was saved and i want to store only the 20 items to prevent the memory overflow . please help someone.
I have an arraylist ArrayList> fetch where i am storing the data while fetching from the server.
The server must be returning the information in JSON or XML format, simply put it in shared preferences and retrieve/show later based on a few internal flags.
For example, here's a sample code to store stuff in shared preferences:
private void writeStrToPreferences(String strKey, String str){
if(strKey == null) return;
if(str == null) return;
if(str.length() <= 0) return;
SharedPreferences.Editor ed = getSharedPreferences(strKey, 0).edit();
ed.putString(strKey, str);
ed.commit();
}
Reading back will also be similar and simple
private String readStrFromPreferences(String strKey){
if(strKey == null)return "NA";
if(strKey.length() <= 0) return "NA";
return getSharedPreferences(strKey, 0).getString(strKey, "NA");
}
For more, check documentation here:
http://developer.android.com/reference/android/content/SharedPreferences.html
I use httpclient along with cookiestore to keep my session, now I want to use the same session on the next activity, I'm using api 8 so I can't use cookiemanager. Is it possible? If only I could somehow send the cookie list through, eg:
Intent i = new Intent(this, Login.class);
i.putExtra("domain", domain);
//need to get the following list across
List<Cookie> cookies = cookieStore.getCookies();
//i.putMyDamnCookies("cookies",cookies);
startActivity(i);
Any idea how I could achieve this?
Yes you can send a List to another activity, but first you'll need to convert it to an instance of ArrayList, or String[] array.
Take a look over this threads:
Passing a List to another Activity in Android
How to put a List in intent
store your List as an array of strings and pass it to in an intent to next activity like this:
String[] cookieArray = new String[cookies.size()];
//copy your List of Strings into the Array
int i=0;
for(Cookie c : cookies ){
cookieArray[i]=c.toString();
i++;
}
//then pass it in your intent
Intent intent = new Intent(this, Login.class);
intent.putExtra("cookieArray", cookieArray);
startActivity(i);
Then in your next actvity, retrieve the array of cookies from the intent and convert the cookies back like so:
List<Cookie> cookies = new List<Cookies>();
for(int i=0;i<cookieArray.size;i++)
{
cookies.add(new HttpCookie(cookieArray[i]));
}
Sure - just read the cookie from the HTTP header, and store it however is convenient for you.
I think this is probably overkill, but here's an example that uses the Apache HTTP Client that ships with Android 2.2:
How do I make an http request using cookies on Android?
Also look here (available since level 1):
http://developer.android.com/reference/org/apache/http/cookie/package-summary.html