diff --git a/README.md b/README.md
index f96b443..9a5b758 100755
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ approach that keeps the details of the OAuth process abstracted from the end-use
This library leverages a few key libraries underneath to power the functionality:
* [scribe-java](https://github.com/scribejava/scribejava) - Simple OAuth library for handling the authentication flow.
- * [Android Async HTTP](https://github.com/loopj/android-async-http) - Simple asynchronous HTTP requests with JSON parsing.
+ * [Android Async HTTP](https://github.com/codepath/asynchttpclient) - Simple asynchronous HTTP requests with JSON parsing.
## Installation
@@ -24,7 +24,7 @@ Next, add this line to your `app/build.gradle` file:
```gradle
dependencies {
- compile 'com.codepath.libraries:android-oauth-handler:1.3.1'
+ compile 'com.codepath.libraries:android-oauth-handler:2.1.3'
}
```
@@ -51,12 +51,12 @@ public class TwitterClient extends OAuthBaseClient {
public TwitterClient(Context context) {
super(context, REST_API_INSTANCE, REST_URL,
- REST_CONSUMER_KEY, REST_CONSUMER_SECRET, REST_CALLBACK_URL);
+ REST_CONSUMER_KEY, REST_CONSUMER_SECRET, null, REST_CALLBACK_URL);
}
// ENDPOINTS BELOW
- public void getHomeTimeline(int page, AsyncHttpResponseHandler handler) {
+ public void getHomeTimeline(int page, JsonHttpResponseHandler handler) {
String apiUrl = getApiUrl("statuses/home_timeline.json");
RequestParams params = new RequestParams();
params.put("page", String.valueOf(page));
@@ -169,9 +169,9 @@ with a `JsonHttpResponseHandler` handler:
// SomeActivity.java
RestClient client = RestClientApp.getRestClient();
client.getHomeTimeline(1, new JsonHttpResponseHandler() {
- public void onSuccess(int statusCode, Header[] headers, JSONArray json) {
+ public void onSuccess(int statusCode, Headers headers, JSON json) {
// Response is automatically parsed into a JSONArray
- // json.getJSONObject(0).getLong("id");
+ // json.jsonArray.getJSONObject(0).getLong("id");
}
});
```
@@ -180,27 +180,18 @@ Based on the JSON response (array or object), you need to declare the expected t
```java
RestClient client = RestClientApp.getRestClient();
-client.get("http://www.google.com", new AsyncHttpResponseHandler() {
+client.get("http://www.google.com", new JsonHttpResponseHandler() {
@Override
- public void onSuccess(int statusCode, Header[] headers, String response) {
+ public void onSuccess(int statusCode, Headers headers, String response) {
System.out.println(response);
}
});
```
-Check out [Android Async HTTP Docs](http://loopj.com/android-async-http/) for more request creation details.
+Check out [Android Async HTTP Docs](https://github.com/codepath/asynchttpclient) for more request creation details.
## Extra Functionality
-### Adding Request Headers
-
-In certain cases, requests will require a particular custom header to be passed through the client. In this case, you can add custom headers to the client that will be added to all requests with:
-
-```java
-RestClient client = RestApplication.getRestClient();
-// Specify the header to append to the request
-client.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1)");
-```
### Access Authorization
@@ -226,40 +217,32 @@ This can be helpful in cases where you must add a flag such as when encountering
You can log out by clearing the access token at any time through the client object:
-You can log out by clearing the access token at any time through the client object:
-
```java
RestClient client = RestApplication.getRestClient();
client.clearAccessToken();
```
-### Enabling a Proxy
+### Debugging
-In order to [troubleshoot API calls](http://guides.codepath.com/android/Troubleshooting-API-calls) using a method such as Charles Proxy, you'll want to enable proxy support with:
+In order to [troubleshoot API calls](http://guides.codepath.com/android/Troubleshooting-API-calls), you can take advantage of the Stetho library:
+Next, initialize Stetho inside your Application object:
```java
-RestClient client = RestApplication.getRestClient();
-client.enableProxy();
+public class MyApplication extends Application {
+ public void onCreate() {
+ super.onCreate();
+ Stetho.initializeWithDefaults(this);
+ }
+}
```
-Proxies are useful for monitoring the network traffic but require a custom SSL certificate to be added to your emulator or device. Because Android API 24 and above now require [explicit control](https://developer.android.com/training/articles/security-config.html) on custom SSL certificates that are used in apps, you will need to allow for added certs to be added by specifying `res/xml/network_security_config.xml` in your app:
-
+Edit the manifest.xml file in your project. To let the Android operating system know that you have a custom Application class, add an attribute called `android:name` to the manifest’s application tag and set the value to the name of your custom Application class.
```xml
-
-
-
-
-
-
-
-
-
+
```
-Inside your AndroidManifest.xml file, make sure to include this `networkSecurityConfig` parameter:
-
-```xml
- klass
return instance;
}
- public OAuthBaseClient(Context c, final BaseApi apiInstance, String consumerUrl, final String consumerKey, final String consumerSecret, String callbackUrl) {
+ public OAuthBaseClient(Context c, final BaseApi apiInstance, String consumerUrl, final String consumerKey, final String consumerSecret, @Nullable String scope, String callbackUrl) {
this.baseUrl = consumerUrl;
this.callbackUrl = callbackUrl;
tokenClient = new OAuthTokenClient(apiInstance, consumerKey,
- consumerSecret, callbackUrl, new OAuthTokenClient.OAuthTokenHandler() {
+ consumerSecret, callbackUrl, scope, new OAuthTokenClient.OAuthTokenHandler() {
// Store request token and launch the authorization URL in the browser
@Override
@@ -60,6 +62,7 @@ public void onReceivedRequestToken(Token requestToken, String authorizeUrl, Stri
OAuth1RequestToken oAuth1RequestToken = (OAuth1RequestToken) requestToken;
editor.putString(OAUTH1_REQUEST_TOKEN, oAuth1RequestToken.getToken());
editor.putString(OAUTH1_REQUEST_TOKEN_SECRET, oAuth1RequestToken.getTokenSecret());
+ editor.putInt(OAuthConstants.VERSION, 1);
editor.commit();
}
}
@@ -86,8 +89,7 @@ public void onReceivedAccessToken(Token accessToken, String oAuthVersion) {
editor.commit();
} else if (oAuthVersion == OAUTH2_VERSION) {
OAuth2AccessToken oAuth2AccessToken = (OAuth2AccessToken) accessToken;
-
- //TODO(rhu) - create client for OAuth2 cases
+ instantiateClient(consumerKey, consumerSecret, oAuth2AccessToken);
tokenClient.setAccessToken(accessToken);
editor.putString(OAuthConstants.TOKEN, oAuth2AccessToken.getAccessToken());
editor.putString(OAuthConstants.SCOPE, oAuth2AccessToken.getScope());
@@ -122,8 +124,10 @@ public void instantiateClient(String consumerKey, String consumerSecret, Token t
if (token instanceof OAuth1AccessToken) {
client = OAuthAsyncHttpClient.create(consumerKey, consumerSecret, (OAuth1AccessToken)(token));
+ } else if (token instanceof OAuth2AccessToken){
+ client = OAuthAsyncHttpClient.create((OAuth2AccessToken) token);
} else {
-
+ throw new IllegalStateException("unrecognized token type" + token);
}
}
@@ -162,10 +166,15 @@ protected OAuthTokenClient getTokenClient() {
return tokenClient;
}
- // Returns the request token stored during the request token phase
- protected OAuth1RequestToken getOAuth1RequestToken() {
- return new OAuth1RequestToken(prefs.getString(OAUTH1_REQUEST_TOKEN, ""),
- prefs.getString(OAUTH1_REQUEST_TOKEN_SECRET, ""));
+ // Returns the request token stored during the request token phase (OAuth1 only)
+ protected @Nullable Token getOAuth1RequestToken() {
+ int oAuthVersion = prefs.getInt(OAuthConstants.VERSION, 0);
+
+ if (oAuthVersion == 1) {
+ return new OAuth1RequestToken(prefs.getString(OAUTH1_REQUEST_TOKEN, ""),
+ prefs.getString(OAUTH1_REQUEST_TOKEN_SECRET, ""));
+ }
+ return null;
}
// Assigns the base url for the API
diff --git a/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java b/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java
index b92c578..d76079d 100755
--- a/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java
+++ b/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java
@@ -30,15 +30,24 @@ public class OAuthTokenClient {
// Requires the apiClass, consumerKey, consumerSecret and callbackUrl along with the TokenHandler
public OAuthTokenClient(BaseApi apiInstance, String consumerKey, String consumerSecret, String callbackUrl,
- OAuthTokenHandler handler) {
+ String scope, OAuthTokenHandler handler) {
this.apiInstance = apiInstance;
this.handler = handler;
if (callbackUrl == null) { callbackUrl = OAuthConstants.OUT_OF_BAND; };
- this.service = new ServiceBuilder()
+ if(scope == null) {
+ this.service = new ServiceBuilder()
.apiKey(consumerKey)
.apiSecret(consumerSecret).callback(callbackUrl)
.httpClientConfig(OkHttpHttpClientConfig.defaultConfig())
.build(apiInstance);
+ } else {
+ this.service = new ServiceBuilder()
+ .apiKey(consumerKey)
+ .apiSecret(consumerSecret).callback(callbackUrl)
+ .httpClientConfig(OkHttpHttpClientConfig.defaultConfig())
+ .scope(scope) // OAuth2 requires scope
+ .build(apiInstance);
+ }
}
// Get a request token and the authorization url
diff --git a/library/src/main/java/com/codepath/utils/GenericsUtil.java b/library/src/main/java/com/codepath/utils/GenericsUtil.java
new file mode 100644
index 0000000..66298f9
--- /dev/null
+++ b/library/src/main/java/com/codepath/utils/GenericsUtil.java
@@ -0,0 +1,80 @@
+package com.codepath.utils;
+
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+@SuppressWarnings("rawtypes")
+public class GenericsUtil {
+ public static List> getTypeArguments(Class baseClass,
+ Class extends T> childClass) {
+ Map resolvedTypes = new HashMap();
+ Type type = childClass;
+ // start walking up the inheritance hierarchy until we hit baseClass
+ while (!getClass(type).equals(baseClass)) {
+ if (type instanceof Class) {
+ type = ((Class) type).getGenericSuperclass();
+ } else {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ assert parameterizedType != null;
+ Class> rawType = (Class) parameterizedType.getRawType();
+
+ Type[] actualTypeArguments = parameterizedType
+ .getActualTypeArguments();
+ TypeVariable>[] typeParameters = rawType.getTypeParameters();
+ for (int i = 0; i < actualTypeArguments.length; i++) {
+ resolvedTypes
+ .put(typeParameters[i], actualTypeArguments[i]);
+ }
+
+ if (!rawType.equals(baseClass)) {
+ type = rawType.getGenericSuperclass();
+ }
+ }
+ }
+
+ // finally, for each actual type argument provided to baseClass,
+ // determine (if possible)
+ // the raw class for that type argument.
+ Type[] actualTypeArguments;
+ if (type instanceof Class) {
+ actualTypeArguments = ((Class) type).getTypeParameters();
+ } else {
+ assert !(type == null);
+ actualTypeArguments = ((ParameterizedType) type)
+ .getActualTypeArguments();
+ }
+ List> typeArgumentsAsClasses = new ArrayList>();
+ // resolve types by chasing down type variables.
+ for (Type baseType : actualTypeArguments) {
+ while (resolvedTypes.containsKey(baseType)) {
+ baseType = resolvedTypes.get(baseType);
+ }
+ typeArgumentsAsClasses.add(getClass(baseType));
+ }
+ return typeArgumentsAsClasses;
+ }
+
+ private static Class> getClass(Type type) {
+ if (type instanceof Class) {
+ return (Class) type;
+ } else if (type instanceof ParameterizedType) {
+ return getClass(((ParameterizedType) type).getRawType());
+ } else if (type instanceof GenericArrayType) {
+ Type componentType = ((GenericArrayType) type)
+ .getGenericComponentType();
+ Class> componentClass = getClass(componentType);
+ if (componentClass != null) {
+ return Array.newInstance(componentClass, 0).getClass();
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+}