Searched a lot.
I have an app. App logins on server and receive some cookies, then it can execute some POST requests with them (e.g. to get user profile). I want to store them between sessions (it means I can restart a device, run app and get profile without extra logging in). Or, in other words, how to create persistent cookie storage?
I'm using the only DefaultHttpClient and there are no WebViews. But I should initialize this client after creation with some cookies. I should store it in file or what? Are there ways to do it in iOS way without weird hacks, storing in files/DBs and manual filling CookieManagers?
Now I'm using a PersistentCookieStorage class for those purposes. I create a singleton when the app is launched. It stores cookies in SharedPreferences.
An implementation of java.net.CookieStore for providing persistent cookies.
Use this https://gist.github.com/manishk3008/2a2373c6c155a5df6326
Storing some data in CookieManager:
void populateCookieStore(URI uri)
throws IOException {
CookieManager cm = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cm);
Map<String,List<String>> header = new HashMap<>();
List<String> values = new ArrayList<>();
values.add("JSESSIONID=" + SESSION_ID + "; version=1; Path="
+ URI_PATH +"; HttpOnly");
values.add("CUSTOMER=WILE_E_COYOTE; version=1; Path=" + URI_PATH);
header.put("Set-Cookie", values);
cm.put(uri, header);
}
Related
I am using Android Web View in my Xamarin Project to perform third party authentication. Once the login is successful I need to extract the authentication cookies. This cookies I am storing in persistent storage and then I am using them for passing to subsequent requests.
For example:
Android App >(opens) webview > Loads (idp provider) url > User provides credentials and saml request is sent to my backend server > backend server validates saml and returns authentication cookies.
It returns two cookies.
Now everything works fine. And in OnPageFinished method of the WebClient of webview I am trying to extract the cookies using the method.
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
var handler = OnPageCompleted;
var uri = new Uri(url);
AllowCookies(view);
var cookies = CookieManager.Instance.GetCookie(url);
var onPageCompletedEventArgs = new OnPageCompletedEventArgs { Cookies = cookies, Url = uri.AbsolutePath, RelativeUrl = uri.PathAndQuery, Host = uri.Host };
handler?.Invoke(this, onPageCompletedEventArgs);
}
private void AllowCookies(WebView view)
{
CookieManager.Instance.Flush();
CookieManager.AllowFileSchemeCookies();
CookieManager.SetAcceptFileSchemeCookies(true);
CookieManager.Instance.AcceptCookie();
CookieManager.Instance.AcceptThirdPartyCookies(view);
CookieManager.Instance.SetAcceptCookie(true);
CookieManager.Instance.SetAcceptThirdPartyCookies(view, true);
}
The problem is, I am able to get just one cookie(wc_cookie_ps_ck
), I am unable to see the other authentication cookie(.AspNetCore.Cookies
).
Here's how the cookies appear in browser.
Please note that in postman and in chrome browser both the cookies appear.
But in android webview only cookie with name ".AspNetCore.Cookies" is not appearing at all.
As per Java document,"When retrieving cookies from the cookie store, CookieManager also enforces the path-match rule from section 3.3.4 of RFC 2965 . So, a cookie must also have its “path” attribute set so that the path-match rule can be applied before the cookie is retrieved from the cookie store."
Since both of my cookies have different path, is that the reason the one with path set as "/project" is not appearing?
After days and days of finding the answer to the question. I finally have found an answer.
I did remote debugging of the webview with the desktop chrome and I found out that all the cookies that I needed were present in the webview.
However the method,
var cookies = CookieManager.Instance.GetCookie(url);
doesn't return the cookie which has the same site variable set.
This looks like a bug from Xamarin Android. I have already raised an issue in Xamarin Android github.
In the xamarin android github issue I have mentioned the steps to reproduce.
For me, the workaround to resolve the issue was to set the samesite cookie varibale off in my asp.net core back end project.
As follows:
In order to configure the application cookie when using Identity, you can use the ConfigureApplicationCookie method inside your Startup’s ConfigureServices:
// add identity
services.AddIdentity<ApplicationUser, IdentityRole>();
// configure the application cookie
services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
});
Link for the above solution mentioned. Here.
In my Android application, I need to open a link in Browser. This page can receive some data just via POST. Could I add these parameters (data) to the intent which start the browser?
Do you know if this is possible? If it is, could you give my a hint?
Use a webview:
WebView webview = new WebView(this);
setContentView(webview);
byte[] post = EncodingUtils.getBytes("postvariable=value&nextvar=value2", "BASE64");
webview.postUrl("http://www.geenie.nl/AnHeli/mobile/ranking/demo/index.php", post);
Intents sent to the browser can contain more than just a URL. In older versions of android it was possible to package extra POST data in the intent, in newer versions that capability is gone but one can send extra header data for a GET (which can be just about anything representable as a string) in the intent delivered to the browser.
try{
String finalUrl = "javascript:" +
"var to = 'http://the_link_you_want_to_open';" +
"var p = {param1:'"+your_param+"',param2:'"+your_param+"'};" +
"var myForm = document.createElement('form');" +
"myForm.method='post' ;" +
"myForm.action = to;" +
"for (var k in p) {" +
"var myInput = document.createElement('input') ;" +
"myInput.setAttribute('type', 'text');" +
"myInput.setAttribute('name', k) ;" +
"myInput.setAttribute('value', p[k]);" +
"myForm.appendChild(myInput) ;" +
"}" +
"document.body.appendChild(myForm) ;" +
"myForm.submit() ;" +
"document.body.removeChild(myForm) ;";
Uri uriUrl = Uri.parse(finalUrl);
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
PackageManager packageManager = this.getPackageManager();
browserIntent.setData(uriUrl);
List<ResolveInfo> list = packageManager.queryIntentActivities(browserIntent, 0);
for (ResolveInfo resolveInfo : list) {
String activityName = resolveInfo.activityInfo.name;
if (activityName.contains("BrowserActivity")) {
browserIntent =
packageManager.getLaunchIntentForPackage(resolveInfo.activityInfo.packageName);
ComponentName comp =
new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
browserIntent.setAction(Intent.ACTION_VIEW);
browserIntent.addCategory(Intent.CATEGORY_BROWSABLE);
browserIntent.setComponent(comp);
browserIntent.setData(uriUrl);
}
}
this.startActivity(browserIntent);
}catch (Exception e){
e.printStackTrace();
txtHeader.setText(e.toString());
}
I believe that there is a little misconception in the question. What is missing is the purpose that you need of the POST instead of GET.
If you admit I will make few assumptions that could be common in this context:
You need to hide the actual variables from history
You Need some interaction with server before user gets control
You cannot control the server itself and it uses (on purpose) POST requests
Any of those options or requirements implies some additional processing that is distinct from usual browser use case (which is to give full control over the processing and interaction). It seems that you are actually asking for Machine to Machine (M2M) communication with eventual HTML output.
If that renders to be true, then using some OKHttp, HTTPURLConnection, Apache HTTP Client, etc. is the right choice. Rather then invoking the browser via Intent, that has close to zero messaging capabilities (just fire and forget - in case of http:...). It actually requires some analysis of the data flow (sequence diagram might help) and then engineering of that process into M2M or assisted M2M interaction.
If the server you are using to interact with is your own, then why you do not create some REST/JSON/SOAP or other M2M API to make remote method calls (RPC/RMI/...). It is not that complex as it might look (e.g.: http://coreymaynard.com/blog/creating-a-restful-api-with-php/ or https://docs.phalconphp.com/pt/latest/reference/tutorial-rest.html)
Alternative would be to make your M2M interaction rather on your APP server, because then the eventual changes to the BE server data flow could be reflected without app change. By this you would be actually shifting the M2M communication partially to server side.
Note: Using application to interact with 3rd party servers might have some legal implications. In fact those server might not allow other use than through browser (human detection = captcha, User-Agent detection). In such case you have to negotiate with the server owner.
I am working on an app, which requires-
Pull contact details and images from the local contact book.
Some interval apart sync this contact data (all of it) to a server.
Pull contact data (images as well) from the server whenever needed.
I basically know how to implement them individually. For example, I have already managed to pull local contacts, I am yet to achieve 2 and 3. I have few questions regarding them.
Where do I save the images (both local and networked)? Do I need to save them in any particular folder? If yes then what is the recommended way of doing that?
I have used volley library in another project, and I am hoping to use it again here. AFAIK, volley caches networked images in the memory. But I believe that in my app, there can be users who will have more than 2000 contact data. My intuition is that not all the images will remain in the cache for ever, so if I want my app to work offline, I will need to images to be stored locally. I am confused about where to store the images and how to achieve that. Point to note, this app will be accessed frequently.
What is the recommended way of sending image data over the network to a server.
The questions may seem broad, but I feel that they are tightly coupled, considering a single app. I am expecting expert opinion on the recommended ways of achieving these features.
Thanks!
You typically save them either to your internal folder or to the SD card in your directory. The internal data folder will be locked to your app (unless the phone is rooted) and inacessible by other apps, the sd card will be only on 4.3 and higher. Either way you should manage the amount of data cached, set a limit and not allow it to go higher than that (kicking them out in some matter, most likely LRU or LFU). YOu'll need to do that by hand or find a library to do it for you, its not built into Android.
As for downloading them from the server- typically its just an HTTP request, with a webservice that will do any necessary privacy checking before sending down either an image result or an error. You don't want to do anything like JSON or the like here, it will just waste bandwidth.
There is no "recommended directory for images". The decision is up to you. But you must always remember that memory on user's device is finite and on some handsets even extra few megabytes are inappropriate expenditure. The docs are pretty clear.
Saving ~2000 photos on user's device is not a good idea. But again it's up to you. The Volley library is for general https interaction but not for images downloading, encoding and caching. Picasso is aimed to work with images: loads images from network or by Uri, has cache size settings and many other features.
It depends on your server. The most common way is just POST http request or multipart request if you need to send some additional data.
About third question, if you're going to keep using Volley, you can try to override the getBody() to return the image's bytes, rest of other parameters should be encoding within the URL, this way would use both of GET and POST method.
public class ContactRequest extends StringRequest {
public static String buildRequestUrl(String url,
Map<String, String> params, String paramsEncoding) {
StringBuilder urlBud = new StringBuilder(url).append('?');
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
urlBud.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
urlBud.append('=');
urlBud.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
urlBud.append('&');
}
return urlBud.toString();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding);
}
}
private String imageFilePath;
public ContactRequest(String url, String imageFilePath,
Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, listener, errorListener);
this.imageFilePath = imageFilePath;
}
#Override
public byte[] getBody() throws AuthFailureError {
return getBytesFromFile(new File(imageFilePath));
}
}
build the ContactRequest and serve to RequestQueue like this :
String originUrl = "http://.../contact_push.do";
String imageFilePath = "/sdcard/.../contact_avatar_path";
Map<String, String> params = new HashMap<String, String>();
params.put("firstName", "Vince");
params.put("lastName", "Styling");
new ContactRequest(
ContactRequest.buildRequestUrl(originUrl, params, HTTP.UTF_8),
imageFilePath, null, null);
'cause I never faced this problem before, so I don't sure this Request can reach to the server correctly, it's an un-test solution for me, hope can help.
I'm using a Videoview to play http video.That Http video url needs Authentication.
So please let me know how authentication can be set to the VideoView?If not is there any other alternative for viewing authenticated video.?
Thanks & Regards,
Sree Harsha .
There is a hidden method in VideoView that allows setting HTTP headers. You can use reflection to access it. But it will only help if the server supports basic authentication
Method setVideoURIMethod = videoView.getClass().getMethod("setVideoURI", Uri.class, Map.class);
Map<String, String> params = new HashMap<String, String>(1);
final String cred = login + ":" + pwd;
final String auth = "Basic " + Base64.encodeBytes(cred.getBytes("UTF-8"));
params.put("Authorization", auth);
setVideoURIMethod.invoke(videoView, uri, params);
Of course since this is undocumented API it is not guaranteed to work properly, you should handle exceptions and have a fallback plan.
First you should know what kind of authentication is required by this server: http://unixpapa.com/auth/index.html
Second, depending of auth type, you should provide auth parameters (username/password) inside the URL. Usually this will be accepted (but not necessatilly, you should test): http://username:password#www.yourhostname.com/whatever
This are basic, http-style authentications. Modern sites use other options such as OpenID and OAuth. This are a bit harder to implement.
Anyhow, you should know type type of authentication, before you start looking for solution.
The reflection API access to the setViewUri(Uri, Map) worked for me.
I guess a safer alternative is to use the MediaPlayer.setDataSource(Context, Uri, Map):
A risk of using the reflection API is that the API might disappear in a future release...
In my Android application, I need to open a link in Browser. This page can receive some data just via POST. Could I add these parameters (data) to the intent which start the browser?
Do you know if this is possible? If it is, could you give my a hint?
Use a webview:
WebView webview = new WebView(this);
setContentView(webview);
byte[] post = EncodingUtils.getBytes("postvariable=value&nextvar=value2", "BASE64");
webview.postUrl("http://www.geenie.nl/AnHeli/mobile/ranking/demo/index.php", post);
Intents sent to the browser can contain more than just a URL. In older versions of android it was possible to package extra POST data in the intent, in newer versions that capability is gone but one can send extra header data for a GET (which can be just about anything representable as a string) in the intent delivered to the browser.
try{
String finalUrl = "javascript:" +
"var to = 'http://the_link_you_want_to_open';" +
"var p = {param1:'"+your_param+"',param2:'"+your_param+"'};" +
"var myForm = document.createElement('form');" +
"myForm.method='post' ;" +
"myForm.action = to;" +
"for (var k in p) {" +
"var myInput = document.createElement('input') ;" +
"myInput.setAttribute('type', 'text');" +
"myInput.setAttribute('name', k) ;" +
"myInput.setAttribute('value', p[k]);" +
"myForm.appendChild(myInput) ;" +
"}" +
"document.body.appendChild(myForm) ;" +
"myForm.submit() ;" +
"document.body.removeChild(myForm) ;";
Uri uriUrl = Uri.parse(finalUrl);
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
PackageManager packageManager = this.getPackageManager();
browserIntent.setData(uriUrl);
List<ResolveInfo> list = packageManager.queryIntentActivities(browserIntent, 0);
for (ResolveInfo resolveInfo : list) {
String activityName = resolveInfo.activityInfo.name;
if (activityName.contains("BrowserActivity")) {
browserIntent =
packageManager.getLaunchIntentForPackage(resolveInfo.activityInfo.packageName);
ComponentName comp =
new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
browserIntent.setAction(Intent.ACTION_VIEW);
browserIntent.addCategory(Intent.CATEGORY_BROWSABLE);
browserIntent.setComponent(comp);
browserIntent.setData(uriUrl);
}
}
this.startActivity(browserIntent);
}catch (Exception e){
e.printStackTrace();
txtHeader.setText(e.toString());
}
I believe that there is a little misconception in the question. What is missing is the purpose that you need of the POST instead of GET.
If you admit I will make few assumptions that could be common in this context:
You need to hide the actual variables from history
You Need some interaction with server before user gets control
You cannot control the server itself and it uses (on purpose) POST requests
Any of those options or requirements implies some additional processing that is distinct from usual browser use case (which is to give full control over the processing and interaction). It seems that you are actually asking for Machine to Machine (M2M) communication with eventual HTML output.
If that renders to be true, then using some OKHttp, HTTPURLConnection, Apache HTTP Client, etc. is the right choice. Rather then invoking the browser via Intent, that has close to zero messaging capabilities (just fire and forget - in case of http:...). It actually requires some analysis of the data flow (sequence diagram might help) and then engineering of that process into M2M or assisted M2M interaction.
If the server you are using to interact with is your own, then why you do not create some REST/JSON/SOAP or other M2M API to make remote method calls (RPC/RMI/...). It is not that complex as it might look (e.g.: http://coreymaynard.com/blog/creating-a-restful-api-with-php/ or https://docs.phalconphp.com/pt/latest/reference/tutorial-rest.html)
Alternative would be to make your M2M interaction rather on your APP server, because then the eventual changes to the BE server data flow could be reflected without app change. By this you would be actually shifting the M2M communication partially to server side.
Note: Using application to interact with 3rd party servers might have some legal implications. In fact those server might not allow other use than through browser (human detection = captcha, User-Agent detection). In such case you have to negotiate with the server owner.