Is it possible to set placeholder path params with OkHttp - android

I have an url http://example.com/{x}/push/{y} and I'm using OkHttp curl it.
final HttpUrl httpUrl = HttpUrl
.parse("http://example.com/{x}/push/{y}")
.newBuilder()
???
.build();
Is it possible to set these {x} and {y} path params?
I can see method like addPathSegment which is somehow related, but not what I want.

Here’s one technique that might help you to get started.
HttpUrl template = HttpUrl.parse("http://example.com/{a}/b/{c}");
HttpUrl.Builder builder = template.newBuilder();
for (int i = 0; i < template.pathSegments().size(); i++) {
String parameter = template.pathSegments().get(i);
String replacement = null;
if (parameter.equals("{a}")) {
replacement = "foo";
} else if (parameter.equals("{c}")) {
replacement = "bar";
}
if (replacement != null) {
builder.setPathSegment(i, replacement);
}
}
HttpUrl url = builder.build();

Maybe HttpUrl.setPathSegment(index, value) can make it look a bit better :D

fun HttpUrl.insertPathSegment(index: Int, pathSegment: String): HttpUrl {
val newPathSegments: ArrayList<String> =
encodedPathSegments().fold(ArrayList()) { acc, oldPathSegment ->
printLog("OkHttp", "insertPathSegment oldPathSegment:$oldPathSegment ")
acc.add(oldPathSegment)
acc
}
return newBuilder().apply {
try {
newPathSegments.add(index, pathSegment)
addEncodedPathSegment("")
newPathSegments.forEachIndexed { index, path ->
printLog("OkHttp", "insertPathSegment setEncodedPathSegment:$index $path ")
setEncodedPathSegment(index, path)
//printLog("OkHttp", "insertPathSegment setPathSegment:$index $path ")
//setPathSegment(index, path)
}
} catch (e: Exception) {
e.printStackTrace()
}
}.build()}

I used the following approach to pass path variables:
inputUrl= http://localhost:8080/getResults/firstName/%s/lastName/%s
HttpUrl url = HttpUrl.get(String.format(inputUrl, "fn","ln"));
Request request = new Request.Builder().url(url).build();

Related

KTor URLBuilder encodedPath with dynamic path

I'm trying to build a network module for Multiplatform project with ktor.
My code for GET request is something like this:
val result = httpClient.get<HttpResponse> {
url {
protocol = baseProtocol
host = baseUrl
encodedPath = urlPath
}
}
In some point my path contain a user id like this /users/{user_id}.
I can do a search and replace in string and replace this user_id with actual value, BUT is there any other way to do this? any ktor specific way.
For example with Retrofit we have this:
#GET("users/{user_id}/")
SomeData getUserData(#Path("user_id") String userId);
EDIT: adding more code
val result = httpClient.get<HttpResponse> {
url {
protocol = baseProtocol
host = baseUrl
var requestPath = request.requestPath.value
request.path?.forEach {
requestPath = requestPath.replace(it.first, it.second)
}
encodedPath = requestPath
if (request.parameters != null) {
parameters.appendAll(getParametersFromList(request.parameters))
}
}
the request.path?.forEach { requestPath = requestPath.replace(it.first, it.second)} replacing any runtime path value.

Make HttpLoggingInterceptor do not log images

I'm trying to get rid of trash in logs, like
*�$ʞx���J/
when i recieve an image
So i tried to override HttpLoggingInterceptor intercept(), to detect is there a Content-Type => image/jpeg header in responce, but HttpLoggingInterceptor is final so i cant extend it :(
Code in RetrofitModule:
OkHttpClient provideOkHttpClient(Context context, Application app, Preferences preferences) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.HEADERS);
Cache cache = new Cache(app.getCacheDir(), cacheSize);
return new OkHttpClient.Builder()
.addNetworkInterceptor(new OkHttpInterceptor(context, preferences))
.addInterceptor(loggingInterceptor)
.cache(cache)
.build();
}
How can i disable image-logging in my project?
So, since no one have an answer iv'e invent my own bicycle:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
if(!message.contains("�")){
Timber.d(message);
}
}
});
Not really sure if String.contains() cheap enough for use it like this, but goal is reached
You can use OkHttpLogger class to print logs without binary data:
class OkHttpLogger : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
okHttpLog(message)
}
private fun okHttpLog(message: String, level: Int = Log.DEBUG, t: Throwable? = null) {
val maxLogLength = 4000
val tag = "OkHttp"
val encoder = Charset.forName("ISO-8859-1").newEncoder()
var logMessage = message
if (t != null) logMessage = logMessage + '\n'.toString() + Log.getStackTraceString(t)
// Split by line, then ensure each line can fit into Log's maximum length.
var i = 0
val length = logMessage.length
var isBinaryLogDisplayed = false
var isBinaryContentType = false
while (i < length) {
var newline = logMessage.indexOf('\n', i)
newline = if (newline != -1) newline else length
do {
val end = minOf(newline, i + maxLogLength)
val msg = logMessage.substring(i, end).trim()
if (msg.contains("Content-Type") &&
msg.contains("application/octet-stream")) { // use another Content-Type if need
isBinaryContentType = true
}
val isBinaryData = !encoder.canEncode(msg)
// multipart boundary
if (isBinaryLogDisplayed && msg.startsWith("--")) {
isBinaryContentType = false
isBinaryLogDisplayed = false
}
// don't print binary data
if (isBinaryContentType && isBinaryData && !isBinaryLogDisplayed) {
Log.println(level, tag, "<BINARY DATA>")
isBinaryLogDisplayed = true
}
if (!isBinaryLogDisplayed) {
Log.println(level, tag, msg)
}
i = end
} while (i < newline)
i++
}
}
}
To use it pass an instance to HttpLoggingInterceptor's constructor:
val httpLoggingInterceptor = HttpLoggingInterceptor(OkHttpLogger()).apply {
level = HttpLoggingInterceptor.Level.BODY
}

