├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
├── lint.xml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── codepath
│ │ ├── oauth
│ │ ├── OAuthAsyncHttpClient.java
│ │ ├── OAuthBaseClient.java
│ │ ├── OAuthLoginActionBarActivity.java
│ │ ├── OAuthLoginActivity.java
│ │ ├── OAuthLoginFragment.java
│ │ └── OAuthTokenClient.java
│ │ └── utils
│ │ └── GenericsUtil.java
│ └── res
│ ├── .gitkeep
│ └── layout
│ └── .gitkeep
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Source: https://gist.github.com/nesquena/5617544/raw/53710b374e7df3302df43b552488d876040ada3d/.gitignore
2 |
3 | # built application files
4 | *.apk
5 | *.ap_
6 |
7 | # files for the dex VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # generated files
14 | bin/
15 | gen/
16 |
17 | # Local configuration file (sdk path, etc)
18 | local.properties
19 |
20 | # Eclipse project files
21 | .classpath
22 | .project
23 |
24 | # Proguard folder generated by Eclipse
25 | proguard/
26 |
27 | # Intellij project files
28 | *.iml
29 | *.ipr
30 | *.iws
31 | .idea/
32 |
33 | *.pydevproject
34 | .project
35 | .metadata
36 | bin/**
37 | tmp/**
38 | tmp/**/*
39 | *.tmp
40 | *.bak
41 | *.swp
42 | *~.nib
43 | local.properties
44 | .classpath
45 | .settings/
46 | .loadpath
47 |
48 | # External tool builders
49 | .externalToolBuilders/
50 |
51 | # Locally stored "Eclipse launch configurations"
52 | *.launch
53 |
54 | # CDT-specific
55 | .cproject
56 |
57 | # PDT-specific
58 | .buildpath
59 |
60 | # Android Studio project files
61 | *.iml
62 | .gradle
63 | .idea
64 | build
65 | import-summary.txt
66 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk:
3 | - oraclejdk8
4 | sudo: false
5 | android:
6 | components:
7 | - tools
8 | - platform-tools
9 | - build-tools-28.0.3
10 | - android-28
11 | before_install:
12 | - yes | sdkmanager "platforms;android-28"
13 | script:
14 | - "./gradlew build check --daemon"
15 | after_failure: "cat $TRAVIS_BUILD_DIR/app/build/outputs/lint-results.xml"
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 CodePath
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodePath OAuth Handler [](https://travis-ci.org/codepath/android-oauth-handler)
2 |
3 | This library is an Android library for managing OAuth requests with an extremely easy
4 | approach that keeps the details of the OAuth process abstracted from the end-user developer.
5 |
6 | This library leverages a few key libraries underneath to power the functionality:
7 |
8 | * [scribe-java](https://github.com/scribejava/scribejava) - Simple OAuth library for handling the authentication flow.
9 | * [Android Async HTTP](https://github.com/codepath/asynchttpclient) - Simple asynchronous HTTP requests with JSON parsing.
10 |
11 | ## Installation
12 |
13 | Inside your project's root `build.gradle`, make sure the jCenter repository is added:
14 |
15 | ```gradle
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | }
20 | }
21 | ```
22 |
23 | Next, add this line to your `app/build.gradle` file:
24 |
25 | ```gradle
26 | dependencies {
27 | compile 'com.codepath.libraries:android-oauth-handler:2.1.3'
28 | }
29 | ```
30 |
31 | If you want an easier way to get setup with this library, try downloading the
32 | [android-rest-client-template](https://github.com/thecodepath/android-rest-client-template/archive/master.zip)
33 | instead and using that as the template for your project.
34 |
35 | ## Getting Started
36 |
37 | This library is very simple to use and simply requires you to create an Activity that is used for authenticating with OAuth and ultimately give your application access to an authenticated API.
38 |
39 | ### Creating a REST Client
40 |
41 | The first step is to create a REST Client that will be used to access the authenticated APIs
42 | within your application. A REST Client is defined in the structure below:
43 |
44 | ```java
45 | public class TwitterClient extends OAuthBaseClient {
46 | public static final BaseApi REST_API_INSTANCE = TwitterApi.instance();
47 | public static final String REST_URL = "https://api.twitter.com/1.1";
48 | public static final String REST_CONSUMER_KEY = "SOME_KEY_HERE";
49 | public static final String REST_CONSUMER_SECRET = "SOME_SECRET_HERE";
50 | public static final String REST_CALLBACK_URL = "oauth://arbitraryname.com";
51 |
52 | public TwitterClient(Context context) {
53 | super(context, REST_API_INSTANCE, REST_URL,
54 | REST_CONSUMER_KEY, REST_CONSUMER_SECRET, null, REST_CALLBACK_URL);
55 | }
56 |
57 | // ENDPOINTS BELOW
58 |
59 | public void getHomeTimeline(int page, JsonHttpResponseHandler handler) {
60 | String apiUrl = getApiUrl("statuses/home_timeline.json");
61 | RequestParams params = new RequestParams();
62 | params.put("page", String.valueOf(page));
63 | client.get(apiUrl, params, handler);
64 | }
65 | }
66 | ```
67 |
68 | Configure the `REST_API_INSTANCE`, `REST_URL`, `REST_CONSUMER_KEY`, `REST_CONSUMER_SECRET` based on the values needed to connect to your particular API. The `REST_URL` should be the base URL used for connecting to the API (i.e `https://api.twitter.com`). The `REST_API_INSTANCE` should be the instance defining the service you wish to connect to. Check out the [full list of services](https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/main/java/com/github/scribejava/apis) you can select (i.e `FlickrApi.instance()`).
69 |
70 | Make sure that the project's `AndroidManifest.xml` has the appropriate `intent-filter` tags that correspond
71 | with the `REST_CALLBACK_URL` defined in the client:
72 |
73 | ```xml
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
85 |
86 |
87 | ```
88 |
89 | If the manifest does not have a matching `intent-filter` then the OAuth flow will not work.
90 |
91 | #### Customizations for Twitter
92 |
93 | The above instructions cover most OAuth integrations. When using this template to build a Twitter integration, you will need to make a few changes.
94 |
95 | In `AndroidManifest.xml`, use the OAuth scheme name `x-oauthflow-twitter`:
96 |
97 | ```xml
98 |
99 |
100 |
104 |
105 |
106 | ```
107 |
108 | In `TwitterClient.java`, the value for `REST_CALLBACK_URL` must also use the `x-oauthflow-twitter` protocol.
109 |
110 | ```java
111 | public static final String REST_CALLBACK_URL = "x-oauthflow-twitter://arbitraryname.com";
112 | ```
113 |
114 | Note that the `arbitraryname.com` value can be any string. If you leave the setting unchecked for callback locking in your Twitter developer settings, then you can use any placeholder value. The callback host value in `AndroidManifest.xml` must correspond to the part after the `://` in `REST_CALLBACK_URL`, but it need not match the setting in your Twitter developer settings page.
115 |
116 | ### Creating a LoginActivity
117 |
118 | The next step to add support for authenticating with a service is to create a `LoginActivity` which is responsible for the task:
119 |
120 | ```java
121 | public class LoginActivity extends OAuthLoginActivity {
122 | // This fires once the user is authenticated, or fires immediately
123 | // if the user is already authenticated.
124 |
125 | @Override
126 | protected void onCreate(Bundle savedInstanceState) {
127 | super.onCreate(savedInstanceState);
128 | setContentView(R.layout.activity_login);
129 | }
130 |
131 | @Override
132 | public void onLoginSuccess() {
133 | Intent i = new Intent(this, PhotosActivity.class);
134 | startActivity(i);
135 | }
136 |
137 | // Fires if the authentication process fails for any reason.
138 | @Override
139 | public void onLoginFailure(Exception e) {
140 | e.printStackTrace();
141 | }
142 |
143 | // Method to be called to begin the authentication process
144 | // assuming user is not authenticated.
145 | // Typically used as an event listener for a button for the user to press.
146 | public void loginToRest(View view) {
147 | getClient().connect();
148 | }
149 | }
150 | ```
151 |
152 | A few notes for your `LoginActivity`:
153 |
154 | * Your activity must extend from `OAuthLoginActivity`
155 | * Your activity must implement `onLoginSuccess` and `onLoginFailure`
156 | * The `onLoginSuccess` should launch an "authenticated" activity.
157 | * The activity should have a button or other view a user can press to trigger authentication
158 | * Authentication is initiated by invoking `getClient().connect()` within the LoginActivity.
159 |
160 | In more advanced cases where you want to authenticate **multiple services from a single activity**, check out the related
161 | [guide for using OAuthLoginFragment](https://github.com/thecodepath/android-oauth-handler/wiki/Advanced-Use-with-OAuthLoginFragment).
162 |
163 | ### Using the REST Client
164 |
165 | These endpoint methods will automatically execute asynchronous requests signed with the authenticated access token anywhere your application. To use JSON endpoints, simply invoke the method
166 | with a `JsonHttpResponseHandler` handler:
167 |
168 | ```java
169 | // SomeActivity.java
170 | RestClient client = RestClientApp.getRestClient();
171 | client.getHomeTimeline(1, new JsonHttpResponseHandler() {
172 | public void onSuccess(int statusCode, Headers headers, JSON json) {
173 | // Response is automatically parsed into a JSONArray
174 | // json.jsonArray.getJSONObject(0).getLong("id");
175 | }
176 | });
177 | ```
178 |
179 | Based on the JSON response (array or object), you need to declare the expected type inside the `onSuccess` signature i.e `public void onSuccess(int statusCode, Header[] headers, JSONObject json)`. If the endpoint does not return JSON, then you can use the `AsyncHttpResponseHandler`:
180 |
181 | ```java
182 | RestClient client = RestClientApp.getRestClient();
183 | client.get("http://www.google.com", new JsonHttpResponseHandler() {
184 | @Override
185 | public void onSuccess(int statusCode, Headers headers, String response) {
186 | System.out.println(response);
187 | }
188 | });
189 | ```
190 |
191 | Check out [Android Async HTTP Docs](https://github.com/codepath/asynchttpclient) for more request creation details.
192 |
193 | ## Extra Functionality
194 |
195 |
196 | ### Access Authorization
197 |
198 | Once the request token has been received, an access token is granted by redirecting to the device's browser to allow the user to grant permission on the API provider's web address. The browser is opened using an implicit intent with no intent flags specified:
199 |
200 | ```java
201 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authorizeUrl));
202 | ```
203 |
204 | However, specifying [intent flags](http://guides.codepath.com/android/Navigation-and-Task-Stacks#configuring-intent-flags) to alter that behavior can be added to the message using the following:
205 |
206 | ```java
207 | RestClient client = RestApplication.getRestClient();
208 | // Specify the intent flags as desired
209 | client.setRequestIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
210 | // Trigger authorization
211 | client.connect();
212 | ```
213 |
214 | This can be helpful in cases where you must add a flag such as when encountering the `android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?` error.
215 |
216 | ### Logging Out
217 |
218 | You can log out by clearing the access token at any time through the client object:
219 |
220 | ```java
221 | RestClient client = RestApplication.getRestClient();
222 | client.clearAccessToken();
223 | ```
224 |
225 | ### Debugging
226 |
227 | In order to [troubleshoot API calls](http://guides.codepath.com/android/Troubleshooting-API-calls), you can take advantage of the Stetho library:
228 |
229 | Next, initialize Stetho inside your Application object:
230 | ```java
231 | public class MyApplication extends Application {
232 | public void onCreate() {
233 | super.onCreate();
234 | Stetho.initializeWithDefaults(this);
235 | }
236 | }
237 | ```
238 |
239 | 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.
240 | ```xml
241 |
246 | ```
247 |
248 | You can then use `chrome://inspect`, pick the app currently running, and click on the Network tab to view. See [this guide](https://github.com/codepath/android_guides/wiki/Debugging-with-Stetho) for more context.
249 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 | /*
19 | allprojects { thisproject ->
20 | bintray {
21 | if (thisproject == rootProject) {
22 | configurations = []
23 | } else {
24 | configurations = ['archives']
25 | }
26 | }
27 | }*/
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codepath/android-oauth-handler/c6a1c4cc12a9c7597d28b702193a6698dd5a0d72/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Aug 11 22:16:42 PDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'maven-publish'
3 | apply plugin: 'signing'
4 |
5 | ext {
6 |
7 | GROUP = 'com.codepath.libraries'
8 | BASE_VERSION = "2.3"
9 | VERSION_NAME = "2.3.0"
10 | POM_PACKAGING = "aar"
11 | POM_DESCRIPTION = "CodePath OAuth Handler"
12 |
13 | POM_ARTIFACT_ID = "android-oauth-handler"
14 | POM_NAME = "CodePath OAuth Handler"
15 | POM_URL = "https://github.com/codepath/android-oauth-handler/"
16 | POM_SCM_URL = "https://github.com/codepath/android-oauth-handler/"
17 | POM_SCM_CONNECTION = "scm:git:https://github.com/codepath/android-oauth-handler.git"
18 | POM_SCM_DEV_CONNECTION = "scm:git:git@github.com:codepath/android-oauth-handler.git"
19 |
20 | POM_LICENCE_NAME = "The Apache Software License, Version 2.0"
21 | POM_LICENCE_URL = "http://www.apache.org/licenses/LICENSE-2.0.txt"
22 | POM_LICENCE_DIST = "repo"
23 |
24 | POM_DEVELOPER_ID = "codepath"
25 | POM_DEVELOPER_NAME = "CodePath, Inc."
26 | }
27 |
28 | version = VERSION_NAME
29 | group = GROUP
30 | archivesBaseName = POM_ARTIFACT_ID
31 |
32 | android {
33 | compileSdkVersion 30
34 |
35 | defaultConfig {
36 | versionCode 1
37 | versionName VERSION_NAME
38 | minSdkVersion 21
39 | targetSdkVersion 30
40 | }
41 |
42 | // Related to https://github.com/scribejava/scribejava/issues/480
43 | // Scribe expects Java 7 or this custom Apache library
44 | lintOptions {
45 | lintConfig rootProject.file('gradle/lint.xml')
46 | }
47 | buildTypes {
48 | release {
49 | minifyEnabled false
50 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
51 | }
52 | }
53 | }
54 |
55 | afterEvaluate {
56 | publishing {
57 | publications {
58 | // Creates a Maven publication called "release".
59 | release(MavenPublication) {
60 | // Applies the component for the release build variant.
61 | from components.release
62 |
63 | // You can then customize attributes of the publication as shown below.
64 | groupId = GROUP
65 | artifactId = POM_ARTIFACT_ID
66 | version = VERSION_NAME
67 |
68 | pom {
69 | name = POM_NAME
70 | url = POM_URL
71 | description = POM_DESCRIPTION
72 | licenses {
73 | license {
74 | name = POM_LICENCE_NAME
75 | url = POM_LICENCE_URL
76 | }
77 | }
78 | developers {
79 | developer {
80 | id = POM_DEVELOPER_ID
81 | name = POM_DEVELOPER_NAME
82 | }
83 | }
84 | scm {
85 | url = POM_SCM_URL
86 | }
87 | }
88 | }
89 | }
90 | repositories {
91 | maven {
92 | name = "Sonatype"
93 | credentials {
94 | username = System.getenv('NEXUS_USERNAME')
95 | password = System.getenv('NEXUS_PASSWORD')
96 | }
97 | def releasesRepoUrl = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
98 | def snapshotsRepoUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
99 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
100 | setUrl(url)
101 | }
102 | }
103 | }
104 | }
105 |
106 | signing {
107 | // gpg on MacOS is the same as gpg2
108 | // ln -s /usr/local/bin/gpg /usr/local/bin/gpg2
109 | // Make sure to populate the variables in gradle.properties
110 | // signing.gnupg.keyName=XXX
111 | // signing.gnupg.passpharse
112 | useGpgCmd()
113 | sign(publishing.publications)
114 | }
115 |
116 | task sourcesJar(type: Jar) {
117 | from android.sourceSets.main.java.srcDirs
118 | archiveClassifier.set("sources")
119 | }
120 |
121 | artifacts {
122 | archives sourcesJar
123 | }
124 |
125 | ext {
126 | supportVersion = '28.0.0'
127 | }
128 |
129 | dependencies {
130 | api "androidx.appcompat:appcompat:1.3.0"
131 | api 'com.codepath.libraries:asynchttpclient:2.1.1'
132 | api 'com.github.scribejava:scribejava-apis:4.1.1'
133 | api 'com.github.scribejava:scribejava-httpclient-okhttp:4.1.1'
134 | implementation 'se.akerfeldt:okhttp-signpost:1.1.0'
135 | implementation 'oauth.signpost:signpost-core:1.2.1.2'
136 | implementation 'com.facebook.stetho:stetho:1.5.1'
137 | implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
138 | implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"
139 |
140 | }
141 |
142 | task jar(type: Jar) {
143 | from android.sourceSets.main.java.srcDirs
144 | }
145 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import com.codepath.asynchttpclient.AsyncHttpClient;
4 | import com.facebook.stetho.okhttp3.StethoInterceptor;
5 | import com.github.scribejava.core.model.OAuth1AccessToken;
6 | import com.github.scribejava.core.model.OAuth2AccessToken;
7 |
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.io.IOException;
11 |
12 | import okhttp3.Interceptor;
13 | import okhttp3.OkHttpClient;
14 | import okhttp3.Request;
15 | import okhttp3.Response;
16 | import okhttp3.logging.HttpLoggingInterceptor;
17 | import se.akerfeldt.okhttp.signpost.OkHttpOAuthConsumer;
18 | import se.akerfeldt.okhttp.signpost.SigningInterceptor;
19 |
20 | public class OAuthAsyncHttpClient extends AsyncHttpClient {
21 |
22 | protected OAuthAsyncHttpClient(OkHttpClient httpClient) {
23 | super(httpClient);
24 | }
25 |
26 | private static String BEARER = "Bearer";
27 |
28 | public static HttpLoggingInterceptor createLogger() {
29 | HttpLoggingInterceptor logger = new HttpLoggingInterceptor();
30 | logger.level(HttpLoggingInterceptor.Level.HEADERS);
31 | return logger;
32 | }
33 |
34 | public static OAuthAsyncHttpClient create(String consumerKey, String consumerSecret, OAuth1AccessToken token) {
35 | OkHttpOAuthConsumer consumer = new OkHttpOAuthConsumer(consumerKey, consumerSecret);
36 | HttpLoggingInterceptor logging = createLogger();
37 |
38 | consumer.setTokenWithSecret(token.getToken(), token.getTokenSecret());
39 | OkHttpClient httpClient = new OkHttpClient.Builder()
40 | .addInterceptor(logging)
41 | .addNetworkInterceptor(new StethoInterceptor())
42 | .addInterceptor(new SigningInterceptor(consumer)).build();
43 |
44 | OAuthAsyncHttpClient asyncHttpClient = new OAuthAsyncHttpClient(httpClient);
45 | return asyncHttpClient;
46 | }
47 |
48 | public static OAuthAsyncHttpClient create(final OAuth2AccessToken token) {
49 | final String bearer = String.format("%s %s", BEARER, token.getAccessToken());
50 |
51 | HttpLoggingInterceptor logging = createLogger();
52 |
53 | OkHttpClient httpClient = new OkHttpClient.Builder()
54 | .addInterceptor(logging)
55 | .addNetworkInterceptor(new StethoInterceptor())
56 | .addInterceptor(new Interceptor() {
57 | @NotNull
58 | @Override
59 | public Response intercept(@NotNull Chain chain) throws IOException {
60 | Request originalRequest = chain.request();
61 | Request authedRequest = originalRequest.newBuilder().header("Authorization", bearer).build();
62 | return chain.proceed(authedRequest);
63 | }
64 | }).build();
65 |
66 | OAuthAsyncHttpClient asyncHttpClient = new OAuthAsyncHttpClient(httpClient);
67 | return asyncHttpClient;
68 | }
69 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthBaseClient.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.net.Uri;
7 |
8 | import androidx.annotation.Nullable;
9 |
10 | import com.github.scribejava.core.builder.api.BaseApi;
11 | import com.github.scribejava.core.model.OAuth1AccessToken;
12 | import com.github.scribejava.core.model.OAuth1RequestToken;
13 | import com.github.scribejava.core.model.OAuth2AccessToken;
14 | import com.github.scribejava.core.model.OAuthConstants;
15 | import com.github.scribejava.core.model.Token;
16 |
17 | import java.util.HashMap;
18 |
19 | public abstract class OAuthBaseClient {
20 | protected String baseUrl;
21 | protected Context context;
22 | protected OAuthTokenClient tokenClient;
23 | protected OAuthAsyncHttpClient client;
24 | protected SharedPreferences prefs;
25 | protected SharedPreferences.Editor editor;
26 | protected OAuthAccessHandler accessHandler;
27 | protected String callbackUrl;
28 | protected int requestIntentFlags = -1;
29 |
30 | private static final String OAUTH1_REQUEST_TOKEN = "request_token";
31 | private static final String OAUTH1_REQUEST_TOKEN_SECRET = "request_token_secret";
32 | private static final String OAUTH1_VERSION = "1.0";
33 | private static final String OAUTH2_VERSION = "2.0";
34 |
35 | protected static HashMap, OAuthBaseClient> instances =
36 | new HashMap, OAuthBaseClient>();
37 |
38 | public static OAuthBaseClient getInstance(Class extends OAuthBaseClient> klass, Context context) {
39 | OAuthBaseClient instance = instances.get(klass);
40 | if (instance == null) {
41 | try {
42 | instance = (OAuthBaseClient) klass.getConstructor(Context.class).newInstance(context);
43 | instances.put(klass, instance);
44 | } catch (Exception e) {
45 | e.printStackTrace();
46 | }
47 | }
48 | return instance;
49 | }
50 |
51 | public OAuthBaseClient(Context c, final BaseApi apiInstance, String consumerUrl, final String consumerKey, final String consumerSecret, @Nullable String scope, String callbackUrl) {
52 | this.baseUrl = consumerUrl;
53 | this.callbackUrl = callbackUrl;
54 | tokenClient = new OAuthTokenClient(apiInstance, consumerKey,
55 | consumerSecret, callbackUrl, scope, new OAuthTokenClient.OAuthTokenHandler() {
56 |
57 | // Store request token and launch the authorization URL in the browser
58 | @Override
59 | public void onReceivedRequestToken(Token requestToken, String authorizeUrl, String oAuthVersion) {
60 | if (requestToken != null) {
61 | if (oAuthVersion == OAUTH1_VERSION) { // store for OAuth1.0a
62 | OAuth1RequestToken oAuth1RequestToken = (OAuth1RequestToken) requestToken;
63 | editor.putString(OAUTH1_REQUEST_TOKEN, oAuth1RequestToken.getToken());
64 | editor.putString(OAUTH1_REQUEST_TOKEN_SECRET, oAuth1RequestToken.getTokenSecret());
65 | editor.putInt(OAuthConstants.VERSION, 1);
66 | editor.commit();
67 | }
68 | }
69 | // Launch the authorization URL in the browser
70 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authorizeUrl));
71 | if (requestIntentFlags != -1) {
72 | intent.setFlags(requestIntentFlags);
73 | }
74 | OAuthBaseClient.this.context.startActivity(intent);
75 | }
76 |
77 | // Store the access token in preferences, set the token in the tokenClient and fire the success callback
78 | @Override
79 | public void onReceivedAccessToken(Token accessToken, String oAuthVersion) {
80 |
81 | if (oAuthVersion == OAUTH1_VERSION) {
82 | OAuth1AccessToken oAuth1AccessToken = (OAuth1AccessToken) accessToken;
83 |
84 | tokenClient.setAccessToken(accessToken);
85 | instantiateClient(consumerKey, consumerSecret, oAuth1AccessToken);
86 | editor.putString(OAuthConstants.TOKEN, oAuth1AccessToken.getToken());
87 | editor.putString(OAuthConstants.TOKEN_SECRET, oAuth1AccessToken.getTokenSecret());
88 | editor.putInt(OAuthConstants.VERSION, 1);
89 | editor.commit();
90 | } else if (oAuthVersion == OAUTH2_VERSION) {
91 | OAuth2AccessToken oAuth2AccessToken = (OAuth2AccessToken) accessToken;
92 | instantiateClient(consumerKey, consumerSecret, oAuth2AccessToken);
93 | tokenClient.setAccessToken(accessToken);
94 | editor.putString(OAuthConstants.TOKEN, oAuth2AccessToken.getAccessToken());
95 | editor.putString(OAuthConstants.SCOPE, oAuth2AccessToken.getScope());
96 | editor.putString(OAuthConstants.REFRESH_TOKEN, oAuth2AccessToken.getRefreshToken());
97 | editor.putInt(OAuthConstants.VERSION, 2);
98 | editor.commit();
99 |
100 | }
101 | accessHandler.onLoginSuccess();
102 | }
103 |
104 | @Override
105 | public void onFailure(Exception e) {
106 | accessHandler.onLoginFailure(e);
107 | }
108 |
109 | });
110 |
111 | this.context = c;
112 | // Store preferences namespaced by the class and consumer key used
113 | this.prefs = this.context.getSharedPreferences("OAuth_" + apiInstance.getClass().getSimpleName() + "_" + consumerKey, 0);
114 | this.editor = this.prefs.edit();
115 | // Set access token in the tokenClient if already stored in preferences
116 | Token accessToken = this.checkAccessToken();
117 | if (accessToken != null) {
118 | tokenClient.setAccessToken(accessToken);
119 | instantiateClient(consumerKey, consumerSecret, accessToken);
120 | }
121 | }
122 |
123 | public void instantiateClient(String consumerKey, String consumerSecret, Token token) {
124 |
125 | if (token instanceof OAuth1AccessToken) {
126 | client = OAuthAsyncHttpClient.create(consumerKey, consumerSecret, (OAuth1AccessToken)(token));
127 | } else if (token instanceof OAuth2AccessToken){
128 | client = OAuthAsyncHttpClient.create((OAuth2AccessToken) token);
129 | } else {
130 | throw new IllegalStateException("unrecognized token type" + token);
131 | }
132 |
133 | }
134 | // Fetches a request token and retrieve and authorization url
135 | // Should open a browser in onReceivedRequestToken once the url has been received
136 | public void connect() {
137 | tokenClient.fetchRequestToken();
138 | }
139 |
140 | // Retrieves access token given authorization url
141 | public void authorize(Uri uri, OAuthAccessHandler handler) {
142 | this.accessHandler = handler;
143 | if (checkAccessToken() == null && uri != null) {
144 | // TODO: check UriServiceCallback with intent:// scheme
145 | tokenClient.fetchAccessToken(getOAuth1RequestToken(), uri);
146 |
147 | } else if (checkAccessToken() != null) { // already have access token
148 | this.accessHandler.onLoginSuccess();
149 | }
150 | }
151 |
152 | // Return access token if the token exists in preferences
153 | public Token checkAccessToken() {
154 | int oAuthVersion = prefs.getInt(OAuthConstants.VERSION, 0);
155 |
156 | if (oAuthVersion == 1 && prefs.contains(OAuthConstants.TOKEN) && prefs.contains(OAuthConstants.TOKEN_SECRET)) {
157 | return new OAuth1AccessToken(prefs.getString(OAuthConstants.TOKEN, ""),
158 | prefs.getString(OAuthConstants.TOKEN_SECRET, ""));
159 | } else if (oAuthVersion == 2 && prefs.contains(OAuthConstants.TOKEN)) {
160 | return new OAuth2AccessToken(prefs.getString(OAuthConstants.TOKEN, ""));
161 | }
162 | return null;
163 | }
164 |
165 | protected OAuthTokenClient getTokenClient() {
166 | return tokenClient;
167 | }
168 |
169 | // Returns the request token stored during the request token phase (OAuth1 only)
170 | protected @Nullable Token getOAuth1RequestToken() {
171 | int oAuthVersion = prefs.getInt(OAuthConstants.VERSION, 0);
172 |
173 | if (oAuthVersion == 1) {
174 | return new OAuth1RequestToken(prefs.getString(OAUTH1_REQUEST_TOKEN, ""),
175 | prefs.getString(OAUTH1_REQUEST_TOKEN_SECRET, ""));
176 | }
177 | return null;
178 | }
179 |
180 | // Assigns the base url for the API
181 | protected void setBaseUrl(String url) {
182 | this.baseUrl = url;
183 | }
184 |
185 | // Returns the full ApiUrl
186 | protected String getApiUrl(String path) {
187 | return this.baseUrl + "/" + path;
188 | }
189 |
190 | // Removes the access tokens (for signing out)
191 | public void clearAccessToken() {
192 | tokenClient.setAccessToken(null);
193 | editor.remove(OAuthConstants.TOKEN);
194 | editor.remove(OAuthConstants.TOKEN_SECRET);
195 | editor.remove(OAuthConstants.REFRESH_TOKEN);
196 | editor.remove(OAuthConstants.SCOPE);
197 | editor.commit();
198 | }
199 |
200 | // Returns true if the tokenClient is authenticated; false otherwise.
201 | public boolean isAuthenticated() {
202 | return tokenClient.getAccessToken() != null;
203 | }
204 |
205 | // Sets the flags used when launching browser to authenticate through OAuth
206 | public void setRequestIntentFlags(int flags) {
207 | this.requestIntentFlags = flags;
208 | }
209 |
210 | // Defines the handler events for the OAuth flow
211 | public static interface OAuthAccessHandler {
212 | public void onLoginSuccess();
213 |
214 | public void onLoginFailure(Exception e);
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 |
6 | import androidx.appcompat.app.AppCompatActivity;
7 |
8 | import com.codepath.utils.GenericsUtil;
9 |
10 |
11 | // This is the ActionBarActivity supportv7 version of LoginActivity
12 | public abstract class OAuthLoginActionBarActivity extends
13 | AppCompatActivity
14 | implements OAuthBaseClient.OAuthAccessHandler {
15 |
16 | private T client;
17 |
18 | // Use this to properly assign the new intent with callback code
19 | // for activities with a "singleTask" launch mode
20 | @Override
21 | protected void onNewIntent(Intent intent) {
22 | super.onNewIntent(intent);
23 | setIntent(intent);
24 | }
25 |
26 | // Extract the uri data and call authorize to retrieve access token
27 | // This is why after the browser redirects to the app, authentication is completed
28 | @SuppressWarnings("unchecked")
29 | @Override
30 | protected void onResume() {
31 | super.onResume();
32 | Class clientClass = getClientClass();
33 | // Extracts the authenticated url data after the user
34 | // authorizes the OAuth app in the browser
35 | Uri uri = getIntent().getData();
36 |
37 | try {
38 | client = (T) OAuthBaseClient.getInstance(clientClass, this);
39 | client.authorize(uri, this); // fetch access token (if needed)
40 | } catch (Exception e) {
41 | e.printStackTrace();
42 | }
43 | }
44 |
45 | public T getClient() {
46 | return client;
47 | }
48 |
49 | @SuppressWarnings("unchecked")
50 | private Class getClientClass() {
51 | return (Class) GenericsUtil.getTypeArguments(OAuthLoginActionBarActivity.class, this.getClass()).get(0);
52 | }
53 | }
54 |
55 | /*
56 | * 1) Subclass OAuthBaseClient like TwitterClient
57 | * 2) Subclass OAuthLoginActivity
58 | * 3) Invoke .login
59 | * 4) Optionally override
60 | * a) onLoginSuccess
61 | * b) onLoginFailure(Exception e)
62 | * 5) In other activities that need the client
63 | * a) c = TwitterClient.getSharedClient()
64 | * b) c.getTimeline(...)
65 | * 6) Modify AndroidManifest.xml to add an IntentFilter w/ the callback URL
66 | * defined in the OAuthBaseClient.
67 | */
68 |
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthLoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 |
6 | import androidx.fragment.app.FragmentActivity;
7 |
8 | import com.codepath.utils.GenericsUtil;
9 |
10 | //This is the FragmentActivity supportv4 version of LoginActivity
11 | public abstract class OAuthLoginActivity extends FragmentActivity
12 | implements OAuthBaseClient.OAuthAccessHandler {
13 |
14 | private T client;
15 |
16 | // Use this to properly assign the new intent with callback code
17 | // for activities with a "singleTask" launch mode
18 | @Override
19 | protected void onNewIntent(Intent intent) {
20 | super.onNewIntent(intent);
21 | setIntent(intent);
22 | }
23 |
24 | // Extract the uri data and call authorize to retrieve access token
25 | // This is why after the browser redirects to the app, authentication is completed
26 | @SuppressWarnings("unchecked")
27 | @Override
28 | protected void onResume() {
29 | super.onResume();
30 | Class clientClass = getClientClass();
31 | // Extracts the authenticated url data after the user
32 | // authorizes the OAuth app in the browser
33 | Uri uri = getIntent().getData();
34 |
35 | try {
36 | client = (T) OAuthBaseClient.getInstance(clientClass, this);
37 | client.authorize(uri, this); // fetch access token (if needed)
38 | } catch (Exception e) {
39 | e.printStackTrace();
40 | }
41 | }
42 |
43 | public T getClient() {
44 | return client;
45 | }
46 |
47 | @SuppressWarnings("unchecked")
48 | private Class getClientClass() {
49 | return (Class) GenericsUtil.getTypeArguments(OAuthLoginActivity.class, this.getClass()).get(0);
50 | }
51 | }
52 |
53 | /*
54 | * 1) Subclass OAuthBaseClient like TwitterClient
55 | * 2) Subclass OAuthLoginActivity
56 | * 3) Invoke .login
57 | * 4) Optionally override
58 | * a) onLoginSuccess
59 | * b) onLoginFailure(Exception e)
60 | * 5) In other activities that need the client
61 | * a) c = TwitterClient.getSharedClient()
62 | * b) c.getTimeline(...)
63 | * 6) Modify AndroidManifest.xml to add an IntentFilter w/ the callback URL
64 | * defined in the OAuthBaseClient.
65 | */
66 |
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthLoginFragment.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import android.net.Uri;
4 | import android.os.Bundle;
5 |
6 | import androidx.fragment.app.Fragment;
7 |
8 | import com.codepath.utils.GenericsUtil;
9 |
10 | public abstract class OAuthLoginFragment extends Fragment implements
11 | OAuthBaseClient.OAuthAccessHandler {
12 |
13 | private T client;
14 |
15 | @SuppressWarnings("unchecked")
16 | @Override
17 | public void onActivityCreated(Bundle saved) {
18 | super.onActivityCreated(saved);
19 |
20 | // Fetch the uri that was passed in (which exists if this is being returned from authorization flow)
21 | Uri uri = getActivity().getIntent().getData();
22 | // Fetch the client class this fragment is responsible for.
23 | Class clientClass = getClientClass();
24 |
25 | try {
26 | client = (T) OAuthBaseClient.getInstance(clientClass, getActivity());
27 | client.authorize(uri, this); // fetch access token (if not stored)
28 | } catch (Exception e) {
29 | e.printStackTrace();
30 | }
31 | }
32 |
33 | public T getClient() {
34 | return client;
35 | }
36 |
37 | @SuppressWarnings("unchecked")
38 | private Class getClientClass() {
39 | return (Class) GenericsUtil.getTypeArguments(OAuthLoginFragment.class, this.getClass()).get(0);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java:
--------------------------------------------------------------------------------
1 | package com.codepath.oauth;
2 |
3 | import android.net.Uri;
4 |
5 | import com.github.scribejava.core.builder.ServiceBuilder;
6 | import com.github.scribejava.core.builder.api.BaseApi;
7 | import com.github.scribejava.core.exceptions.OAuthException;
8 | import com.github.scribejava.core.model.OAuth1AccessToken;
9 | import com.github.scribejava.core.model.OAuth1RequestToken;
10 | import com.github.scribejava.core.model.OAuth2AccessToken;
11 | import com.github.scribejava.core.model.OAuthAsyncRequestCallback;
12 | import com.github.scribejava.core.model.OAuthConstants;
13 | import com.github.scribejava.core.model.Token;
14 | import com.github.scribejava.core.oauth.OAuth10aService;
15 | import com.github.scribejava.core.oauth.OAuth20Service;
16 | import com.github.scribejava.core.oauth.OAuthService;
17 | import com.github.scribejava.httpclient.okhttp.OkHttpHttpClientConfig;
18 |
19 | /*
20 | * OAuthTokenClient is responsible for managing the request and access token exchanges and then
21 | * signing all requests with the OAuth signature after access token has been retrieved and stored.
22 | * The client is based on AsyncHttpClient for async http requests and uses Scribe to manage the OAuth authentication.
23 | */
24 | public class OAuthTokenClient {
25 |
26 | private BaseApi apiInstance;
27 | private OAuthTokenHandler handler;
28 | private Token accessToken;
29 | private OAuthService service;
30 |
31 | // Requires the apiClass, consumerKey, consumerSecret and callbackUrl along with the TokenHandler
32 | public OAuthTokenClient(BaseApi apiInstance, String consumerKey, String consumerSecret, String callbackUrl,
33 | String scope, OAuthTokenHandler handler) {
34 | this.apiInstance = apiInstance;
35 | this.handler = handler;
36 | if (callbackUrl == null) { callbackUrl = OAuthConstants.OUT_OF_BAND; };
37 | if(scope == null) {
38 | this.service = new ServiceBuilder()
39 | .apiKey(consumerKey)
40 | .apiSecret(consumerSecret).callback(callbackUrl)
41 | .httpClientConfig(OkHttpHttpClientConfig.defaultConfig())
42 | .build(apiInstance);
43 | } else {
44 | this.service = new ServiceBuilder()
45 | .apiKey(consumerKey)
46 | .apiSecret(consumerSecret).callback(callbackUrl)
47 | .httpClientConfig(OkHttpHttpClientConfig.defaultConfig())
48 | .scope(scope) // OAuth2 requires scope
49 | .build(apiInstance);
50 | }
51 | }
52 |
53 | // Get a request token and the authorization url
54 | // Once fetched, fire the onReceivedRequestToken for the request token handler
55 | // Works for both OAuth1.0a and OAuth2
56 | public void fetchRequestToken() {
57 | if (service.getVersion() == "1.0") {
58 | final OAuth10aService oAuth10aService = (OAuth10aService) service;
59 | oAuth10aService.getRequestTokenAsync(new OAuthAsyncRequestCallback() {
60 | @Override
61 | public void onCompleted(OAuth1RequestToken requestToken) {
62 | String authorizeUrl = oAuth10aService.getAuthorizationUrl((OAuth1RequestToken) requestToken);
63 | handler.onReceivedRequestToken(requestToken, authorizeUrl, service.getVersion());
64 |
65 | }
66 |
67 | @Override
68 | public void onThrowable(Throwable t) {
69 | handler.onFailure(new Exception(t.getMessage()));
70 | }
71 | });
72 | }
73 | if (service.getVersion() == "2.0") {
74 | OAuth20Service oAuth20Service = (OAuth20Service) service;
75 | String authorizeUrl = oAuth20Service.getAuthorizationUrl(null);
76 | handler.onReceivedRequestToken(null, authorizeUrl, service.getVersion());
77 | }
78 | }
79 |
80 | // Get the access token by exchanging the requestToken to the defined URL
81 | // Once receiving the access token, fires the onReceivedAccessToken method on the handler
82 | public void fetchAccessToken(final Token requestToken, final Uri uri) {
83 |
84 | Uri authorizedUri = uri;
85 |
86 | if (service.getVersion() == "1.0") {
87 | // Use verifier token to fetch access token
88 |
89 | if (authorizedUri.getQuery().contains(OAuthConstants.VERIFIER)) {
90 | String oauth_verifier = authorizedUri.getQueryParameter(OAuthConstants.VERIFIER);
91 | OAuth1RequestToken oAuth1RequestToken = (OAuth1RequestToken) requestToken;
92 | OAuth10aService oAuth10aService = (OAuth10aService) service;
93 |
94 | oAuth10aService.getAccessTokenAsync(oAuth1RequestToken, oauth_verifier,
95 | new OAuthAsyncRequestCallback() {
96 |
97 | @Override
98 | public void onCompleted(OAuth1AccessToken oAuth1AccessToken) {
99 | setAccessToken(oAuth1AccessToken);
100 | handler.onReceivedAccessToken(oAuth1AccessToken, service.getVersion());
101 | }
102 |
103 | @Override
104 | public void onThrowable(Throwable e) {
105 | handler.onFailure(new OAuthException(e.getMessage()));
106 | }
107 | });
108 |
109 | }
110 | else { // verifier was null
111 | throw new OAuthException("No verifier code was returned with uri '" + uri + "' " +
112 | "and access token cannot be retrieved");
113 | }
114 | } else if (service.getVersion() == "2.0") {
115 | if (authorizedUri.getQuery().contains(OAuthConstants.CODE)) {
116 | String code = authorizedUri.getQueryParameter(OAuthConstants.CODE);
117 | OAuth20Service oAuth20Service = (OAuth20Service) service;
118 | oAuth20Service.getAccessToken(code, new OAuthAsyncRequestCallback() {
119 | @Override
120 | public void onCompleted(OAuth2AccessToken accessToken) {
121 | setAccessToken(accessToken);
122 | handler.onReceivedAccessToken(accessToken, service.getVersion());
123 |
124 | }
125 |
126 | @Override
127 | public void onThrowable(Throwable t) {
128 |
129 | }
130 | });
131 | }
132 | else { // verifier was null
133 | handler.onFailure(new OAuthException("No code was returned with uri '" + uri + "' " +
134 | "and access token cannot be retrieved"));
135 | }
136 | }
137 | }
138 |
139 | // Set the access token used for signing requests
140 | public void setAccessToken(Token accessToken) {
141 | if (accessToken == null) {
142 | this.accessToken = null;
143 | } else {
144 | this.accessToken = accessToken;
145 | }
146 | }
147 |
148 | public Token getAccessToken() {
149 | return this.accessToken;
150 | }
151 |
152 | // Defines the interface handler for different token handlers
153 | public interface OAuthTokenHandler {
154 | public void onReceivedRequestToken(Token requestToken, String authorizeUrl, String oAuthVersion);
155 | public void onReceivedAccessToken(Token accessToken, String oAuthVersion);
156 | public void onFailure(Exception e);
157 | }
158 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/codepath/utils/GenericsUtil.java:
--------------------------------------------------------------------------------
1 | package com.codepath.utils;
2 |
3 | import java.lang.reflect.*;
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 |
10 | @SuppressWarnings("rawtypes")
11 | public class GenericsUtil {
12 | public static List> getTypeArguments(Class baseClass,
13 | Class extends T> childClass) {
14 | Map resolvedTypes = new HashMap();
15 | Type type = childClass;
16 | // start walking up the inheritance hierarchy until we hit baseClass
17 | while (!getClass(type).equals(baseClass)) {
18 | if (type instanceof Class) {
19 | type = ((Class) type).getGenericSuperclass();
20 | } else {
21 | ParameterizedType parameterizedType = (ParameterizedType) type;
22 | assert parameterizedType != null;
23 | Class> rawType = (Class) parameterizedType.getRawType();
24 |
25 | Type[] actualTypeArguments = parameterizedType
26 | .getActualTypeArguments();
27 | TypeVariable>[] typeParameters = rawType.getTypeParameters();
28 | for (int i = 0; i < actualTypeArguments.length; i++) {
29 | resolvedTypes
30 | .put(typeParameters[i], actualTypeArguments[i]);
31 | }
32 |
33 | if (!rawType.equals(baseClass)) {
34 | type = rawType.getGenericSuperclass();
35 | }
36 | }
37 | }
38 |
39 | // finally, for each actual type argument provided to baseClass,
40 | // determine (if possible)
41 | // the raw class for that type argument.
42 | Type[] actualTypeArguments;
43 | if (type instanceof Class) {
44 | actualTypeArguments = ((Class) type).getTypeParameters();
45 | } else {
46 | assert !(type == null);
47 | actualTypeArguments = ((ParameterizedType) type)
48 | .getActualTypeArguments();
49 | }
50 | List> typeArgumentsAsClasses = new ArrayList>();
51 | // resolve types by chasing down type variables.
52 | for (Type baseType : actualTypeArguments) {
53 | while (resolvedTypes.containsKey(baseType)) {
54 | baseType = resolvedTypes.get(baseType);
55 | }
56 | typeArgumentsAsClasses.add(getClass(baseType));
57 | }
58 | return typeArgumentsAsClasses;
59 | }
60 |
61 | private static Class> getClass(Type type) {
62 | if (type instanceof Class) {
63 | return (Class) type;
64 | } else if (type instanceof ParameterizedType) {
65 | return getClass(((ParameterizedType) type).getRawType());
66 | } else if (type instanceof GenericArrayType) {
67 | Type componentType = ((GenericArrayType) type)
68 | .getGenericComponentType();
69 | Class> componentClass = getClass(componentType);
70 | if (componentClass != null) {
71 | return Array.newInstance(componentClass, 0).getClass();
72 | } else {
73 | return null;
74 | }
75 | } else {
76 | return null;
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/library/src/main/res/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codepath/android-oauth-handler/c6a1c4cc12a9c7597d28b702193a6698dd5a0d72/library/src/main/res/.gitkeep
--------------------------------------------------------------------------------
/library/src/main/res/layout/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codepath/android-oauth-handler/c6a1c4cc12a9c7597d28b702193a6698dd5a0d72/library/src/main/res/layout/.gitkeep
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 |
--------------------------------------------------------------------------------