I have a strange behaviour when I click on a link in a webview that is a PDF file link.
For example: https://my.server.com/foobar.pdf
So I have made some research and this link will start a dowload in my webview.
I have a DownloadListener and a onDownloadStart method.
In it I send the URL to Android so that PDF apps on phone can open it.
My strange behaviour is here. If the link in the webview does not have parameters I am not able to add parameters in the URL but if the URL have one parameter -> my parameters are added.
Example will be more meaningful.
Here url in link is "https://my.server.com/foobar.pdf"
val uriTest = Uri.parse(url).buildUpon()
.appendQueryParameter("key1", val1)
.appendQueryParameter("key2", val2)
.build()
val intent = Intent(Intent.ACTION_VIEW, uriTest)
startActivity(intent)
So when the PDF app called by startActivity(intent) call the URL on my server I have no parameters in URL and on my server I see a call to "https://my.server.com/foobar.pdf" not to "https://my.server.com/foobar.pdf?key1=val1&key2=val2".
BUT
If the url in link is "https://my.server.com/foobar.pdf?t=t" when my Android code is executed on my server side I can see a call to ""https://my.server.com/foobar.pdf?t=t&key1=val1&key2=val2". My parameters are added in this case.
Is it normal? Am I missing something?
Thanks for your help!
---EDIT---
I also tried to add my parameters in the string directly and it is the same -> my parameters are ignored until the URL I get has one parameter.
Example: I get "https://my.server.com/foobar.pdf" and I do:
val url1 = url + "?key1=" + val1
or
val url1 = "$url?key1=$val1"
val yourUrl = StringBuilder("https://my.server.com/foobar.pdf")
val parameters = hashMapOf<String, String>()
parameters["key1"] = "val1"
parameters["key2"] = "val2"
var count = 0
for (i in parameters.entries) {
if (count == 0)
yourUrl.append("?${i.key}=${i.value}&")
else
yourUrl.append("${i.key}=${i.value}&")
count++
}
val yourNewUrl = yourUrl.substring(0, yourUrl.length - 1)
Timber.e("URL: $yourNewUrl")
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(yourNewUrl))
startActivity(intent)
Happy coding :)
I have seen many examples in Delphi and not one a C ++ builder. I tried to recreate the code in C ++, but it flies only exception. How to use the Intent to С++?
void __fastcall TForm1::Button1Click(TObject *Sender)
{
callEmail("russia#gmail.com", "Application");
}
//---------------------------------------------------------------------------
void TForm1::callEmail(const String address, const String Subject){
JIntent* intent;
TJIntent* intentTwo;
intent = intentTwo->Create();
intent->setAction(intentTwo->JavaClass->ACTION_SEND);
intent->setFlags(intentTwo->JavaClass->FLAG_ACTIVITY_NEW_TASK);
intent->putExtra(intentTwo->JavaClass->EXTRA_EMAIL, StringToJString(address));
intent->putExtra(intentTwo->JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString('vnd.android.cursor.dir/email'));
SharedActivity()->startActivity(intent);
}
I thnik, maybe I think maybe something needs to change in androidmanifest or user-permission?
Your code is crashing because you are not constructing the Intent object correctly.
Create() is a constructor in Delphi. intent := TJIntent.Create in Delphi would be intent = new TJIntent in C++.
Also, Embarcadero uses interfaces for its iOS/Android bridge frameworks, so you should use the provided DelphiInterface<T> typedefs, such as _di_JIntent instead of JIntent* directly.
Also, JavaClass (and OCClass in iOS) is a static class property. You do not need an object instance to access it, just the class type.
Also, C++ uses single-quotes for character literals and double-quotes for string literals, whereas Delphi uses single-quotes for both. 'vnd.android.cursor.dir/email' in C++ is not a string literal, it is a multi-byte character literal instead, which is not what you want here. Use double-quotes instead.
Also, EXTRA_EMAIL must be expressed as an array of strings.
Try something more like this:
void TForm1::callEmail(const String address, const String Subject)
{
_di_JIntent intent;
intent = new TJIntent; // or: intent = TJIntent::JavaClass->init();
intent->setAction(TJIntent::JavaClass->ACTION_SEND);
// or: intent = TJIntent::JavaClass->init(TJIntent::JavaClass->ACTION_SEND);
intent->setFlags(TJIntent::JavaClass->FLAG_ACTIVITY_NEW_TASK);
TJavaObjectArray__1<_di_JString> *Recipients = new TJavaObjectArray__1<_di_JString>(1);
Recipients->Items[0] = StringToJString(address);
intent->putExtra(TJIntent::JavaClass->EXTRA_EMAIL, Recipients);
intent->putExtra(TJIntent::JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString(L"vnd.android.cursor.dir/email"));
SharedActivity()->startActivity(intent);
}
Now, that said, you really should not be using vnd.android.cursor.dir/email as the intent's MIME type. Use message/rfc822 instead, or even plain/text. But those do not limit the intent to just email clients, other apps might also support those types. To send an email using only a true email client, use ACTION_SENDTO with a mailto: URI instead. For 1 recipient, you can put the address directly in the URI and not use EXTRA_EMAIL at all. For 2+ recipients, use a mailto: URI with no address in it and use EXTRA_EMAIL for the addresses. This is mentioned in the Android documentation:
Common Intents | Email
For example:
void TForm1::callEmail(const String address, const String Subject)
{
_di_JIntent intent;
intent = new TJIntent; // or: intent = TJIntent::JavaClass->init();
intent->setAction(TJIntent::JavaClass->ACTION_SENDTO);
intent->setData(StrToJURI(L"mailto:" + address));
// or: intent = TJIntent::JavaClass->init(TJIntent::JavaClass->ACTION_SENDTO, StrToJURI(L"mailto:" + address));
intent->setFlags(TJIntent::JavaClass->FLAG_ACTIVITY_NEW_TASK);
intent->putExtra(TJIntent::JavaClass->EXTRA_SUBJECT, StringToJString(Subject));
intent->setType(StringToJString(L"message/rfc822"));
SharedActivity()->startActivity(intent);
}
I'm developing an Android app. I need to build a URI for my app to make an API request. Unless there's another way to put a variable in a URI, this is the easiest way I've found. I found that you need to use Uri.Builder, but I'm not quite sure how to. My url is:
http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=[redacted]&mapid=value
My scheme is http, authority is lapi.transitchicago.com, path is /api/1.0, path segment(s) is ttarrivals.aspx, and query string is key=[redacted]&mapid=value.
My code is below:
Intent intent = getIntent();
String value = intent.getExtras().getString("value");
Uri.Builder builder = new Uri.Builder();
builder.scheme("http")
.authority("www.lapi.transitchicago.com")
.appendPath("api")
.appendPath("1.0")
.appendPath("ttarrivals.aspx")
.appendQueryParameter("key", "[redacted]")
.appendQueryParameter("mapid", value);
I understand that I can do URI.add, but how do I integrate it into the Uri.Builder? Should I add everything like URI.add(scheme), URI.add(authority) and so on? Or is that not the way to do it? Also, is there any other easier way to add a variable to a URI/URL?
Let's say that I want to create the following URL:
https://www.myawesomesite.com/turtles/types?type=1&sort=relevance#section-name
To build this with the Uri.Builder I would do the following.
Uri.Builder builder = new Uri.Builder();
builder.scheme("https")
.authority("www.myawesomesite.com")
.appendPath("turtles")
.appendPath("types")
.appendQueryParameter("type", "1")
.appendQueryParameter("sort", "relevance")
.fragment("section-name");
String myUrl = builder.build().toString();
There is another way of using Uri and we can achieve the same goal
http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7
To build the Uri you can use this:
final String FORECAST_BASE_URL =
"http://api.example.org/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
You can declare all this the above way or even inside the Uri.parse() and appendQueryParameter()
Uri builtUri = Uri.parse(FORECAST_BASE_URL)
.buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(FORMAT_PARAM, "json")
.appendQueryParameter(UNITS_PARAM, "metric")
.appendQueryParameter(DAYS_PARAM, Integer.toString(7))
.build();
At last
URL url = new URL(builtUri.toString());
Source: Udacity Android course / Sunshine app
Excellent answer from above turned into a simple utility method.
private Uri buildURI(String url, Map<String, String> params) {
// build url with parameters.
Uri.Builder builder = Uri.parse(url).buildUpon();
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.appendQueryParameter(entry.getKey(), entry.getValue());
}
return builder.build();
}
here is a good way to explain it:
there are two forms of the URI
1 - Builder(ready to be modified, not ready to be used)
2 - Built(not ready to be modified, ready to be used )
You can create a builder by
Uri.Builder builder = new Uri.Builder();
this gonna return a Builder ready to be modified like this:-
builder.scheme("https");
builder.authority("api.github.com");
builder.appendPath("search");
builder.appendPath("repositories");
builder.appendQueryParameter(PARAMETER_QUERY,parameterValue);
but to use it you have to build it first
retrun builder.build();
or however you gonna use it.
and then you have built that is already built for you, ready to use but cannot be modified.
Uri built = Uri.parse("your URI goes here");
this is ready to use but if you want to modify it you need to buildUpon()
Uri built = Uri.parse("Your URI goes here")
.buildUpon(); //now it's ready to be modified
.buildUpon()
.appendQueryParameter(QUERY_PARAMATER, parameterValue)
//any modification you want to make goes here
.build(); // you have to build it back cause you are storing it
// as Uri not Uri.builder
now every time you want to modify it you need to buildUpon() and in the end build().
so Uri.Builder is a Builder type that store a Builder in it.
Uri is a Built type that store an already built URI in it.
new Uri.Builder(); rerurns a Builder.
Uri.parse("your URI goes here") returns a Built.
and with build() you can change it from Builder to Built.
buildUpon() you can change it from Built to Builder.
Here is what you can do
Uri.Builder builder = Uri.parse("URL").buildUpon();
// here you created a builder, made an already built URI with Uri.parse
// and then change it to builder with buildUpon();
Uri built = builder.build();
//when you want to change your URI, change Builder
//when you want to use your URI, use Built
and also the opposite:-
Uri built = new Uri.Builder().build();
// here you created a reference to a built URI
// made a builder with new Uri.Builder() and then change it to a built with
// built();
Uri.Builder builder = built.buildUpon();
hope my answer helped :) <3
for the example in the second Answer I used this technique for the same URL
http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7
Uri.Builder builder = new Uri.Builder();
builder.scheme("https")
.authority("api.openweathermap.org")
.appendPath("data")
.appendPath("2.5")
.appendPath("forecast")
.appendPath("daily")
.appendQueryParameter("q", params[0])
.appendQueryParameter("mode", "json")
.appendQueryParameter("units", "metric")
.appendQueryParameter("cnt", "7")
.appendQueryParameter("APPID", BuildConfig.OPEN_WEATHER_MAP_API_KEY);
then after finish building it get it as URL like this
URL url = new URL(builder.build().toString());
and open a connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
and if link is simple like location uri, for example
geo:0,0?q=29203
Uri geoLocation = Uri.parse("geo:0,0?").buildUpon()
.appendQueryParameter("q",29203).build();
Using appendEncodePath() could save you multiple lines than appendPath(), the following code snippet builds up this url: http://api.openweathermap.org/data/2.5/forecast/daily?zip=94043
Uri.Builder urlBuilder = new Uri.Builder();
urlBuilder.scheme("http");
urlBuilder.authority("api.openweathermap.org");
urlBuilder.appendEncodedPath("data/2.5/forecast/daily");
urlBuilder.appendQueryParameter("zip", "94043,us");
URL url = new URL(urlBuilder.build().toString());
Best answer: https://stackoverflow.com/a/19168199/413127
Example for
http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7
Now with Kotlin
val myUrl = Uri.Builder().apply {
scheme("https")
authority("www.myawesomesite.com")
appendPath("turtles")
appendPath("types")
appendQueryParameter("type", "1")
appendQueryParameter("sort", "relevance")
fragment("section-name")
build()
}.toString()
You can do that with lambda expressions;
private static final String BASE_URL = "http://api.example.org/data/2.5/forecast/daily";
private String getBaseUrl(Map<String, String> params) {
final Uri.Builder builder = Uri.parse(BASE_URL).buildUpon();
params.entrySet().forEach(entry -> builder.appendQueryParameter(entry.getKey(), entry.getValue()));
return builder.build().toString();
}
and you can create params like that;
Map<String, String> params = new HashMap<String, String>();
params.put("zip", "94043,us");
params.put("units", "metric");
Btw. If you will face any issue like “lambda expressions not supported at this language level”, please check this URL;
https://stackoverflow.com/a/22704620/2057154
In my Android project I'm using Robospice with spring-android. Which works fine for all REST communication. But for the below request query parameter "=" is getting converted to "&". Because of this the request is getting failed.
Query String: tags=["keywords:default=hello"]
By checking the logs the request is converted as below for making call by the library.
http://XXX/rest/media/search?token=123&tags=%5B%22keywords:default&hello%22%5D
here "=" sign is converted to "&" in "keywords:default=hello"
Request Class
here tags = String.format("[\"keywords:default=%s\"]", mTag);
#Override
public MVMediaSearch loadDataFromNetwork() throws Exception
{
String search="";
if(!tags.equals(Constants.EMPTY_DATA))
search="&tags="+tags;
return getRestTemplate().getForObject( Constants.BASE_URL+"/media/search?token="+token+search, MVMediaSearch.class );
}
If I fire the URL in a browser, I'm getting error. And if I change the '&' sign to its corresponding url encoded value in browser, it works fine.
I also have the same issue.
For alternative, I use getForObject(java.net.URI, java.lang.Class).
URI uri = new URI(Constants.BASE_URL+"/media/search?token="+token+search);
getRestTemplate().getForObject(uri, MVMediaSearch.class );
http://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject(java.net.URI, java.lang.Class)
You can do something like this:
URI uri = new URI(
"http",
Constants.BASE_URL,
"/media/search?token=",
token,
search,
null);
String request = uri.toASCIIString();
take a look at THIS and see if you understand (you have to adapt to your code - this is not completely done for you)
I am using the following code (from sdk examples "Hackbook") to add a tag to a photo after uploading it.
json = Util.parseJson(response);
photo_id = json.getString("id");
String relativePath = photo_id + "/tags/" + Utility.userUID;
Bundle params = new Bundle();
params.putString("x", "25");
params.putString("y", "25");
Utility.mAsyncRunner.request(relativePath, params, "POST", new TagPhotoRequestListener(), null);
However, I sometimes (not always) receive the the response from facebook as "false" instead of "true". Is there a specific reason for this?
Additionally, is there a way to tag the photo while uploading it, instead of making an additional call?