HTTP Request in Android with Kotlin

I want to do a login validation using POST method and to get some information using GET method.
I've URL, server Username and Password already of my previous project.
For Android, Volley is a good place to get started. For all platforms, you might also want to check out ktor client or http4k which are both good libraries.
However, you can also use standard Java libraries like java.net.HttpURLConnection
which is part of the Java SDK:
fun sendGet() {
val url = URL("http://www.google.com/")
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")
inputStream.bufferedReader().use {
it.lines().forEach { line ->
println(line)
}
}
}
}
Or simpler:
URL("https://google.com").readText()
Send HTTP POST/GET request with parameters using HttpURLConnection :
POST with Parameters:
fun sendPostRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Your API Link>")
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "POST"
val wr = OutputStreamWriter(getOutputStream());
wr.write(reqParam);
wr.flush();
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
println("Response : $response")
}
}
}
GET with Parameters:
fun sendGetRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Yout API Link>?"+reqParam)
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "GET"
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
println("Response : $response")
}
}
}
Using only the standard library with minimal code!
thread {
val json = try {
URL(url).readText()
} catch (e: Exception) {
return#thread
}
runOnUiThread { displayOrWhatever(json) }
}
This starts a GET request on a new thread, leaving the UI thread to respond to user input. However, we can only modify UI elements from the main/UI thread, so we actually need a runOnUiThread block to show the result to our user. This enqueues our display code to be run on the UI thread soon.
The try/catch is there so your app won't crash if you make a request with your phone's internet off. Add your own error handling (e.g. showing a Toast) as you please.
.readText() is not part of the java.net.URL class but a Kotlin extension method, Kotlin "glues" this method onto URL. This is enough for plain GET requests, but for more control and POST requests you need something like the Fuel library.
Have a look at Fuel library, a sample GET request
"https://httpbin.org/get"
.httpGet()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
}
is Result.Success -> {
val data = result.get()
}
}
}
// You can also use Fuel.get("https://httpbin.org/get").responseString { ... }
// You can also use FuelManager.instance.get("...").responseString { ... }
A sample POST request
Fuel.post("https://httpbin.org/post")
.jsonBody("{ \"foo\" : \"bar\" }")
.also { println(it) }
.response { result -> }
Their documentation can be found here
​
I think using okhttp is the easiest solution. Here you can see an example for POST method, sending a json, and with auth.
val url = "https://example.com/endpoint"
val client = OkHttpClient()
val JSON = MediaType.get("application/json; charset=utf-8")
val body = RequestBody.create(JSON, "{\"data\":\"$data\"}")
val request = Request.Builder()
.addHeader("Authorization", "Bearer $token")
.url(url)
.post(body)
.build()
val response = client . newCall (request).execute()
println(response.request())
println(response.body()!!.string())
Remember to add this dependency to your project https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
UPDATE: July 7th, 2019
I'm gonna give two examples using latest Kotlin (1.3.41), OkHttp (4.0.0) and Jackson (2.9.9).
UPDATE: January 25th, 2021
Everything is okay with the most updated versions.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
Get Method
fun get() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users?page=2")
val request = Request.Builder()
.url(url)
.get()
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val mapperAll = ObjectMapper()
val objData = mapperAll.readTree(responseBody)
objData.get("data").forEachIndexed { index, jsonNode ->
println("$index $jsonNode")
}
}
POST Method
fun post() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users")
//just a string
var jsonString = "{\"name\": \"Rolando\", \"job\": \"Fakeador\"}"
//or using jackson
val mapperAll = ObjectMapper()
val jacksonObj = mapperAll.createObjectNode()
jacksonObj.put("name", "Rolando")
jacksonObj.put("job", "Fakeador")
val jacksonString = jacksonObj.toString()
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jacksonString.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val objData = mapperAll.readTree(responseBody)
println("My name is " + objData.get("name").textValue() + ", and I'm a " + objData.get("job").textValue() + ".")
}
Maybe the simplest GET
For everybody stuck with NetworkOnMainThreadException for the other solutions: use AsyncTask or, even shorter, (yet still experimental) Coroutines:
launch {
val jsonStr = URL("url").readText()
}
If you need to test with plain http don't forget to add to your manifest:
android:usesCleartextTraffic="true"
For the experimental Coroutines you have to add to build.gradle as of 10/10/2018:
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0"
...
If you are using Kotlin, you might as well keep your code as succinct as possible. The run method turns the receiver into this and returns the value of the block.
this as HttpURLConnection creates a smart cast. bufferedReader().readText() avoids a bunch of boilerplate code.
return URL(url).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
You can also wrap this into an extension function.
fun URL.getText(): String {
return openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
And call it like this
return URL(url).getText()
Finally, if you are super lazy, you can extend the String class instead.
fun String.getUrlText(): String {
return URL(this).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
}
And call it like this
return "http://somewhere.com".getUrlText()
You can use kohttp library. It is a Kotlin DSL HTTP client. It supports the features of square.okhttp and provides a clear DSL for them. KoHttp async calls are powered by coroutines.
httpGet extension function
val response: Response = "https://google.com/search?q=iphone".httpGet()
you can also use async call with coroutines
val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()
or DSL function for more complex requests
val response: Response = httpGet {
host = "google.com"
path = "/search"
param {
"q" to "iphone"
"safe" to "off"
}
}
You can find more details in docs
To get it with gradle use
implementation 'io.github.rybalkinsd:kohttp:0.12.0'
Without adding additional dependencies, this works. You don't need Volley for this. This works using the current version of Kotlin as of Dec 2018: Kotlin 1.3.10
If using Android Studio, you'll need to add this declaration in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You should manually declare imports here. The auto-import tool caused me many conflicts.:
import android.os.AsyncTask
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection
You can't perform network requests on a background thread. You must subclass AsyncTask.
To call the method:
NetworkTask().execute(requestURL, queryString)
Declaration:
private class NetworkTask : AsyncTask<String, Int, Long>() {
override fun doInBackground(vararg parts: String): Long? {
val requestURL = parts.first()
val queryString = parts.last()
// Set up request
val connection: HttpsURLConnection = URL(requestURL).openConnection() as HttpsURLConnection
// Default is GET so you must override this for post
connection.requestMethod = "POST"
// To send a post body, output must be true
connection.doOutput = true
// Create the stream
val outputStream: OutputStream = connection.outputStream
// Create a writer container to pass the output over the stream
val outputWriter = OutputStreamWriter(outputStream)
// Add the string to the writer container
outputWriter.write(queryString)
// Send the data
outputWriter.flush()
// Create an input stream to read the response
val inputStream = BufferedReader(InputStreamReader(connection.inputStream)).use {
// Container for input stream data
val response = StringBuffer()
var inputLine = it.readLine()
// Add each line to the response container
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
// TODO: Add main thread callback to parse response
println(">>>> Response: $response")
}
connection.disconnect()
return 0
}
protected fun onProgressUpdate(vararg progress: Int) {
}
override fun onPostExecute(result: Long?) {
}
}
GET and POST using OkHttp
private const val CONNECT_TIMEOUT = 15L
private const val READ_TIMEOUT = 15L
private const val WRITE_TIMEOUT = 15L
private fun performPostOperation(urlString: String, jsonString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val body = jsonString.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.post(body)
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
private fun performGetOperation(urlString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.get()
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
Object serialization and deserialization
#Throws(JsonProcessingException::class)
fun objectToJson(obj: Any): String {
return ObjectMapper().writeValueAsString(obj)
}
#Throws(IOException::class)
fun jsonToAgentObject(json: String?): MyObject? {
return if (json == null) { null } else {
ObjectMapper().readValue<MyObject>(json, MyObject::class.java)
}
}
Dependencies
Put the following lines in your gradle (app) file. Jackson is optional. You can use it for object serialization and deserialization.
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
You can use this library Fuel Library as well, which makes it further easier.
val map = mutableMapOf<String, String>()
map.put("id","629eeb9da9d8f50016e1af96")
val httpAsync = url
.httpPost()
.jsonBody(
Gson().toJson(map) // for json string
)
.responseString { request, response, result -> //do something with the response }

Replace query parameters in Uri.Builder in Android?

I'm passing around a Uri.Builder object as a mechanism for subclasses to fill in whatever parameters necessary into a Uri before it is executed in Android.
Problem is, one of the parameters that the base class adds using builder.appendQueryParameter("q",searchPhrase); needs to be replaced in the sub-class, but I can only find appendQueryParameter(), there is no replace or set method. appendQueryParameter() with the same parameter name adds another instance of the parameter, doesn't replace it.
Should I give up and try another way? Or is there a way to replace query parameters that I haven't found yet?
Since there is no in-built method, the best way I have found is to build a new Uri. You iterate over all the query parameters of the old Uri and then replace the desired key with the new value.
private static Uri replaceUriParameter(Uri uri, String key, String newValue) {
final Set<String> params = uri.getQueryParameterNames();
final Uri.Builder newUri = uri.buildUpon().clearQuery();
for (String param : params) {
newUri.appendQueryParameter(param,
param.equals(key) ? newValue : uri.getQueryParameter(param));
}
return newUri.build();
}
This will add a parameter, or replace an existing parameter's value in Kotlin.
Extension of Uri:
fun Uri.addUriParameter(key: String, newValue: String): Uri {
val params = queryParameterNames
val newUri = buildUpon().clearQuery()
var isSameParamPresent = false
for (param in params) {
// if same param is present override it, otherwise add the old param back
newUri.appendQueryParameter(param,
if (param == key) newValue else getQueryParameter(param))
if (param == key) {
// make sure we do not add new param again if already overridden
isSameParamPresent = true
}
}
if (!isSameParamPresent) {
// never overrode same param so add new passed value now
newUri.appendQueryParameter(key,
newValue)
}
return newUri.build()
}
Implementation:
val appendedURL = originalUri.addUriParameter("UID","123456")
Somewhat more concise way of doing what #bmjohns is suggesting.
fun Uri.addUriParameter(key: String, newValue: String): Uri =
with(buildUpon()) {
clearQuery()
queryParameterNames.forEach {
if (it != key) appendQueryParameter(it, getQueryParameter(it))
}
appendQueryParameter(key, newValue)
build()
}
/*
* Append or replace query parameters
*/
fun Uri.Builder.addQueryParameters(uri: Uri, params: Map<String, String>) = apply {
if (uri.query == null) {
appendQueryParameters(params)
} else {
clearQuery()
appendQueryParameters(params)
val names = params.keys
uri.queryParameterNames.forEach {
if (it !in names) appendQueryParameter(it, uri.getQueryParameter(it))
}
}
}
fun Uri.Builder.appendQueryParameters(params: Map<String, String>) = apply {
params.forEach { name, value ->
appendQueryParameter(name, value)
}
}
I have same problem ago, and decide to go another way.
Make an wrapper class and store queries (say setQueryParameter(key, value)) in Map (or ArrayMap, something like that).
Then wrapper instance's build() method processes original Builder's appendQueryParameter() and build()
With UrlQuerySanitizer -
val sanitizer = UrlQuerySanitizer(serviceUrl)
val paramVal = sanitizer.getValue("byCustomValue")
val replacedUrl = serviceUrl.replace(paramVal, "REPLACE_HERE")
Here is a simple method to encode a specific param -
fun encodeParameter(url: String, key: String): String {
val sanitizer = UrlQuerySanitizer(url)
return if (sanitizer.hasParameter(key)) {
val paramValue = sanitizer.getValue(key)
val encodedValue = try {
URLEncoder.encode(paramValue, "utf-8")
} catch (e: UnsupportedEncodingException) {
paramValue
}
url.replace(paramValue, encodedValue)
} else url
}

OAuth Request token = null in android app

i am trying to authenticate something(in this case LinkedIn) using OAuth but the requested token always returns null?
Here is my code below:
public void authenticateAppOauthApi() {
Log.d(TAG, "authenticateAppOauthApi");
OAuthServiceProvider provider = new OAuthServiceProvider(
REQUEST_TOKEN_PATH, AUTHORIZE_PATH, ACCESS_TOKEN_PATH);
OAuthConsumer consumer = new OAuthConsumer(CALLBACK_URL, API_KEY,
SECRET_KEY, provider);
OAuthAccessor accessor = new OAuthAccessor(consumer);
Intent intent = new Intent(Intent.ACTION_VIEW);
Log.d(TAG, "Intent intent = new Intent(Intent.ACTION_VIEW );");
// intent.setData(Uri.parse(url));
String url = accessor.consumer.serviceProvider.userAuthorizationURL
+ "?oauth_token=" + accessor.requestToken + "&oauth_callback="
+ accessor.consumer.callbackURL;
intent.setData(Uri.parse(url));
Log.d(TAG, "intent.setData(Uri.parse(url)); = " + url);
mContext.startActivity(intent);
Log.d(TAG, "finish authenticateApp");
}
I basicaly followed the example here http://donpark.org/blog/2009/01/24/android-client-side-oauth
thanks in advance
you can try this code.
OAuthClient oAuthClient = new OAuthClient(new HttpClient4());
try {
oAuthClient.getRequestToken(accessor);
} catch (IOException e) {
e.printStackTrace();
} catch (OAuthException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
Just a idea, Is it HTTP URL or HTTPS URL ?
I had some problem to access HTTPS URL, browser & app told that certificate was wrong.
Some Root Certificate are not known by Android.
I had some trouble using one of the OAuth libs I found on the net in my Scala Android app. Instead of finding a way to use the lib I just rolled my own... Not sure if it would work against linkedin (it works well with Yammer, which uses HTTPS). Well, below is the relevant code, mind you I'm pretty new to both Android and Scala so there are probably better ways to accomplish this.
The layout file "R.layout.authorization" is very basic contains with two buttons and a text field in a RelativeLayout.
class Authorization extends Activity {
val client = new DefaultHttpClient()
val reqUrl = "https://www.yammer.com/oauth/request_token"
val authUrl = "https://www.yammer.com/oauth/authorize?oauth_token="
val accessUrl = "https://www.yammer.com/oauth/access_token"
override def onCreate(bundle:Bundle) = {
super.onCreate(bundle)
this.setContentView(R.layout.authorization)
val authButton = findViewById(R.id.authButton).asInstanceOf[Button]
val getCodeButton = findViewById(R.id.getCode).asInstanceOf[Button]
val prefs = getSharedPreferences(PreferenceFile(), 0)
if(prefs.contains("oauth_request_token")) {
authButton.setVisibility(View.VISIBLE)
}
setupListeners(authButton, getCodeButton)
}
private def getAuthVerifier() = {
val authVerifierBox:EditText = Authorization.this.findViewById(R.id.authVerifier).asInstanceOf[EditText]
if(authVerifierBox != null && authVerifierBox.getText() != null) {
authVerifierBox.getText().toString()
} else {
""
}
}
private def setupListeners(authButton:Button, getCodeButton:Button) = {
authButton.setOnClickListener(new View.OnClickListener() {
override def onClick(view:View) = {
retrieveAuthTokenAndSecret()
}
})
getCodeButton.setOnClickListener(new View.OnClickListener() {
override def onClick(view:View) = {
try {
// Retrieve a request token with an async task and then start the browser...
// Use of an implicit definition to convert tuple to an async task.
(() => {
// Task to perform
val reqPost = new HttpPost(reqUrl)
reqPost.setHeader("Authorization", OAuthHeaderBuilder(null,null,null))
reqPost.setHeader("Content-Type", "application/x-www-form-urlencoded")
val reqResp= client.execute(reqPost)
reqResp.getEntity()
},
(entity:HttpEntity) => {
// PostExecute handle result from task...
if(entity != null) {
val reader = new BufferedReader(new InputStreamReader(entity.getContent()))
val line = reader.readLine()
val (oauth_request_token, oauth_token_secret) = OAuthTokenExtractor(line)
// Store request tokens so they can be used when retrieving auth tokens...
val editor = getSharedPreferences(PreferenceFile(), 0).edit()
editor.putString("oauth_request_token", oauth_request_token)
editor.putString("oauth_token_secret", oauth_token_secret)
editor.commit()
// Start browser...
val intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl + oauth_request_token))
startActivity(intent)
val authButton = findViewById(R.id.authButton).asInstanceOf[Button]
authButton.setVisibility(View.VISIBLE)
}
}).doInBackground()
} catch {
case e:Exception => Log.e("ERROR", "ERROR IN CODE:"+e.toString())
}
}
})
}
private def retrieveAuthTokenAndSecret() = {
val authVerifier = getAuthVerifier()
val accessPost = new HttpPost(accessUrl)
val prefs = getSharedPreferences(PreferenceFile(), 0)
val token = prefs.getString("oauth_request_token","")
val secret = prefs.getString("oauth_token_secret","")
accessPost.setHeader("Authorization", OAuthHeaderBuilder(token, secret, authVerifier))
accessPost.setHeader("Content-Type", "application/x-www-form-urlencoded")
val accessResp = client.execute(accessPost)
val entity = accessResp.getEntity()
if(entity != null) {
val reader = new BufferedReader(new InputStreamReader(entity.getContent()))
val builder = new StringBuilder()
val line = reader.readLine()
val (oauth_token, oauth_token_secret) = OAuthTokenExtractor(line)
val result = new Intent()
val editor = getSharedPreferences(PreferenceFile(), 0).edit()
editor.putString("oauth_token", oauth_token)
editor.putString("oauth_token_secret", oauth_token_secret)
editor.commit()
setResult(Activity.RESULT_OK, result)
finish()
}
}
}
The OAuthHeaderBuilder is basically a copy of the Yammer oauth sample code:
object OAuthHeaderBuilder {
// Apply function taken from the Yammer oauth sample
def apply(token:String, secret:String,verifier:String):String = {
val buff = new StringBuilder()
val currentTime = System.currentTimeMillis()
// Hardcoded values for consumer key and secret...
val consumerKey = "<your consumer key here>"
val consumerSecret = "<your consumer secret here>"
buff.append("OAuth realm=\"");
buff.append("\", oauth_consumer_key=\"");
buff.append(consumerKey);
buff.append("\", ");
if (token != null) {
buff.append("oauth_token=\"");
buff.append(token);
buff.append("\", ");
}
buff.append("oauth_signature_method=\"");
buff.append("PLAINTEXT");
buff.append("\", oauth_signature=\"");
buff.append(consumerSecret);
buff.append("%26");
if (secret != null) {
buff.append(secret);
}
buff.append("\", oauth_timestamp=\"");
buff.append(currentTime);
buff.append("\", oauth_nonce=\"");
buff.append(currentTime);
if (verifier != null) {
buff.append("\", ");
buff.append("oauth_verifier=\"");
buff.append(verifier);
}
buff.append("\", oauth_version=\"1.0\"");
return buff.toString();
}
}
And inorder to extract the tokens I made a OAuthTokenExtractor object...
object OAuthTokenExtractor {
def apply(line:String) = {
val token = (line split ("&")).find(x => x.startsWith("oauth_token=")) match {
case Some(oauth_token) => (oauth_token split ("=") )(1)
case _ => ""
}
val secret = (line split ("&")).find(x => x.startsWith("oauth_token_secret=")) match {
case Some(oauth_token_secret) => (oauth_token_secret split ("=") )(1)
case _ => ""
}
(token,secret)
}
}
Hope it helps :)
Better use this article as a reference:
There is a code to get authentication URL in current version of Signpost is:
provider.retrieveRequestToken(CALLBACK_URL);
(and be sure to use CommonsHttpOAuthConsumer and CommonsHttpOAuthProvider)

Categories

Resources