From 9878043661e0ba835fd4d6e2f4a45a0eb6839653 Mon Sep 17 00:00:00 2001 From: Will Vanderhoef Date: Wed, 27 Feb 2019 13:54:59 -0500 Subject: [PATCH 001/193] [Gradle Release Plugin] - pre tag commit: 'v0.10.1'. --- CHANGELOG.md | 2 +- gradle.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b2517ce..6ab4b97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -v0.10.1 - TBD +v0.10.1 - 02/27/2019 ------------- v0.10.0 - 12/14/2018 diff --git a/gradle.properties b/gradle.properties index 6d5c1872..a1b544eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -#Fri, 14 Dec 2018 13:35:12 -0800 +#Wed, 27 Feb 2019 13:53:30 -0500 GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.1-SNAPSHOT -VERSION_NAME=0.10.1-SNAPSHOT +version=0.10.1 +VERSION_NAME=0.10.1 POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 25fc5fa1aa96e48fda0d534cf1e7c6751720e10c Mon Sep 17 00:00:00 2001 From: Will Vanderhoef Date: Wed, 27 Feb 2019 13:55:32 -0500 Subject: [PATCH 002/193] [Gradle Release Plugin] - new version commit: 'v0.10.2-SNAPSHOT'. --- CHANGELOG.md | 3 +++ gradle.properties | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab4b97e..341e4ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +v0.10.2 - TBD +------------- + v0.10.1 - 02/27/2019 ------------- diff --git a/gradle.properties b/gradle.properties index a1b544eb..462c5907 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -#Wed, 27 Feb 2019 13:53:30 -0500 +#Wed, 27 Feb 2019 13:55:32 -0500 GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.1 -VERSION_NAME=0.10.1 +version=0.10.2-SNAPSHOT +VERSION_NAME=0.10.2-SNAPSHOT POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 584b89b88be73e2afd3dc2507c15b1c1726baf85 Mon Sep 17 00:00:00 2001 From: Will Vanderhoef Date: Wed, 20 Feb 2019 10:28:35 -0800 Subject: [PATCH 003/193] Handle redirect for ChromeTab Auth Code flow. fixes #159 --- .../uber/sdk/android/core/auth/AuthUtils.java | 7 +++- .../sdk/android/core/auth/LoginActivity.java | 34 ++++++++++++++----- .../sdk/android/core/auth/AuthUtilsTest.java | 34 ++++++++++++------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java index cc8a6b1e..2f658878 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java @@ -49,6 +49,7 @@ * A utility class for the Uber SDK. */ class AuthUtils { + static final String KEY_AUTHENTICATION_CODE = "code"; static final String KEY_EXPIRATION_TIME = "expires_in"; static final String KEY_SCOPES = "scope"; static final String KEY_TOKEN = "access_token"; @@ -222,8 +223,12 @@ static Intent parseTokenUriToIntent(@NonNull Uri uri) throws LoginAuthentication return data; } + static boolean isAuthorizationCodePresent(@NonNull Uri uri) { + return !TextUtils.isEmpty(uri.getQueryParameter(KEY_AUTHENTICATION_CODE)); + } + static String parseAuthorizationCode(@NonNull Uri uri) throws LoginAuthenticationException { - final String code = uri.getQueryParameter("code"); + final String code = uri.getQueryParameter(KEY_AUTHENTICATION_CODE); if (TextUtils.isEmpty(code)) { throw new LoginAuthenticationException(AuthenticationError.INVALID_RESPONSE); } diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 3c1553ce..2bdc45ff 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -245,12 +245,29 @@ protected void loadWebPage(String redirectUri, ResponseType responseType, Sessio } } - protected boolean handleResponse(@NonNull Uri uri) { - final String fragment = uri.getFragment(); + /** + * Handler for both AccessToken and AuthorizationCode redirects. + * + * @param uri The redirect Uri. + */ + private void handleResponse(@NonNull Uri uri) { + if (AuthUtils.isAuthorizationCodePresent(uri)) { + onCodeReceived(uri); + } else { + handleAccessTokenResponse(uri); + } + } - if (fragment == null) { + /** + * Process the callback for AccessToken. + * + * @param uri Redirect URI containing AccessToken values. + */ + private void handleAccessTokenResponse(@NonNull Uri uri) { + final String fragment = uri.getFragment(); + if (TextUtils.isEmpty(fragment)) { onError(AuthenticationError.INVALID_RESPONSE); - return true; + return; } final Uri fragmentUri = new Uri.Builder().encodedQuery(fragment).build(); @@ -259,11 +276,9 @@ protected boolean handleResponse(@NonNull Uri uri) { final String error = fragmentUri.getQueryParameter(ERROR); if (!TextUtils.isEmpty(error)) { onError(AuthenticationError.fromString(error)); - return true; + } else { + onTokenReceived(fragmentUri); } - - onTokenReceived(fragmentUri); - return true; } protected void loadWebview(String url, String redirectUri) { @@ -427,7 +442,8 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } if (url.startsWith(redirectUri)) { - return handleResponse(uri); + handleAccessTokenResponse(uri); + return true; } return super.shouldOverrideUrlLoading(view, url); diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java index 550e3468..4c7ded39 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java @@ -35,10 +35,7 @@ import java.util.Arrays; import java.util.List; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; +import static junit.framework.Assert.*; import static org.assertj.core.api.Assertions.assertThat; public class AuthUtilsTest extends RobolectricTestBase { @@ -47,7 +44,7 @@ public class AuthUtilsTest extends RobolectricTestBase { private static final String BEARER = "Bearer"; private final String ACCESS_TOKEN_STRING = "accessToken1234"; - private final long EXPIRATION_TIME = 1458770906206l; + private final long EXPIRATION_TIME = 1458770906206L; @Test public void stringToScopeCollection_whenOneScopeInString_shouldReturnCollectionOfOneScope() { @@ -237,6 +234,23 @@ public void generateAccessTokenFromUrl_whenValidAccessTokenWithMultipleScopes_sh assertThat(accessToken.getTokenType()).isEqualTo(BEARER); } + @Test + public void isAuthorizationCodePresent_whenPresent_shouldReturnTrue() { + String redirectUrl = "http://localhost:1234?code=" + AUTH_CODE; + + assertTrue(AuthUtils.isAuthorizationCodePresent(Uri.parse(redirectUrl))); + } + + @Test + public void isAuthorizationCodePresent_whenEmpty_shouldReturnFalse() { + assertFalse(AuthUtils.isAuthorizationCodePresent(Uri.parse("http://localhost:1234?code="))); + } + + @Test + public void isAuthorizationCodePresent_whenMissing_shouldReturnFalse() { + assertFalse(AuthUtils.isAuthorizationCodePresent(Uri.parse("http://localhost:1234"))); + } + @Test public void getCodeFromUrl_whenValidAuthorizationCodePassed() throws LoginAuthenticationException { String redirectUrl = "http://localhost:1234?code=" + AUTH_CODE; @@ -244,18 +258,12 @@ public void getCodeFromUrl_whenValidAuthorizationCodePassed() throws LoginAuthen assertThat(AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl))).isEqualTo(AUTH_CODE); } - @Test + @Test(expected = LoginAuthenticationException.class) public void getCodeFromUrl_whenNoValidAuthorizationCodePassed() throws LoginAuthenticationException { String redirectUrl = "http://localhost:1234?access_token=" + ACCESS_TOKEN_STRING + "&expires_in=" + EXPIRATION_TIME + "&scope=history"; - - try { - AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl)); - fail("Should throw an exception"); - } catch (LoginAuthenticationException e) { - assertThat(e.getAuthenticationError()).isEqualTo(AuthenticationError.INVALID_RESPONSE); - } + AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl)); } @Test From 45ff7c711c6a5c33c7aa6bfe932025190cbc9c62 Mon Sep 17 00:00:00 2001 From: Will Vanderhoef Date: Fri, 1 Mar 2019 16:35:40 -0500 Subject: [PATCH 004/193] Addressing feedback on AuthUtilsTest --- .../uber/sdk/android/core/auth/AuthUtilsTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java index 4c7ded39..0f68f4a8 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java @@ -23,12 +23,10 @@ package com.uber.sdk.android.core.auth; import android.net.Uri; - import com.uber.sdk.android.core.RobolectricTestBase; import com.uber.sdk.core.auth.AccessToken; import com.uber.sdk.core.auth.Scope; import com.uber.sdk.core.client.SessionConfiguration; - import org.junit.Test; import java.util.ArrayList; @@ -44,6 +42,7 @@ public class AuthUtilsTest extends RobolectricTestBase { private static final String BEARER = "Bearer"; private final String ACCESS_TOKEN_STRING = "accessToken1234"; + // GMT: Wednesday, March 23, 2016 10:08:26 PM private final long EXPIRATION_TIME = 1458770906206L; @Test @@ -258,12 +257,17 @@ public void getCodeFromUrl_whenValidAuthorizationCodePassed() throws LoginAuthen assertThat(AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl))).isEqualTo(AUTH_CODE); } - @Test(expected = LoginAuthenticationException.class) - public void getCodeFromUrl_whenNoValidAuthorizationCodePassed() throws LoginAuthenticationException { + @Test + public void getCodeFromUrl_whenNoValidAuthorizationCodePassed() { String redirectUrl = "http://localhost:1234?access_token=" + ACCESS_TOKEN_STRING + "&expires_in=" + EXPIRATION_TIME + "&scope=history"; - AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl)); + try { + AuthUtils.parseAuthorizationCode(Uri.parse(redirectUrl)); + fail("Authorization Code should not be parsable from Access Token response."); + } catch (LoginAuthenticationException e) { + // When an access token string is found when parsing authorization code we expect to get an exception. + } } @Test From 6d7506015afca8d05208d507278622afa5687b9c Mon Sep 17 00:00:00 2001 From: Marvyn Leroy Date: Wed, 6 Mar 2019 17:24:21 -0500 Subject: [PATCH 005/193] Update minimum Uber Eats version to 2488 --- .../main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java index 58155f2d..57748fb8 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java @@ -59,7 +59,7 @@ public class SsoDeeplink implements Deeplink { @VisibleForTesting static final int MIN_UBER_RIDES_VERSION_SUPPORTED = 31302; @VisibleForTesting - static final int MIN_UBER_EATS_VERSION_SUPPORTED = 1085; + static final int MIN_UBER_EATS_VERSION_SUPPORTED = 2488; @VisibleForTesting static final int MIN_UBER_RIDES_VERSION_REDIRECT_FLOW_SUPPORTED = 35757; From bed5d60a2325f5bc710832e36196bd3c38e3666b Mon Sep 17 00:00:00 2001 From: Marvyn Leroy Date: Thu, 7 Mar 2019 11:25:03 -0500 Subject: [PATCH 006/193] Recognize nightly & internal builds --- .../com/uber/sdk/android/core/utils/AppProtocol.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java index 5f81279d..b391b9b4 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java @@ -26,20 +26,19 @@ import static com.uber.sdk.android.core.SupportedAppType.UBER_EATS; public class AppProtocol { - @Deprecated - public static final String[] UBER_PACKAGE_NAMES = - {"com.ubercab", "com.ubercab.presidio.app", "com.ubercab.presidio.exo", "com.ubercab.presidio.development"}; - @VisibleForTesting static final String[] RIDER_PACKAGE_NAMES = {"com.ubercab.presidio.development", "com.ubercab.presidio.exo", "com.ubercab.presidio.app", "com.ubercab"}; @VisibleForTesting static final String[] EATS_PACKAGE_NAMES = - {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats"}; + {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats", "com.ubercab.eats.internal", "com.ubercab.eats.nightly"}; public static final String PLATFORM = "android"; - private static final String UBER_RIDER_HASH = "411c40b31f6d01dac68d711df99b6eafeec8e73b"; + // Both com.ubercab.eats and com.ubercab.eats.nightly share the same hash private static final String UBER_EATS_HASH = "ae0b86995f174533b423067837beba13d922fbb0"; + // For com.ubercab.eats.internal + private static final String UBER_EATS_INTERNAL_HASH = "9a715f0cbb44b01e3c41c9bda30feba107561e79"; + private static final String UBER_RIDER_HASH = "411c40b31f6d01dac68d711df99b6eafeec8e73b"; private static final String HASH_ALGORITHM_SHA1 = "SHA-1"; private static final int DEFAULT_MIN_VERSION = 0; @@ -50,6 +49,7 @@ private static HashSet buildAppSignatureHashes() { HashSet set = new HashSet<>(); set.add(UBER_RIDER_HASH); set.add(UBER_EATS_HASH); + set.add(UBER_EATS_INTERNAL_HASH); return set; } From 6edb3ac9b817546e0a83372bae3a5a27a376e697 Mon Sep 17 00:00:00 2001 From: Marvyn Leroy Date: Thu, 7 Mar 2019 11:46:26 -0500 Subject: [PATCH 007/193] Bring back deprecated code and change order of package names --- .../java/com/uber/sdk/android/core/utils/AppProtocol.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java index b391b9b4..445deec0 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java @@ -26,12 +26,16 @@ import static com.uber.sdk.android.core.SupportedAppType.UBER_EATS; public class AppProtocol { + @Deprecated + public static final String[] UBER_PACKAGE_NAMES = + {"com.ubercab", "com.ubercab.presidio.app", "com.ubercab.presidio.exo", "com.ubercab.presidio.development"}; + @VisibleForTesting static final String[] RIDER_PACKAGE_NAMES = {"com.ubercab.presidio.development", "com.ubercab.presidio.exo", "com.ubercab.presidio.app", "com.ubercab"}; @VisibleForTesting static final String[] EATS_PACKAGE_NAMES = - {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats", "com.ubercab.eats.internal", "com.ubercab.eats.nightly"}; + {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats.internal", "com.ubercab.eats.nightly", "com.ubercab.eats"}; public static final String PLATFORM = "android"; // Both com.ubercab.eats and com.ubercab.eats.nightly share the same hash From 950c92e962f605cc5eafc9ee7facd3bcd93aa928 Mon Sep 17 00:00:00 2001 From: Marvyn Leroy Date: Thu, 7 Mar 2019 18:32:11 -0500 Subject: [PATCH 008/193] Introduce compatibility with nightly build --- .../main/java/com/uber/sdk/android/core/utils/AppProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java index 445deec0..77d35f92 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java @@ -35,7 +35,7 @@ public class AppProtocol { {"com.ubercab.presidio.development", "com.ubercab.presidio.exo", "com.ubercab.presidio.app", "com.ubercab"}; @VisibleForTesting static final String[] EATS_PACKAGE_NAMES = - {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats.internal", "com.ubercab.eats.nightly", "com.ubercab.eats"}; + {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats.nightly", "com.ubercab.eats"}; public static final String PLATFORM = "android"; // Both com.ubercab.eats and com.ubercab.eats.nightly share the same hash From 2107517bed9977627576c508e223123a8f3f7095 Mon Sep 17 00:00:00 2001 From: Marvyn Leroy Date: Thu, 7 Mar 2019 18:39:48 -0500 Subject: [PATCH 009/193] Revert previous code --- .../java/com/uber/sdk/android/core/utils/AppProtocol.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java index 77d35f92..75d9ceb5 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java @@ -38,11 +38,8 @@ public class AppProtocol { {"com.ubercab.eats.debug", "com.ubercab.eats.exo", "com.ubercab.eats.nightly", "com.ubercab.eats"}; public static final String PLATFORM = "android"; - // Both com.ubercab.eats and com.ubercab.eats.nightly share the same hash - private static final String UBER_EATS_HASH = "ae0b86995f174533b423067837beba13d922fbb0"; - // For com.ubercab.eats.internal - private static final String UBER_EATS_INTERNAL_HASH = "9a715f0cbb44b01e3c41c9bda30feba107561e79"; private static final String UBER_RIDER_HASH = "411c40b31f6d01dac68d711df99b6eafeec8e73b"; + private static final String UBER_EATS_HASH = "ae0b86995f174533b423067837beba13d922fbb0"; private static final String HASH_ALGORITHM_SHA1 = "SHA-1"; private static final int DEFAULT_MIN_VERSION = 0; @@ -53,7 +50,6 @@ private static HashSet buildAppSignatureHashes() { HashSet set = new HashSet<>(); set.add(UBER_RIDER_HASH); set.add(UBER_EATS_HASH); - set.add(UBER_EATS_INTERNAL_HASH); return set; } From 06e43f6cae59b687452bb6c03ec16e3c6a4ce2ca Mon Sep 17 00:00:00 2001 From: Will Vanderhoef Date: Thu, 30 May 2019 17:09:33 -0400 Subject: [PATCH 010/193] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341e4ea2..79f2cb31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ v0.10.2 - TBD v0.10.1 - 02/27/2019 ------------- +### Fixed + - [Issue #153](https://github.com/uber/rides-android-sdk/issues/153) NullPointerException when login via SSO without setting product flow priority + - [Issue #151](https://github.com/uber/rides-android-sdk/issues/151) Login throws IllegalStateException when using only CustomScopes + + v0.10.0 - 12/14/2018 ------------ From c4cac9022df1071f26943f479d8f9c94be152a61 Mon Sep 17 00:00:00 2001 From: Adam Rogal Date: Tue, 29 Oct 2019 14:36:49 -0700 Subject: [PATCH 011/193] fix proposal --- .gitignore | 1 + .../sdk/android/core/auth/LoginActivity.java | 6 ++++++ .../android/core/utils/CustomTabsHelper.java | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d12963ef..ed5c9cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ obj .DS_Store log.txt +.vscode diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 2bdc45ff..24ef9747 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -199,6 +199,12 @@ protected void init() { } } + @Override + protected void onDestroy() { + super.onDestroy(); + customTabsHelper.destory(this); + } + protected void loadUrl() { Intent intent = getIntent(); diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java index 471ba75c..6e808434 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java @@ -51,6 +51,8 @@ public class CustomTabsHelper { private static String packageNameToUse; + private CustomTabsServiceConnection connection; + public CustomTabsHelper() {} /** @@ -69,7 +71,7 @@ public void openCustomTab( final String packageName = getPackageNameToUse(context); if (packageName != null) { - final CustomTabsServiceConnection connection = new CustomTabsServiceConnection() { + connection = new CustomTabsServiceConnection() { @Override public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) { client.warmup(0L); // This prevents backgrounding after redirection @@ -78,18 +80,27 @@ public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabs customTabsIntent.intent.setData(uri); customTabsIntent.launchUrl(context, uri); } + @Override - public void onServiceDisconnected(ComponentName name) {} + public void onServiceDisconnected(ComponentName name) { + } }; CustomTabsClient.bindCustomTabsService(context, packageName, connection); } else if (fallback != null) { fallback.openUri(context, uri); } else { - Log.e(UberSdk.UBER_SDK_LOG_TAG, - "Use of openCustomTab without Customtab support or a fallback set"); + Log.e(UberSdk.UBER_SDK_LOG_TAG, "Use of openCustomTab without Customtab support or a fallback set"); } } + /** + * Called to clean up the CustomTab when the parentActivity is destroyed. + */ + public void onDestroy(Activity parentActivity) { + parentActivity.unbindService(connection); + connection = null; + } + /** * Goes through all apps that handle VIEW intents and have a warmup service. Picks * the one chosen by the user if there is one, otherwise makes a best effort to return a From 7a70a1ad6aab725aa90d2f196198fa141dc8d1de Mon Sep 17 00:00:00 2001 From: Adam Rogal Date: Tue, 29 Oct 2019 15:04:24 -0700 Subject: [PATCH 012/193] fix syntax --- .../main/java/com/uber/sdk/android/core/auth/LoginActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 24ef9747..21dd2bda 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -202,7 +202,7 @@ protected void init() { @Override protected void onDestroy() { super.onDestroy(); - customTabsHelper.destory(this); + customTabsHelper.onDestroy(this); } protected void loadUrl() { From ee405a47f69126e286d73d2d2d88200feaab386b Mon Sep 17 00:00:00 2001 From: Ty Smith Date: Tue, 3 Dec 2019 14:16:08 -0800 Subject: [PATCH 013/193] [Gradle Release Plugin] - pre tag commit: 'v0.10.2'. --- CHANGELOG.md | 2 +- gradle.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f2cb31..c23411d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -v0.10.2 - TBD +v0.10.2 - 12/03/2019 ------------- v0.10.1 - 02/27/2019 diff --git a/gradle.properties b/gradle.properties index 462c5907..992d35bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -#Wed, 27 Feb 2019 13:55:32 -0500 +#Tue, 03 Dec 2019 14:12:31 -0800 GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.2-SNAPSHOT -VERSION_NAME=0.10.2-SNAPSHOT +version=0.10.2 +VERSION_NAME=0.10.2 POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From c55bec96dfd129b3b64ca833aecfd74cd2af1c81 Mon Sep 17 00:00:00 2001 From: Ty Smith Date: Tue, 3 Dec 2019 14:16:24 -0800 Subject: [PATCH 014/193] [Gradle Release Plugin] - new version commit: 'v0.10.3-SNAPSHOT'. --- CHANGELOG.md | 3 +++ gradle.properties | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c23411d5..ee246e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +v0.10.3 - TBD +------------- + v0.10.2 - 12/03/2019 ------------- diff --git a/gradle.properties b/gradle.properties index 992d35bf..f0518966 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -#Tue, 03 Dec 2019 14:12:31 -0800 +#Tue, 03 Dec 2019 14:16:24 -0800 GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.2 -VERSION_NAME=0.10.2 +version=0.10.3-SNAPSHOT +VERSION_NAME=0.10.3-SNAPSHOT POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 405f53a8b7c6dd795f9d691f874c4c1fce093380 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Thu, 30 Apr 2020 18:23:59 +0530 Subject: [PATCH 015/193] fix crash in CustomTabsHelper if connection is null --- .../com/uber/sdk/android/core/utils/CustomTabsHelper.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java index 6e808434..614f1587 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java @@ -97,8 +97,10 @@ public void onServiceDisconnected(ComponentName name) { * Called to clean up the CustomTab when the parentActivity is destroyed. */ public void onDestroy(Activity parentActivity) { - parentActivity.unbindService(connection); - connection = null; + if (connection != null) { + parentActivity.unbindService(connection); + connection = null; + } } /** From 4fb5a36443a0c725e5e5a7139072072497aa1b65 Mon Sep 17 00:00:00 2001 From: Rafael Toledo Date: Thu, 6 May 2021 15:07:43 -0300 Subject: [PATCH 016/193] Remove Travis and migrate to Github Actions --- .github/workflows/build.yml | 81 +++++++++++++++++++++++++++++++++++++ .travis.yml | 51 ----------------------- README.md | 2 +- 3 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..2ac29e28 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,81 @@ +name: CI + +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1.0.3 + - name: Install JDK + uses: actions/setup-java@v2.0.0 + with: + distribution: 'adopt' + java-version: '8' + - name: Lint and Unit tests + run: ./gradlew check --stacktrace + - name: Upload lint and test reports + if: always() + uses: actions/upload-artifact@v2.2.3 + with: + name: execution-reports + path: | + ./core-android/build/reports + ./rides-android/build/reports + + test: + runs-on: macOS-latest # enables hardware acceleration in the virtual machine, required for emulator testing + strategy: + matrix: + api-level: [ 21, 23, 26, 29, 30 ] + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1.0.3 + - name: Install JDK + uses: actions/setup-java@v2.0.0 + with: + distribution: 'adopt' + java-version: '8' + - name: Emulator tests + uses: reactivecircus/android-emulator-runner@v2.15.0 + with: + api-level: ${{ matrix.api-level }} + target: google_apis + arch: x86 + disable-animations: true + script: ./gradlew connectedCheck --stacktrace + - name: Upload instrumented test reports + if: always() + uses: actions/upload-artifact@v2.2.3 + with: + name: test-reports + path: | + ./core-android/build/reports + ./rides-android/build/reports + + upload-snapshots: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + needs: + - check + - test + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1.0.3 + - name: Install JDK + uses: actions/setup-java@v2.0.0 + with: + distribution: 'adopt' + java-version: '8' + - name: Upload snapshots + run: ./gradlew uploadArchives --stacktrace + env: + SONATYPE_NEXUS_USERNAME: ${{ secrets.SonatypeUsername }} + SONATYPE_NEXUS_PASSWORD: ${{ secrets.SonatypePassword }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index aca24380..00000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -language: android - -android: - components: - # Update tools and then platform-tools explicitly so lint gets an updated database. Can be removed once 3.0 is out. - - tools - - platform-tools - -jdk: - - oraclejdk8 - -before_install: - # Install SDK license so Android Gradle plugin can install deps. - - mkdir "$ANDROID_HOME/licenses" || true - - echo "$LICENSES_HASH" > "$ANDROID_HOME/licenses/android-sdk-license" - - echo "$LICENSES_HASH_TWO" >> "$ANDROID_HOME/licenses/android-sdk-license" - # Install the rest of tools (e.g., avdmanager) - - yes | sdkmanager tools - # Install the system image - - sdkmanager "system-images;android-18;default;armeabi-v7a" - # Create and start emulator for the script. Meant to race the install task. - - echo no | avdmanager create avd --force -n test -k "system-images;android-18;default;armeabi-v7a" - - $ANDROID_HOME/emulator/emulator -avd test -no-window > /dev/null 2>&1 & - -install: ./gradlew clean assemble assembleAndroidTest --stacktrace - -before_script: - - android-wait-for-emulator - - adb shell input keyevent 82 - -script: - - ./gradlew check connectedCheck --stacktrace - -after_success: - - .buildscript/deploy_snapshot.sh - -env: - global: - - secure: "g5g+VmfqVF7PMuTl00NUQpX4pFohsEVb/SUK3adFWdT5SKOZgi28ArwUEmCMbG/he5UyyQJkv38CKfa+6jZM1cDDNkICGEjM3YCImJyCDpUKLKLFlsvDtAPK7H8rUpLgmGOHQONObtJbhtkmWg+0fr6TRj+yltZZl/6dKZ7Im4aeeMDE03Hy8MubvxRqANF3lT1bJMLUTIAP/gHSD46AKhlbnUJHW5QXqKJqMbtt3nyZNZ3aWPVMoGc+zTbpMWD6dvCH0Etc/nEatMINLunHEUf59CVEiCmQSHrWHsyDn75OiVCJUEzj9euTKE1Kz5OjKrPNYPlW1V840EOLdr/I3rz50gFmMjSyNZcd6D4W6jygOC0QDm57ISHO/Jtl4iLaPzqKMRT4daiyqWZJrnFB8Xlt8CjCxxxXT0A1ZXgro1Auoaa4OVg53Ey40CuRABHbVu6KnskcT5A52XHlDxh3wi5LnPaLH5LzB5/erFzzgLn0GPYTVKInKen6/cY+/+h84rMgS8VkKegl3oUuz1Kzej1GkxnFzdwm3j+1CuDTkadmlpn92+N6wXlG628cs7e93m0QhxXsd+mIaAwyicQeelsOeaAXTp72xfvXE7RQA2YNXjAqVoXu0kTWlXs2aDlm3HHOS4FR0kR96TJarqpinD+zAzEseaP4rMCgqdOsKok=" - - secure: "cp+/y+Zw6ijlK/JBigffDVhQyVhZTfSfgkkM5V0USlJIS2c3N7e1vruiXIcRLEIfzgTbU0veVn37NDIDMkoUDlUY9q2p0uM+xwoO8URYK/SqOhevFSd+QgMX9QwnaV5reD6aZzrz/SI6AbA6hAmIRIXZGkxRfgsB1nJN4m1Fwiwib2IXPHmBa+8d1vS7yU36FrY/3fHthgz1T1M+VDVFmbIGbBqCj5GR6CF+TcDcEQW3UvH671s7IAzuY8VzopeQjaqHE+U+8wrkg0mmiX6/B3a9nH6G/rfaeLTpomb0QCojk+W8en85f7KdK6/7SEiKU0NJmJ62ccc8n0h1VxhWYx/r2S2nVM8FfVuOTOPWuGZU0ULI1ylqAYgppBMp49eOlAJV5QzyBB7Q+py20wEPBisIlvZXLytNm7RWojiTt1wm2PaEhFzYmfPgLlViFDv7N+hLhxproDpw+c1XhhM2ZPyxW8oUriRxyMRqXkJClEgNg+0X9IyFzGc93S7yw0u5SMwhC735vjk3G4sSL/bZjre5zSc/XXM+HqIzWP+KUwRZqdcwW7uuGMB8LUWAHiCrZUuCRL25cisKW/xArH3Sg4nrFG3VP+8yMgRRVJWMtCandSzsJcBmiFPEqsb12eTo2hQfJzoKMX9HcOitfkjn1U1zsJm1ycsG61prF7fhdHg=" - -branches: - except: - - gh-pages - -notifications: - email: false - -cache: - directories: - - $HOME/.gradle diff --git a/README.md b/README.md index 6209f33a..e4eb2d81 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Uber Rides Android SDK (beta) [![Build Status](https://travis-ci.org/uber/rides-android-sdk.svg?branch=master)](https://travis-ci.org/uber/rides-android-sdk) +# Uber Rides Android SDK (beta) ![Build Status](https://github.com/uber/rides-android-sdk/workflows/CI/badge.svg) Official Android SDK to support: - Ride Request Button From 34666dfacf4029b00f4f9bae91507f2a17e73889 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Wed, 30 Jun 2021 09:49:47 -0700 Subject: [PATCH 017/193] Migrate to AndroidX --- core-android/build.gradle | 2 +- .../java/com/uber/sdk/android/core/UberButton.java | 14 +++++++------- .../java/com/uber/sdk/android/core/UberSdk.java | 2 +- .../java/com/uber/sdk/android/core/UberStyle.java | 10 +++++----- .../sdk/android/core/auth/AccessTokenManager.java | 6 +++--- .../com/uber/sdk/android/core/auth/AuthUtils.java | 8 ++------ .../sdk/android/core/auth/AuthenticationError.java | 2 +- .../core/auth/LegacyUriRedirectHandler.java | 4 ++-- .../uber/sdk/android/core/auth/LoginActivity.java | 8 +++----- .../uber/sdk/android/core/auth/LoginButton.java | 10 +++++----- .../uber/sdk/android/core/auth/LoginCallback.java | 2 +- .../uber/sdk/android/core/auth/LoginManager.java | 6 +++--- .../uber/sdk/android/core/auth/SsoDeeplink.java | 4 ++-- .../sdk/android/core/install/SignupDeeplink.java | 2 +- .../uber/sdk/android/core/utils/AppProtocol.java | 6 +++--- .../sdk/android/core/utils/CustomTabsHelper.java | 10 +++++----- .../sdk/android/core/utils/PackageManagers.java | 4 ++-- .../uber/sdk/android/core/utils/Preconditions.java | 2 +- .../com/uber/sdk/android/core/utils/Utility.java | 2 +- .../android/core/auth/AccessTokenPreferences.java | 4 ++-- .../sdk/android/core/auth/LoginActivityTest.java | 2 +- .../sdk/android/core/auth/LoginButtonTest.java | 2 +- .../sdk/android/core/auth/LoginManagerTest.java | 4 ++-- gradle.properties | 2 ++ gradle/dependencies.gradle | 14 +++++++------- rides-android/build.gradle | 2 +- .../uber/sdk/android/rides/RequestDeeplink.java | 2 +- .../sdk/android/rides/RequestDeeplinkBehavior.java | 2 +- .../com/uber/sdk/android/rides/RideParameters.java | 4 ++-- .../sdk/android/rides/RideRequestActivity.java | 12 ++++++------ .../android/rides/RideRequestActivityBehavior.java | 2 +- .../uber/sdk/android/rides/RideRequestButton.java | 6 +++--- .../sdk/android/rides/RideRequestDeeplink.java | 8 ++++---- .../uber/sdk/android/rides/RideRequestView.java | 6 +++--- .../internal/RideRequestButtonController.java | 6 +++--- .../rides/internal/RideRequestButtonView.java | 2 +- .../sdk/android/rides/internal/TimeDelegate.java | 2 +- .../android/rides/internal/TimePriceDelegate.java | 2 +- .../sdk/android/rides/RideRequestDeeplinkTest.java | 2 +- .../java/com/uber/sdk/android/rides/TestUtils.java | 2 +- .../sdk/android/samples/LoginSampleActivity.java | 4 ++-- .../sdk/android/rides/samples/SampleActivity.java | 8 +------- 42 files changed, 97 insertions(+), 107 deletions(-) diff --git a/core-android/build.gradle b/core-android/build.gradle index 8f4979c9..f006e1ae 100644 --- a/core-android/build.gradle +++ b/core-android/build.gradle @@ -37,7 +37,7 @@ android { targetSdkVersion deps.build.targetSdkVersion versionName VERSION_NAME consumerProguardFiles 'consumer-proguard-rules.txt' - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } compileOptions { diff --git a/core-android/src/main/java/com/uber/sdk/android/core/UberButton.java b/core-android/src/main/java/com/uber/sdk/android/core/UberButton.java index 9626bb0d..6fd48f8f 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/UberButton.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/UberButton.java @@ -29,13 +29,13 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Typeface; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.annotation.StyleRes; -import android.support.annotation.VisibleForTesting; -import android.support.v7.widget.AppCompatButton; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.widget.AppCompatButton; import android.util.AttributeSet; import android.view.Gravity; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/UberSdk.java b/core-android/src/main/java/com/uber/sdk/android/core/UberSdk.java index 907319dc..64bca388 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/UberSdk.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/UberSdk.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.core; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.core.client.SessionConfiguration; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/UberStyle.java b/core-android/src/main/java/com/uber/sdk/android/core/UberStyle.java index b49ead1e..39a530a6 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/UberStyle.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/UberStyle.java @@ -24,11 +24,11 @@ import android.content.Context; import android.content.res.TypedArray; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StyleRes; -import android.support.annotation.StyleableRes; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StyleRes; +import androidx.annotation.StyleableRes; +import androidx.annotation.VisibleForTesting; import android.util.AttributeSet; public enum UberStyle { diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AccessTokenManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AccessTokenManager.java index 3b4c16c8..e1c77e77 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AccessTokenManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AccessTokenManager.java @@ -24,9 +24,9 @@ import android.content.Context; import android.content.SharedPreferences; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import android.webkit.CookieManager; import com.uber.sdk.core.auth.AccessToken; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java index 2f658878..9f96f20b 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java @@ -25,11 +25,9 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ResolveInfo; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Base64; import android.webkit.WebView; @@ -38,10 +36,8 @@ import com.uber.sdk.core.auth.Scope; import com.uber.sdk.core.client.SessionConfiguration; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Locale; import java.util.Set; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthenticationError.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthenticationError.java index e7a68a31..3f1ee11d 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthenticationError.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthenticationError.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.core.auth; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.Locale; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandler.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandler.java index 8ec61f17..d7134fc2 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandler.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandler.java @@ -3,8 +3,8 @@ import android.app.Activity; import android.content.Context; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.v4.util.Pair; +import androidx.annotation.NonNull; +import androidx.core.util.Pair; import com.uber.sdk.android.core.R; import com.uber.sdk.android.core.utils.Utility; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 21dd2bda..6ccdfa1f 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -29,9 +29,9 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; -import android.support.customtabs.CustomTabsIntent; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.browser.customtabs.CustomTabsIntent; import android.text.TextUtils; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; @@ -47,8 +47,6 @@ import com.uber.sdk.core.client.SessionConfiguration; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; /** * {@link android.app.Activity} that shows web view for Uber user authentication and authorization. diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginButton.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginButton.java index ee38ad6c..d4aa6071 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginButton.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginButton.java @@ -26,11 +26,11 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.annotation.StyleRes; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.annotation.VisibleForTesting; import android.util.AttributeSet; import android.view.View; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginCallback.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginCallback.java index a8bc569b..e4418723 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginCallback.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginCallback.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.core.auth; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.core.auth.AccessToken; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index e495928b..ca26daab 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -24,9 +24,9 @@ import android.app.Activity; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import android.util.Log; import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.UberSdk; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java index 57748fb8..589244de 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/SsoDeeplink.java @@ -28,8 +28,8 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import android.util.Log; import com.uber.sdk.android.core.BuildConfig; import com.uber.sdk.android.core.Deeplink; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/install/SignupDeeplink.java b/core-android/src/main/java/com/uber/sdk/android/core/install/SignupDeeplink.java index fee1ea3a..bd8a8491 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/install/SignupDeeplink.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/install/SignupDeeplink.java @@ -25,7 +25,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.core.Deeplink; import com.uber.sdk.android.core.R; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java index 75d9ceb5..954b5bd2 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/AppProtocol.java @@ -6,9 +6,9 @@ import android.content.pm.PackageManager; import android.content.pm.Signature; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import android.util.Base64; import android.util.Pair; import com.uber.sdk.android.core.SupportedAppType; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java index 614f1587..b94e8a42 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/CustomTabsHelper.java @@ -22,11 +22,11 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.customtabs.CustomTabsClient; -import android.support.customtabs.CustomTabsIntent; -import android.support.customtabs.CustomTabsServiceConnection; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.browser.customtabs.CustomTabsClient; +import androidx.browser.customtabs.CustomTabsIntent; +import androidx.browser.customtabs.CustomTabsServiceConnection; import android.text.TextUtils; import android.util.Log; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/PackageManagers.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/PackageManagers.java index 5b17bb90..09957c16 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/PackageManagers.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/PackageManagers.java @@ -25,8 +25,8 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public class PackageManagers { diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/Preconditions.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/Preconditions.java index 10a4cf73..4e3d1f4f 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/Preconditions.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/Preconditions.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.core.utils; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.Collection; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/utils/Utility.java b/core-android/src/main/java/com/uber/sdk/android/core/utils/Utility.java index 6ccade0f..5a4b08d7 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/utils/Utility.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/utils/Utility.java @@ -5,7 +5,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import com.uber.sdk.android.core.R; diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/AccessTokenPreferences.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/AccessTokenPreferences.java index 69bf51b0..6cb503a3 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/AccessTokenPreferences.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/AccessTokenPreferences.java @@ -24,8 +24,8 @@ import android.content.Context; import android.content.SharedPreferences; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.uber.sdk.core.auth.AccessToken; diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index 888f7d92..aa2df67a 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.net.Uri; -import android.support.customtabs.CustomTabsIntent; +import androidx.browser.customtabs.CustomTabsIntent; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.uber.sdk.android.core.RobolectricTestBase; diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginButtonTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginButtonTest.java index 7fdc1c71..7bc84617 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginButtonTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginButtonTest.java @@ -25,7 +25,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.AttributeSet; import com.google.common.collect.Sets; diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java index fc2aab3d..66063414 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.google.common.collect.ImmutableList; import com.uber.sdk.android.core.RobolectricTestBase; import com.uber.sdk.android.core.SupportedAppType; @@ -36,7 +36,7 @@ import com.uber.sdk.core.auth.Scope; import com.uber.sdk.core.client.Session; import com.uber.sdk.core.client.SessionConfiguration; -import edu.emory.mathcs.backport.java.util.Collections; + import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; diff --git a/gradle.properties b/gradle.properties index f0518966..751eb561 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,3 +21,5 @@ GITHUB_OWNER=uber GITHUB_REPO=rides-android-sdk GITHUB_DOWNLOAD_PREFIX=https\://github.com/uber/rides-android-sdk/releases/download/ GITHUB_BRANCH=master +android.useAndroidX=true +android.enableJetifier=true diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index c40d944d..add28e0d 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -22,18 +22,18 @@ def versions = [ def build = [ gradleVersion: '4.9', - buildToolsVersion: '27.0.3', - compileSdkVersion: 27, + buildToolsVersion: '28.0.2', + compileSdkVersion: 28, ci: 'true' == System.getenv('CI'), minSdkVersion: 15, - targetSdkVersion: 27, + targetSdkVersion: 28, repositories: [ plugins: 'https://plugins.gradle.org/m2/' ], gradlePlugins: [ - android: 'com.android.tools.build:gradle:3.1.0', + android: 'com.android.tools.build:gradle:3.2.0', release: 'net.researchgate:gradle-release:2.1.2', github: 'co.riiid:gradle-github-plugin:0.4.2', cobertura: 'net.saliman:gradle-cobertura-plugin:2.3.1', @@ -45,9 +45,9 @@ def misc = [ ] def support = [ - annotations: "com.android.support:support-annotations:${versions.support}", - appCompat: "com.android.support:appcompat-v7:${versions.support}", - chrometabs: "com.android.support:customtabs:${versions.support}", + annotations: 'androidx.annotation:annotation:1.0.0', + appCompat : 'androidx.appcompat:appcompat:1.0.0', + chrometabs : 'androidx.browser:browser:1.0.0', ] def test = [ diff --git a/rides-android/build.gradle b/rides-android/build.gradle index 5ede030b..17fa454f 100644 --- a/rides-android/build.gradle +++ b/rides-android/build.gradle @@ -37,7 +37,7 @@ android { targetSdkVersion deps.build.targetSdkVersion versionName VERSION_NAME consumerProguardFiles 'consumer-proguard-rules.txt' - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } compileOptions { diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplink.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplink.java index 03da7bd1..fc6e512e 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplink.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplink.java @@ -24,7 +24,7 @@ import android.content.Context; import android.net.Uri; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.core.utils.AppProtocol; import com.uber.sdk.android.core.utils.CustomTabsHelper; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplinkBehavior.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplinkBehavior.java index 72a1baa4..e1a5bfa4 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplinkBehavior.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RequestDeeplinkBehavior.java @@ -23,7 +23,7 @@ package com.uber.sdk.android.rides; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.core.UberSdk; import com.uber.sdk.core.client.SessionConfiguration; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideParameters.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideParameters.java index cbb0d566..aee56870 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideParameters.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideParameters.java @@ -24,8 +24,8 @@ import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; /** * Represents the parameters for an Uber ride. diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java index 8964c311..19628edc 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java @@ -30,12 +30,12 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.annotation.VisibleForTesting; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.VisibleForTesting; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import com.uber.sdk.android.core.auth.AccessTokenManager; import com.uber.sdk.android.core.auth.AuthenticationError; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivityBehavior.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivityBehavior.java index 471b18b2..167f9dc7 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivityBehavior.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivityBehavior.java @@ -25,7 +25,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.core.UberSdk; import com.uber.sdk.android.core.auth.AccessTokenManager; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestButton.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestButton.java index c7e554ab..8f28562f 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestButton.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestButton.java @@ -28,9 +28,9 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Typeface; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StyleRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StyleRes; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestDeeplink.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestDeeplink.java index 393111c4..99846e11 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestDeeplink.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestDeeplink.java @@ -24,10 +24,10 @@ import android.content.Context; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.support.customtabs.CustomTabsIntent; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.browser.customtabs.CustomTabsIntent; import com.uber.sdk.android.core.Deeplink; import com.uber.sdk.android.core.utils.AppProtocol; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestView.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestView.java index de742995..053e15b0 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestView.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestView.java @@ -27,9 +27,9 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import android.util.AttributeSet; import android.webkit.GeolocationPermissions; import android.webkit.WebChromeClient; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonController.java b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonController.java index 9188d83c..4071b964 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonController.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonController.java @@ -22,9 +22,9 @@ package com.uber.sdk.android.rides.internal; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.uber.sdk.android.rides.RideParameters; import com.uber.sdk.android.rides.RideRequestButtonCallback; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonView.java b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonView.java index 7be4e7e0..7de310c8 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonView.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/RideRequestButtonView.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.rides.internal; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.rides.client.model.PriceEstimate; import com.uber.sdk.rides.client.model.TimeEstimate; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimeDelegate.java b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimeDelegate.java index c28a6ef0..81493c22 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimeDelegate.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimeDelegate.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.rides.internal; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.rides.RideRequestButtonCallback; import com.uber.sdk.rides.client.error.ApiError; diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimePriceDelegate.java b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimePriceDelegate.java index dcf10c80..a6a99d9f 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimePriceDelegate.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/internal/TimePriceDelegate.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.rides.internal; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.uber.sdk.android.rides.RideRequestButtonCallback; import com.uber.sdk.rides.client.model.PriceEstimate; diff --git a/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestDeeplinkTest.java b/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestDeeplinkTest.java index 358dc2b4..85373545 100644 --- a/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestDeeplinkTest.java +++ b/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestDeeplinkTest.java @@ -24,7 +24,7 @@ import android.content.Context; import android.net.Uri; -import android.support.customtabs.CustomTabsIntent; +import androidx.browser.customtabs.CustomTabsIntent; import com.uber.sdk.android.core.Deeplink; import com.uber.sdk.android.core.utils.AppProtocol; diff --git a/rides-android/src/test/java/com/uber/sdk/android/rides/TestUtils.java b/rides-android/src/test/java/com/uber/sdk/android/rides/TestUtils.java index c052fb5d..b73e1fb3 100644 --- a/rides-android/src/test/java/com/uber/sdk/android/rides/TestUtils.java +++ b/rides-android/src/test/java/com/uber/sdk/android/rides/TestUtils.java @@ -22,7 +22,7 @@ package com.uber.sdk.android.rides; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.TextUtils; import com.google.common.io.Files; diff --git a/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java b/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java index 1123de90..9df846c5 100644 --- a/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java +++ b/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java @@ -27,8 +27,8 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; diff --git a/samples/request-button-sample/src/main/java/com/uber/sdk/android/rides/samples/SampleActivity.java b/samples/request-button-sample/src/main/java/com/uber/sdk/android/rides/samples/SampleActivity.java index bfaebc9a..0be41b4e 100644 --- a/samples/request-button-sample/src/main/java/com/uber/sdk/android/rides/samples/SampleActivity.java +++ b/samples/request-button-sample/src/main/java/com/uber/sdk/android/rides/samples/SampleActivity.java @@ -22,13 +22,11 @@ package com.uber.sdk.android.rides.samples; -import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -36,13 +34,9 @@ import com.uber.sdk.android.core.Deeplink; import com.uber.sdk.android.core.auth.AccessTokenManager; -import com.uber.sdk.android.core.auth.AuthenticationError; import com.uber.sdk.android.rides.RideParameters; -import com.uber.sdk.android.rides.RideRequestActivity; -import com.uber.sdk.android.rides.RideRequestActivityBehavior; import com.uber.sdk.android.rides.RideRequestButton; import com.uber.sdk.android.rides.RideRequestButtonCallback; -import com.uber.sdk.android.rides.RideRequestViewError; import com.uber.sdk.core.auth.AccessToken; import com.uber.sdk.core.auth.AccessTokenStorage; import com.uber.sdk.core.client.ServerTokenSession; From f94d75eecd8b1f2da2f35d4df76c6b83a41a5813 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Wed, 30 Jun 2021 09:57:29 -0700 Subject: [PATCH 018/193] version configuration change --- gradle/dependencies.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index add28e0d..8b68ee75 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -16,7 +16,7 @@ def versions = [ androidTest: '0.5', - support: '27.1.1', + androidxVersion: '1.0.0', uberJava: '0.8.0', ] @@ -45,9 +45,9 @@ def misc = [ ] def support = [ - annotations: 'androidx.annotation:annotation:1.0.0', - appCompat : 'androidx.appcompat:appcompat:1.0.0', - chrometabs : 'androidx.browser:browser:1.0.0', + annotations: "androidx.annotation:annotation:${versions.androidxVersion}", + appCompat : "androidx.appcompat:appcompat:${versions.androidxVersion}", + chrometabs : "androidx.browser:browser:${versions.androidxVersion}", ] def test = [ From 870bea405f0c1a39be8b70e4d7394d391b53eb2a Mon Sep 17 00:00:00 2001 From: Rafael Toledo Date: Mon, 5 Jul 2021 19:18:55 -0300 Subject: [PATCH 019/193] Change how the auth data is published into the config yaml --- .github/workflows/build.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ac29e28..8b69c107 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1.0.3 + uses: gradle/wrapper-validation-action@v1 - name: Install JDK - uses: actions/setup-java@v2.0.0 + uses: actions/setup-java@v2 with: distribution: 'adopt' java-version: '8' @@ -19,7 +19,7 @@ jobs: run: ./gradlew check --stacktrace - name: Upload lint and test reports if: always() - uses: actions/upload-artifact@v2.2.3 + uses: actions/upload-artifact@v2 with: name: execution-reports path: | @@ -33,16 +33,16 @@ jobs: api-level: [ 21, 23, 26, 29, 30 ] steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1.0.3 + uses: gradle/wrapper-validation-action@v1 - name: Install JDK - uses: actions/setup-java@v2.0.0 + uses: actions/setup-java@v2 with: distribution: 'adopt' java-version: '8' - name: Emulator tests - uses: reactivecircus/android-emulator-runner@v2.15.0 + uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} target: google_apis @@ -51,7 +51,7 @@ jobs: script: ./gradlew connectedCheck --stacktrace - name: Upload instrumented test reports if: always() - uses: actions/upload-artifact@v2.2.3 + uses: actions/upload-artifact@v2 with: name: test-reports path: | @@ -60,22 +60,22 @@ jobs: upload-snapshots: runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' needs: - check - test steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1.0.3 + uses: gradle/wrapper-validation-action@v1 - name: Install JDK - uses: actions/setup-java@v2.0.0 + uses: actions/setup-java@v2 with: distribution: 'adopt' java-version: '8' - name: Upload snapshots run: ./gradlew uploadArchives --stacktrace env: - SONATYPE_NEXUS_USERNAME: ${{ secrets.SonatypeUsername }} - SONATYPE_NEXUS_PASSWORD: ${{ secrets.SonatypePassword }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SonatypeUsername }} + ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SonatypePassword }} From 4a1fa72dc3e1c5205695524c7e775b5c7f01d51e Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Wed, 11 Aug 2021 18:29:55 -0700 Subject: [PATCH 020/193] init --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b69c107..55f7f286 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: runs-on: macOS-latest # enables hardware acceleration in the virtual machine, required for emulator testing strategy: matrix: - api-level: [ 21, 23, 26, 29, 30 ] + api-level: [ 29, 30 ] steps: - name: Checkout uses: actions/checkout@v2 From 9cad23395f01e876d6c327fc1f16191dd79bf5af Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Thu, 12 Aug 2021 14:18:59 -0700 Subject: [PATCH 021/193] Upgrade AndroidX RoboElectric --- .../sdk/android/core/RobolectricTestBase.java | 2 +- .../auth/LegacyUriRedirectHandlerTest.java | 2 +- .../android/core/auth/LoginActivityTest.java | 27 +++++++++---------- .../core/auth/OAuthWebViewClientTest.java | 4 +-- .../android/core/auth/SsoDeeplinkTest.java | 7 ++--- gradle/dependencies.gradle | 4 +-- .../rides/RideRequestActivityTest.java | 9 ++++--- .../android/rides/RobolectricTestBase.java | 2 +- 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/core-android/src/test/java/com/uber/sdk/android/core/RobolectricTestBase.java b/core-android/src/test/java/com/uber/sdk/android/core/RobolectricTestBase.java index be796f8c..99a4e1c4 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/RobolectricTestBase.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/RobolectricTestBase.java @@ -30,7 +30,7 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 21) +@Config(sdk = 21) public abstract class RobolectricTestBase { @Rule diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandlerTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandlerTest.java index 38a77e12..63fea4aa 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandlerTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LegacyUriRedirectHandlerTest.java @@ -216,7 +216,7 @@ private void assertLastLog(String message) { private void assertNoLogs() { List logItemList = ShadowLog.getLogsForTag(UberSdk.UBER_SDK_LOG_TAG); - assertThat(ShadowLog.getLogsForTag(UberSdk.UBER_SDK_LOG_TAG)).isNull(); + assertThat(ShadowLog.getLogsForTag(UberSdk.UBER_SDK_LOG_TAG)).isEmpty(); } private void assertDialogShown() { diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index aa2df67a..295f87cf 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -41,9 +41,9 @@ import org.mockito.Mock; import org.robolectric.Robolectric; import org.robolectric.Shadows; +import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.ShadowActivity; import org.robolectric.shadows.ShadowWebView; -import org.robolectric.util.ActivityController; import java.util.ArrayList; import java.util.Set; @@ -91,7 +91,7 @@ public void setup() { .build(); Intent data = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.TOKEN); - loginActivity = Robolectric.buildActivity(LoginActivity.class).withIntent(data).get(); + loginActivity = Robolectric.buildActivity(LoginActivity.class, data).get(); when(ssoDeeplinkFactory.getSsoDeeplink(any(LoginActivity.class), eq(productPriority), any(SessionConfiguration.class))).thenReturn(ssoDeeplink); @@ -116,8 +116,7 @@ public void onLoginLoad_withEmptyScopes_shouldReturnErrorResultIntent() { Intent intent = new Intent(); intent.putExtra(LoginActivity.EXTRA_SESSION_CONFIGURATION, new SessionConfiguration.Builder().setClientId(CLIENT_ID).build()); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class) - .withIntent(intent) + ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent) .create(); ShadowActivity shadowActivity = shadowOf(controller.get()); @@ -136,7 +135,7 @@ public void onLoginLoad_withNullResponseType_shouldReturnErrorResultIntent() { intent.putExtra(LoginActivity.EXTRA_RESPONSE_TYPE, (ResponseType) null); ActivityController controller = Robolectric.buildActivity(LoginActivity.class) - .withIntent(intent) + .newIntent(intent) .create(); ShadowActivity shadowActivity = shadowOf(controller.get()); @@ -153,7 +152,7 @@ public void onLoginLoad_withSsoEnabled_andSupported_shouldExecuteSsoDeeplink() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, loginConfiguration, ResponseType.TOKEN, false, true, true); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class).withIntent(intent); + ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); loginActivity.ssoDeeplinkFactory = ssoDeeplinkFactory; @@ -169,7 +168,7 @@ public void onLoginLoad_withSsoEnabled_andNotSupported_shouldReturnErrorResultIn Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, loginConfiguration, ResponseType.TOKEN, false, true, true); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class).withIntent(intent); + ActivityController controller = Robolectric.buildActivity(LoginActivity.class).newIntent(intent); loginActivity = controller.get(); loginActivity.ssoDeeplinkFactory = ssoDeeplinkFactory; ShadowActivity shadowActivity = shadowOf(loginActivity); @@ -189,7 +188,7 @@ public void onLoginLoad_withResponseTypeCode_andForceWebview_shouldLoadWebview() Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.CODE, true); - loginActivity = Robolectric.buildActivity(LoginActivity.class).withIntent(intent).create().get(); + loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration); @@ -201,7 +200,7 @@ public void onLoginLoad_withResponseTypeCode_andNotForceWebview_shouldLoadChrome Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.CODE, false); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class).withIntent(intent); + ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); @@ -216,7 +215,7 @@ public void onLoginLoad_withResponseTypeToken_andForceWebview_andGeneralScopes_s Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.TOKEN, true); - loginActivity = Robolectric.buildActivity(LoginActivity.class).withIntent(intent).create().get(); + loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); @@ -228,7 +227,7 @@ public void onLoginLoad_withResponseTypeToken_andNotForceWebview_andGeneralScope Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.TOKEN, false); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class).withIntent(intent); + ActivityController controller = Robolectric.buildActivity(LoginActivity.class).newIntent(intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); @@ -248,7 +247,7 @@ public void onLoginLoad_withResponseTypeToken_andForceWebview_andPrivilegedScope Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.TOKEN, true); - loginActivity = Robolectric.buildActivity(LoginActivity.class).withIntent(intent).create().get(); + loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); @@ -265,7 +264,7 @@ public void onLoginLoad_withResponseTypeToken_andNotForceWebview_andPrivilegedSc Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.TOKEN, false); - ActivityController controller = Robolectric.buildActivity(LoginActivity.class).withIntent(intent); + ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); @@ -285,7 +284,7 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), new ArrayList(), loginConfiguration, ResponseType.TOKEN, true, false, true); - ShadowActivity shadowActivity = shadowOf(Robolectric.buildActivity(LoginActivity.class).withIntent(intent).create().get()); + ShadowActivity shadowActivity = shadowOf(Robolectric.buildActivity(LoginActivity.class, intent).create().get()); final Intent signupDeeplinkIntent = shadowActivity.peekNextStartedActivity(); assertThat(signupDeeplinkIntent.getData().toString()).isEqualTo(SIGNUP_DEEPLINK_URL); diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/OAuthWebViewClientTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/OAuthWebViewClientTest.java index a04736c4..9ebae966 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/OAuthWebViewClientTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/OAuthWebViewClientTest.java @@ -36,8 +36,8 @@ import org.mockito.ArgumentCaptor; import org.robolectric.Robolectric; import org.robolectric.Shadows; +import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.ShadowActivity; -import org.robolectric.util.ActivityController; import java.util.Arrays; import java.util.Collection; @@ -73,7 +73,7 @@ public void onLoadLoginView_withNoRedirectUrl_shouldReturnError() { Intent intent = new Intent(); intent.putExtra(LoginActivity.EXTRA_SESSION_CONFIGURATION, config); final ActivityController controller = Robolectric.buildActivity(LoginActivity.class) - .withIntent(intent); + .newIntent(intent); final ShadowActivity shadowActivity = Shadows.shadowOf(controller.get()); controller.create(); diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/SsoDeeplinkTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/SsoDeeplinkTest.java index 4ad5a378..8277a991 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/SsoDeeplinkTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/SsoDeeplinkTest.java @@ -41,7 +41,7 @@ import org.mockito.Mock; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; -import org.robolectric.res.builder.RobolectricPackageManager; +import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.shadows.ShadowResolveInfo; import java.util.Arrays; @@ -65,6 +65,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; public class SsoDeeplinkTest extends RobolectricTestBase { @@ -82,7 +83,7 @@ public class SsoDeeplinkTest extends RobolectricTestBase { Activity activity; - RobolectricPackageManager packageManager; + protected ShadowPackageManager packageManager; ResolveInfo resolveInfo; @@ -97,7 +98,7 @@ public void setUp() { redirectIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(REDIRECT_URI)); redirectIntent.setPackage(activity.getPackageName()); resolveInfo = ShadowResolveInfo.newResolveInfo("", activity.getPackageName()); - packageManager = RuntimeEnvironment.getRobolectricPackageManager(); + packageManager = shadowOf(RuntimeEnvironment.application.getPackageManager()); packageManager.addResolveInfoForIntent(redirectIntent, resolveInfo); ssoDeeplink = new SsoDeeplink.Builder(activity) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 8b68ee75..09182ae4 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -47,14 +47,14 @@ def misc = [ def support = [ annotations: "androidx.annotation:annotation:${versions.androidxVersion}", appCompat : "androidx.appcompat:appcompat:${versions.androidxVersion}", - chrometabs : "androidx.browser:browser:${versions.androidxVersion}", + chrometabs : "androidx.browser:browser:${versions.androidxVersion}" ] def test = [ androidRunner: "com.android.support.test:runner:${versions.androidTest}", androidRules: "com.android.support.test:rules:${versions.androidTest}", junit: 'junit:junit:4.12', - robolectric: 'org.robolectric:robolectric:3.2.2', + robolectric: 'org.robolectric:robolectric:4.0', assertj: 'org.assertj:assertj-core:1.7.1', mockito: 'org.mockito:mockito-core:1.10.19', guava: 'com.google.guava:guava:23.4-android', diff --git a/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestActivityTest.java b/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestActivityTest.java index 19d7a58d..9ba90842 100644 --- a/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestActivityTest.java +++ b/rides-android/src/test/java/com/uber/sdk/android/rides/RideRequestActivityTest.java @@ -62,7 +62,8 @@ public void setup() { Intent data = RideRequestActivity.newIntent(Robolectric.setupActivity(Activity.class), null, new SessionConfiguration.Builder().setClientId("clientId").build(), null); - activity = Robolectric.buildActivity(RideRequestActivity.class).withIntent(data).create() + activity = Robolectric.buildActivity(RideRequestActivity.class, data) + .create() .get(); } @@ -115,7 +116,7 @@ public void onLoad_whenNullUserAgent_shouldAddRideWidgetUserAgent() { rideParameters, new SessionConfiguration.Builder().setClientId("clientId").build(), null); - activity = Robolectric.buildActivity(RideRequestActivity.class).withIntent(data).create().get(); + activity = Robolectric.buildActivity(RideRequestActivity.class, data).create().get(); String tokenString = "accessToken1234"; AccessToken accessToken = new AccessToken(2592000, ImmutableList.of(Scope.RIDE_WIDGETS), tokenString, @@ -135,7 +136,7 @@ public void onLoad_withUserAgentInRideParametersButton_shouldNotGetOverridden() rideParameters, new SessionConfiguration.Builder().setClientId("clientId").build(), null); - activity = Robolectric.buildActivity(RideRequestActivity.class).withIntent(data).create().get(); + activity = Robolectric.buildActivity(RideRequestActivity.class, data).create().get(); assertEquals(userAgent, activity.rideRequestView.rideParameters.getUserAgent()); } @@ -144,7 +145,7 @@ public void onCreate_withNullRideParameters_shouldCreateDefaultParamsAndLoad() { ShadowActivity shadowActivity = shadowOf(activity); Intent intent = new Intent(); intent.putExtra(RideRequestActivity.EXTRA_LOGIN_CONFIGURATION, new SessionConfiguration.Builder().setClientId("clientId").build()); - activity = Robolectric.buildActivity(RideRequestActivity.class).withIntent(intent).create().get(); + activity = Robolectric.buildActivity(RideRequestActivity.class, intent).create().get(); assertNull(shadowActivity.getResultIntent()); assertFalse(shadowActivity.isFinishing()); } diff --git a/rides-android/src/test/java/com/uber/sdk/android/rides/RobolectricTestBase.java b/rides-android/src/test/java/com/uber/sdk/android/rides/RobolectricTestBase.java index b883522b..dabb5866 100644 --- a/rides-android/src/test/java/com/uber/sdk/android/rides/RobolectricTestBase.java +++ b/rides-android/src/test/java/com/uber/sdk/android/rides/RobolectricTestBase.java @@ -30,6 +30,6 @@ * Base test class to remove use of annotations on all test classes. */ @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 21) +@Config(sdk = 21) public abstract class RobolectricTestBase { } From 701154bf1b516fabf0672ba047827d27bfad83cd Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Thu, 12 Aug 2021 14:22:00 -0700 Subject: [PATCH 022/193] restore workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55f7f286..8b69c107 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: runs-on: macOS-latest # enables hardware acceleration in the virtual machine, required for emulator testing strategy: matrix: - api-level: [ 29, 30 ] + api-level: [ 21, 23, 26, 29, 30 ] steps: - name: Checkout uses: actions/checkout@v2 From 74af3f9b16dfc0ff67800ebdfe865c7580046ac5 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Thu, 12 Aug 2021 14:45:19 -0700 Subject: [PATCH 023/193] Update RideRequestActivity.java Retry --- .../java/com/uber/sdk/android/rides/RideRequestActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java index 19628edc..f2ab1a5b 100644 --- a/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java +++ b/rides-android/src/main/java/com/uber/sdk/android/rides/RideRequestActivity.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; From 2e2bbc832ddd755ac733abc916f985d30563c11d Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Tue, 17 Aug 2021 16:34:05 -0700 Subject: [PATCH 024/193] test --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b69c107..63a76f7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: runs-on: macOS-latest # enables hardware acceleration in the virtual machine, required for emulator testing strategy: matrix: - api-level: [ 21, 23, 26, 29, 30 ] + api-level: [ 21, 23, 26 ] steps: - name: Checkout uses: actions/checkout@v2 From 233abbb9ca1f1a29381c7279f90ee0ec10b82de2 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Thu, 19 Aug 2021 14:24:01 -0700 Subject: [PATCH 025/193] [Gradle Release Plugin] - pre tag commit: 'v0.10.3'. --- CHANGELOG.md | 2 +- gradle.properties | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee246e73..185849f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -v0.10.3 - TBD +v0.10.3 - 08/19/2021 ------------- v0.10.2 - 12/03/2019 diff --git a/gradle.properties b/gradle.properties index 751eb561..d7ce2d91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -#Tue, 03 Dec 2019 14:16:24 -0800 +#Thu, 19 Aug 2021 14:13:46 -0700 GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.3-SNAPSHOT -VERSION_NAME=0.10.3-SNAPSHOT +version=0.10.3 +VERSION_NAME=0.10.3 POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 73b25c399f430f3e4e3e1b87490fb60746859b43 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 25 Apr 2023 01:14:18 -0700 Subject: [PATCH 026/193] Added support for login par flow --- core-android/build.gradle | 5 +- .../uber/sdk/android/core/auth/AuthUtils.java | 9 +- .../sdk/android/core/auth/LoginActivity.java | 188 ++++++++++++++---- .../sdk/android/core/auth/LoginManager.java | 43 +++- .../android/core/auth/LoginPARDispatcher.java | 27 +++ core-android/src/main/res/values/dimens.xml | 1 + gradle/dependencies.gradle | 14 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 8 files changed, 236 insertions(+), 55 deletions(-) create mode 100644 core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java diff --git a/core-android/build.gradle b/core-android/build.gradle index f006e1ae..a7061ea8 100644 --- a/core-android/build.gradle +++ b/core-android/build.gradle @@ -41,8 +41,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } testOptions { @@ -60,6 +60,7 @@ dependencies { implementation deps.support.appCompat implementation deps.support.annotations implementation deps.support.chrometabs + implementation deps.network.moshi testImplementation deps.test.junit testImplementation deps.test.assertj diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java index 9f96f20b..19ac1787 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java @@ -258,10 +258,11 @@ static String createEncodedParam(String rawParam) { static String buildUrl( @NonNull String redirectUri, @NonNull ResponseType responseType, - @NonNull SessionConfiguration configuration) { + @NonNull SessionConfiguration configuration, + @NonNull String requestUri) { final String CLIENT_ID_PARAM = "client_id"; - final String ENDPOINT = "login"; + final String ENDPOINT = "auth"; final String HTTPS = "https"; final String PATH = "oauth/v2/authorize"; final String REDIRECT_PARAM = "redirect_uri"; @@ -269,6 +270,7 @@ static String buildUrl( final String SCOPE_PARAM = "scope"; final String SHOW_FB_PARAM = "show_fb"; final String SIGNUP_PARAMS = "signup_params"; + final String REQUEST_URI_PARAM = "request_uri"; final String REDIRECT_LOGIN = "{\"redirect_to_login\":true}"; @@ -284,6 +286,9 @@ static String buildUrl( .appendQueryParameter(SCOPE_PARAM, getScopes(configuration)) .appendQueryParameter(SHOW_FB_PARAM, "false") .appendQueryParameter(SIGNUP_PARAMS, AuthUtils.createEncodedParam(REDIRECT_LOGIN)); + if (!TextUtils.isEmpty(requestUri)) { + builder.appendQueryParameter(REQUEST_URI_PARAM, requestUri); + } return builder.build().toString(); } diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 6ccdfa1f..42a3ebb2 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -33,11 +33,15 @@ import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsIntent; import android.text.TextUtils; +import android.view.Gravity; +import android.view.ViewGroup; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.LinearLayout; +import android.widget.ProgressBar; import com.uber.sdk.android.core.BuildConfig; import com.uber.sdk.android.core.R; @@ -45,6 +49,8 @@ import com.uber.sdk.android.core.install.SignupDeeplink; import com.uber.sdk.android.core.utils.CustomTabsHelper; import com.uber.sdk.core.client.SessionConfiguration; +import com.uber.sdk.core.client.internal.LoginPARRequest; +import com.uber.sdk.core.client.internal.LoginPARRequestException; import java.util.ArrayList; @@ -62,6 +68,10 @@ public class LoginActivity extends Activity { static final String EXTRA_SSO_ENABLED = "SSO_ENABLED"; static final String EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED = "REDIRECT_TO_PLAY_STORE_ENABLED"; + static final String EXTRA_REQUEST_URI = "REQUEST_URI"; + + static final String EXTRA_PAR_FLOW = "PAR_FLOW"; + static final String ERROR = "error"; private ArrayList productPriority; @@ -78,13 +88,16 @@ public class LoginActivity extends Activity { @VisibleForTesting CustomTabsHelper customTabsHelper = new CustomTabsHelper(); + @VisibleForTesting + LinearLayout progressBarLayoutContainer; + /** * Create an {@link Intent} to pass to this activity * - * @param context the {@link Context} for the intent + * @param context the {@link Context} for the intent * @param sessionConfiguration to be used for gather clientId - * @param responseType that is expected + * @param responseType that is expected * @return an intent that can be passed to this activity */ @NonNull @@ -93,16 +106,16 @@ public static Intent newIntent( @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType) { - return newIntent(context, sessionConfiguration, responseType, false); + return newIntent(context, sessionConfiguration, responseType, ""); } /** * Create an {@link Intent} to pass to this activity * - * @param context the {@link Context} for the intent + * @param context the {@link Context} for the intent * @param sessionConfiguration to be used for gather clientId - * @param responseType that is expected - * @param forceWebview Forced to use old webview instead of chrometabs + * @param responseType that is expected + * @param isParFlow specifies whether to send user's profile info to Uber as part of login/signup flow * @return an intent that can be passed to this activity */ @NonNull @@ -110,20 +123,54 @@ public static Intent newIntent( @NonNull Context context, @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType, - boolean forceWebview) { - - return newIntent(context, new ArrayList(), sessionConfiguration, responseType, forceWebview, false, false); + boolean isParFlow) { + + return newIntent(context, + new ArrayList(), + sessionConfiguration, + responseType, + "", + false, + false, + isParFlow); } /** * Create an {@link Intent} to pass to this activity * - * @param context the {@link Context} for the intent - * @param productPriority dictates the order of which Uber applications should be used for SSO. + * @param context the {@link Context} for the intent * @param sessionConfiguration to be used for gather clientId - * @param responseType that is expected - * @param forceWebview Forced to use old webview instead of chrometabs - * @param isSsoEnabled specifies whether to attempt login with SSO + * @param responseType that is expected + * @param requestUri to be used to prefill user's profile hint information + * @return an intent that can be passed to this activity + */ + @NonNull + public static Intent newIntent( + @NonNull Context context, + @NonNull SessionConfiguration sessionConfiguration, + @NonNull ResponseType responseType, + @NonNull String requestUri) { + + return newIntent(context, + new ArrayList(), + sessionConfiguration, + responseType, + requestUri, + false, + false, + false); + } + + /** + * Create an {@link Intent} to pass to this activity + * + * @param context the {@link Context} for the intent + * @param productPriority dictates the order of which Uber applications should be used for SSO. + * @param sessionConfiguration to be used for gather clientId + * @param responseType that is expected + * @param isSsoEnabled specifies whether to attempt login with SSO + * @param isRedirectToPlayStoreEnabled specifies whether to redirect to Play Store if Uber app is not installed + * @param isParFlow specifies whether to send user's profile info to Uber as part of login/signup flow * @return an intent that can be passed to this activity */ @NonNull @@ -132,17 +179,19 @@ static Intent newIntent( @NonNull ArrayList productPriority, @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType, - boolean forceWebview, + String requestUri, boolean isSsoEnabled, - boolean isRedirectToPlayStoreEnabled) { + boolean isRedirectToPlayStoreEnabled, + boolean isParFlow) { final Intent data = new Intent(context, LoginActivity.class) .putExtra(EXTRA_PRODUCT_PRIORITY, productPriority) .putExtra(EXTRA_SESSION_CONFIGURATION, sessionConfiguration) + .putExtra(EXTRA_REQUEST_URI, requestUri) .putExtra(EXTRA_RESPONSE_TYPE, responseType) - .putExtra(EXTRA_FORCE_WEBVIEW, forceWebview) .putExtra(EXTRA_SSO_ENABLED, isSsoEnabled) - .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled); + .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled) + .putExtra(EXTRA_PAR_FLOW, isParFlow); return data; } @@ -171,8 +220,8 @@ protected void onCreate(Bundle savedInstanceState) { protected void onResume() { super.onResume(); - if(webView == null) { - if(!authStarted) { + if (webView == null) { + if (!authStarted) { authStarted = true; return; } @@ -190,10 +239,21 @@ protected void onNewIntent(Intent intent) { } protected void init() { - if(getIntent().getData() != null) { + if (getIntent().getData() != null) { handleResponse(getIntent().getData()); } else { - loadUrl(); + sessionConfiguration = (SessionConfiguration) getIntent().getSerializableExtra(EXTRA_SESSION_CONFIGURATION); + responseType = (ResponseType) getIntent().getSerializableExtra(EXTRA_RESPONSE_TYPE); + if (isParFlow(getIntent())) { + addProgressIndicator(); + LoginPARDispatcher.dispatchPAR( + sessionConfiguration, + responseType, + new LoginPARCallback(responseType) + ); + } else { + loadUrl(); + } } } @@ -228,25 +288,21 @@ protected void loadUrl() { return; } - boolean forceWebview = intent.getBooleanExtra(EXTRA_FORCE_WEBVIEW, false); + String requestUri = intent.getStringExtra(EXTRA_REQUEST_URI); boolean isRedirectToPlayStoreEnabled = intent.getBooleanExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, false); if (responseType == ResponseType.CODE) { - loadWebPage(redirectUri, ResponseType.CODE, sessionConfiguration, forceWebview); + loadWebPage(redirectUri, ResponseType.CODE, sessionConfiguration, requestUri); } else if (responseType == ResponseType.TOKEN && !(AuthUtils.isPrivilegeScopeRequired(sessionConfiguration.getScopes()) && isRedirectToPlayStoreEnabled)) { - loadWebPage(redirectUri, ResponseType.TOKEN, sessionConfiguration, forceWebview); + loadWebPage(redirectUri, ResponseType.TOKEN, sessionConfiguration, requestUri); } else { redirectToInstallApp(this); } } - protected void loadWebPage(String redirectUri, ResponseType responseType, SessionConfiguration sessionConfiguration, boolean forceWebview) { - String url = AuthUtils.buildUrl(redirectUri, responseType, sessionConfiguration); - if (forceWebview) { - loadWebview(url, redirectUri); - } else { - loadChrometab(url); - } + protected void loadWebPage(String redirectUri, ResponseType responseType, SessionConfiguration sessionConfiguration, String requestUri) { + String url = AuthUtils.buildUrl(redirectUri, responseType, sessionConfiguration, requestUri); + loadChrometab(url); } /** @@ -347,7 +403,7 @@ private boolean validateRequestParams() { } if ((sessionConfiguration.getScopes() == null || sessionConfiguration.getScopes().isEmpty()) - && (sessionConfiguration.getCustomScopes() == null || sessionConfiguration.getCustomScopes().isEmpty())) { + && (sessionConfiguration.getCustomScopes() == null || sessionConfiguration.getCustomScopes().isEmpty())) { onError(AuthenticationError.INVALID_SCOPE); return false; } @@ -364,6 +420,42 @@ private void redirectToInstallApp(@NonNull Activity activity) { new SignupDeeplink(activity, sessionConfiguration.getClientId(), USER_AGENT).execute(); } + private boolean isParFlow(Intent intent) { + return intent.getBooleanExtra(EXTRA_PAR_FLOW, false); + } + + private void addProgressIndicator() { + progressBarLayoutContainer = new LinearLayout(this); + progressBarLayoutContainer.setGravity(Gravity.CENTER); + progressBarLayoutContainer.setLayoutParams( + new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT + ) + ); + ProgressBar progressBar = new ProgressBar(this); + progressBar.setLayoutParams( + new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + (int) getResources().getDimension(R.dimen.ub__progress_bar_height) + ) + ); + ViewGroup rootLayout = findViewById(android.R.id.content); + progressBarLayoutContainer.addView(progressBar); + rootLayout.addView(progressBarLayoutContainer); + } + + private void removeProgressBar() { + ViewGroup rootLayout = findViewById(android.R.id.content); + if (progressBarLayoutContainer.getParent() != null) { + ((ViewGroup) progressBarLayoutContainer.getParent()).removeView(progressBarLayoutContainer); + } + } + + private void loginInternal(String requestUri) { + getIntent().putExtra(EXTRA_REQUEST_URI, requestUri); + loadUrl(); + } + /** * Custom {@link WebViewClient} for authorization. */ @@ -384,6 +476,7 @@ public OAuthWebViewClient(@NonNull String redirectUri) { /** * add deprecated member "onReceivedError" to solve compatibility issue when API level < 23 + * * @param view * @param errorCode * @param description @@ -407,7 +500,7 @@ public void onReceivedHttpError(WebView view, WebResourceRequest request, WebRes receivedError(); } - private void receivedError(){ + private void receivedError() { onError(AuthenticationError.CONNECTIVITY_ISSUE); } } @@ -453,4 +546,31 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } } + + class LoginPARCallback implements LoginPARRequest.Callback { + + private final ResponseType responseType; + + LoginPARCallback(ResponseType responseType) { + this.responseType = responseType; + } + + @Override + public void onSuccess(String requestUri) { + removeProgressBar(); + loginInternal(requestUri); + } + + private void removeProgressBar() { + if (progressBarLayoutContainer.getParent() != null) { + ((ViewGroup) progressBarLayoutContainer.getParent()).removeView(progressBarLayoutContainer); + } + } + + @Override + public void onError(LoginPARRequestException e) { + removeProgressBar(); + LoginActivity.this.onError(AuthenticationError.INVALID_FLOW_ERROR); + } + } } diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index ca26daab..9a5e8f32 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -27,24 +27,42 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; + import android.util.Log; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ProgressBar; + +import com.uber.sdk.android.core.R; import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.UberSdk; import com.uber.sdk.android.core.utils.AppProtocol; import com.uber.sdk.core.auth.AccessToken; import com.uber.sdk.core.auth.AccessTokenStorage; import com.uber.sdk.core.auth.Scope; +import com.uber.sdk.core.auth.internal.OAuth2Service; +import com.uber.sdk.core.auth.internal.ProfileHint; import com.uber.sdk.core.client.AccessTokenSession; import com.uber.sdk.core.client.ServerTokenSession; import com.uber.sdk.core.client.Session; import com.uber.sdk.core.client.SessionConfiguration; +import com.uber.sdk.core.client.internal.LoginPARRequest; +import com.uber.sdk.core.client.internal.LoginPARRequestException; + +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; +import java.util.concurrent.Executors; import static com.uber.sdk.android.core.utils.Preconditions.checkState; import static com.uber.sdk.core.client.utils.Preconditions.checkNotNull; +import retrofit2.Retrofit; +import retrofit2.converter.moshi.MoshiConverterFactory; + /** * Manages user login via OAuth 2.0 Implicit Grant. Be sure to call * {@link LoginManager#onActivityResult(Activity, int, int, Intent)} in your @@ -100,6 +118,8 @@ public class LoginManager { @Deprecated private boolean redirectForAuthorizationCode = false; + private LinearLayout progressBarLayoutContainer; + /** * @param accessTokenStorage to store access token. * @param loginCallback callback to be called when {@link LoginManager#onActivityResult(Activity, int, int, Intent)} @@ -182,9 +202,10 @@ public void login(final @NonNull Activity activity) { productFlowPriority, sessionConfiguration, ResponseType.TOKEN, - false, + "", + true, true, - true); + false); activity.startActivityForResult(intent, requestCode); } else if (ssoDeeplink.isSupported(SsoDeeplink.FlowVersion.DEFAULT)) { ssoDeeplink.execute(SsoDeeplink.FlowVersion.DEFAULT); @@ -206,10 +227,7 @@ public void loginForImplicitGrant(@NonNull Activity activity) { if (!legacyUriRedirectHandler.checkValidState(activity, this)) { return; } - - Intent intent = LoginActivity.newIntent(activity, sessionConfiguration, - ResponseType.TOKEN, legacyUriRedirectHandler.isLegacyMode()); - activity.startActivityForResult(intent, requestCode); + executePARRequestIfNecessary(activity, ResponseType.TOKEN); } /** @@ -222,9 +240,7 @@ public void loginForAuthorizationCode(@NonNull Activity activity) { return; } - Intent intent = LoginActivity.newIntent(activity, sessionConfiguration, - ResponseType.CODE, legacyUriRedirectHandler.isLegacyMode()); - activity.startActivityForResult(intent, requestCode); + executePARRequestIfNecessary(activity, ResponseType.CODE); } /** @@ -238,12 +254,17 @@ private void loginForImplicitGrantWithFallback(@NonNull Activity activity) { return; } + executePARRequestIfNecessary(activity, ResponseType.CODE); + } + + private void executePARRequestIfNecessary(Activity activity, ResponseType responseType) { Intent intent = LoginActivity.newIntent( activity, - new ArrayList(), + productFlowPriority, sessionConfiguration, ResponseType.TOKEN, - legacyUriRedirectHandler.isLegacyMode(), + "", + false, false, true); activity.startActivityForResult(intent, requestCode); diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java new file mode 100644 index 00000000..4ae2a002 --- /dev/null +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java @@ -0,0 +1,27 @@ +package com.uber.sdk.android.core.auth; + +import com.uber.sdk.core.auth.internal.OAuth2Service; +import com.uber.sdk.core.client.SessionConfiguration; +import com.uber.sdk.core.client.internal.LoginPARRequest; + +import retrofit2.Retrofit; +import retrofit2.converter.moshi.MoshiConverterFactory; + +public class LoginPARDispatcher { + + public static void dispatchPAR(SessionConfiguration sessionConfiguration, + ResponseType responseType, + LoginActivity.LoginPARCallback callback) { + new LoginPARRequest( + new Retrofit.Builder() + .baseUrl(sessionConfiguration.getLoginHost()) + .addConverterFactory(MoshiConverterFactory.create()) + .build() + .create(OAuth2Service.class), + sessionConfiguration.getProfileHint(), + sessionConfiguration.getClientId(), + responseType.name(), + callback + ).executePAR(); + } +} diff --git a/core-android/src/main/res/values/dimens.xml b/core-android/src/main/res/values/dimens.xml index 121acb14..435edd3b 100644 --- a/core-android/src/main/res/values/dimens.xml +++ b/core-android/src/main/res/values/dimens.xml @@ -28,4 +28,5 @@ 8dp 48dp + 32dp diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 09182ae4..10633d09 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -17,7 +17,11 @@ def versions = [ androidTest: '0.5', androidxVersion: '1.0.0', - uberJava: '0.8.0', + uberJava: '0.8.1-SNAPSHOT', +] + +def network = [ + moshi: 'com.squareup.moshi:moshi:1.9.2', ] def build = [ @@ -25,7 +29,7 @@ def build = [ buildToolsVersion: '28.0.2', compileSdkVersion: 28, ci: 'true' == System.getenv('CI'), - minSdkVersion: 15, + minSdkVersion: 16, targetSdkVersion: 28, repositories: [ @@ -33,10 +37,11 @@ def build = [ ], gradlePlugins: [ - android: 'com.android.tools.build:gradle:3.2.0', + android: 'com.android.tools.build:gradle:3.3.2', release: 'net.researchgate:gradle-release:2.1.2', github: 'co.riiid:gradle-github-plugin:0.4.2', cobertura: 'net.saliman:gradle-cobertura-plugin:2.3.1', + buildConfig: 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.8' ] ] @@ -72,5 +77,6 @@ ext.deps = [ "support": support, "test": test, "versions": versions, - "uber": uber + "uber": uber, + "network": network ] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bd24854f..8b7001e6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip +zipStoreBase=GRADLE_USER_HOME From 0d94f02f1013b684c461f2dea2e811879681cc0a Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 25 Apr 2023 01:41:33 -0700 Subject: [PATCH 027/193] changed executeParIfNeccessary method name to launchOnboardingFlow --- .../sdk/android/core/auth/LoginManager.java | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index 9a5e8f32..cf3a9946 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -29,40 +29,26 @@ import androidx.annotation.VisibleForTesting; import android.util.Log; -import android.view.Gravity; -import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.LinearLayout; -import android.widget.ProgressBar; -import com.uber.sdk.android.core.R; import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.UberSdk; import com.uber.sdk.android.core.utils.AppProtocol; import com.uber.sdk.core.auth.AccessToken; import com.uber.sdk.core.auth.AccessTokenStorage; import com.uber.sdk.core.auth.Scope; -import com.uber.sdk.core.auth.internal.OAuth2Service; -import com.uber.sdk.core.auth.internal.ProfileHint; import com.uber.sdk.core.client.AccessTokenSession; import com.uber.sdk.core.client.ServerTokenSession; import com.uber.sdk.core.client.Session; import com.uber.sdk.core.client.SessionConfiguration; -import com.uber.sdk.core.client.internal.LoginPARRequest; -import com.uber.sdk.core.client.internal.LoginPARRequestException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; -import java.util.concurrent.Executors; import static com.uber.sdk.android.core.utils.Preconditions.checkState; import static com.uber.sdk.core.client.utils.Preconditions.checkNotNull; -import retrofit2.Retrofit; -import retrofit2.converter.moshi.MoshiConverterFactory; - /** * Manages user login via OAuth 2.0 Implicit Grant. Be sure to call * {@link LoginManager#onActivityResult(Activity, int, int, Intent)} in your @@ -227,7 +213,7 @@ public void loginForImplicitGrant(@NonNull Activity activity) { if (!legacyUriRedirectHandler.checkValidState(activity, this)) { return; } - executePARRequestIfNecessary(activity, ResponseType.TOKEN); + launchOnboardingFlow(activity, ResponseType.TOKEN); } /** @@ -240,7 +226,7 @@ public void loginForAuthorizationCode(@NonNull Activity activity) { return; } - executePARRequestIfNecessary(activity, ResponseType.CODE); + launchOnboardingFlow(activity, ResponseType.CODE); } /** @@ -254,10 +240,10 @@ private void loginForImplicitGrantWithFallback(@NonNull Activity activity) { return; } - executePARRequestIfNecessary(activity, ResponseType.CODE); + launchOnboardingFlow(activity, ResponseType.CODE); } - private void executePARRequestIfNecessary(Activity activity, ResponseType responseType) { + private void launchOnboardingFlow(Activity activity, ResponseType responseType) { Intent intent = LoginActivity.newIntent( activity, productFlowPriority, From 04b4a317c99b18fb375d8768dd3ffbd3b0092cb4 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 9 May 2023 15:29:07 -0700 Subject: [PATCH 028/193] updated rides java sdk version and fixed unit tests --- build.gradle | 2 +- core-android/build.gradle | 3 +- .../sdk/android/core/auth/LoginActivity.java | 6 +- .../sdk/android/core/auth/LoginManager.java | 15 +++-- .../android/core/auth/LoginPARDispatcher.java | 6 +- .../sdk/android/core/auth/AuthUtilsTest.java | 4 +- .../android/core/auth/LoginActivityTest.java | 65 ++++--------------- .../android/core/auth/LoginManagerTest.java | 16 ++--- gradle/dependencies.gradle | 2 +- gradle/verification.gradle | 4 +- rides-android/build.gradle | 2 +- samples/login-sample/build.gradle | 2 +- .../android/samples/LoginSampleActivity.java | 11 +++- samples/request-button-sample/build.gradle | 2 +- 14 files changed, 52 insertions(+), 88 deletions(-) diff --git a/build.gradle b/build.gradle index afc8b43f..5a459617 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() maven { url deps.build.repositories.plugins } } dependencies { diff --git a/core-android/build.gradle b/core-android/build.gradle index a7061ea8..3a7bc3c5 100644 --- a/core-android/build.gradle +++ b/core-android/build.gradle @@ -17,7 +17,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() maven { url deps.build.repositories.plugins } } @@ -66,6 +66,7 @@ dependencies { testImplementation deps.test.assertj testImplementation deps.test.mockito testImplementation deps.test.robolectric + testImplementation project(path: ':core-android') } apply from: rootProject.file('gradle/gradle-mvn-push.gradle') \ No newline at end of file diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 42a3ebb2..bd112beb 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -49,8 +49,8 @@ import com.uber.sdk.android.core.install.SignupDeeplink; import com.uber.sdk.android.core.utils.CustomTabsHelper; import com.uber.sdk.core.client.SessionConfiguration; -import com.uber.sdk.core.client.internal.LoginPARRequest; import com.uber.sdk.core.client.internal.LoginPARRequestException; +import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; import java.util.ArrayList; @@ -266,8 +266,6 @@ protected void onDestroy() { protected void loadUrl() { Intent intent = getIntent(); - sessionConfiguration = (SessionConfiguration) intent.getSerializableExtra(EXTRA_SESSION_CONFIGURATION); - responseType = (ResponseType) intent.getSerializableExtra(EXTRA_RESPONSE_TYPE); productPriority = (ArrayList) intent.getSerializableExtra(EXTRA_PRODUCT_PRIORITY); if (!validateRequestParams()) { @@ -547,7 +545,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } } - class LoginPARCallback implements LoginPARRequest.Callback { + class LoginPARCallback implements LoginPushedAuthorizationRequest.Callback { private final ResponseType responseType; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index cf3a9946..dbeca7fc 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -175,7 +175,6 @@ public void login(final @NonNull Activity activity) { checkState(hasScopes, "Scopes must be set in the Session Configuration."); checkNotNull(sessionConfiguration.getRedirectUri(), "Redirect URI must be set in Session Configuration."); - if (!legacyUriRedirectHandler.checkValidState(activity, this)) { return; } @@ -213,7 +212,7 @@ public void loginForImplicitGrant(@NonNull Activity activity) { if (!legacyUriRedirectHandler.checkValidState(activity, this)) { return; } - launchOnboardingFlow(activity, ResponseType.TOKEN); + launchOnboardingFlow(activity, ResponseType.TOKEN, false); } /** @@ -226,7 +225,7 @@ public void loginForAuthorizationCode(@NonNull Activity activity) { return; } - launchOnboardingFlow(activity, ResponseType.CODE); + launchOnboardingFlow(activity, ResponseType.CODE, false); } /** @@ -240,18 +239,20 @@ private void loginForImplicitGrantWithFallback(@NonNull Activity activity) { return; } - launchOnboardingFlow(activity, ResponseType.CODE); + launchOnboardingFlow(activity, ResponseType.TOKEN, true); } - private void launchOnboardingFlow(Activity activity, ResponseType responseType) { + private void launchOnboardingFlow(Activity activity, + ResponseType responseType, + boolean isRedirectToPlayStoreEnabled) { Intent intent = LoginActivity.newIntent( activity, productFlowPriority, sessionConfiguration, - ResponseType.TOKEN, + responseType, "", false, - false, + isRedirectToPlayStoreEnabled, true); activity.startActivityForResult(intent, requestCode); } diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java index 4ae2a002..0f71e806 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java @@ -2,7 +2,7 @@ import com.uber.sdk.core.auth.internal.OAuth2Service; import com.uber.sdk.core.client.SessionConfiguration; -import com.uber.sdk.core.client.internal.LoginPARRequest; +import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; import retrofit2.Retrofit; import retrofit2.converter.moshi.MoshiConverterFactory; @@ -12,7 +12,7 @@ public class LoginPARDispatcher { public static void dispatchPAR(SessionConfiguration sessionConfiguration, ResponseType responseType, LoginActivity.LoginPARCallback callback) { - new LoginPARRequest( + new LoginPushedAuthorizationRequest( new Retrofit.Builder() .baseUrl(sessionConfiguration.getLoginHost()) .addConverterFactory(MoshiConverterFactory.create()) @@ -22,6 +22,6 @@ public static void dispatchPAR(SessionConfiguration sessionConfiguration, sessionConfiguration.getClientId(), responseType.name(), callback - ).executePAR(); + ).execute(); } } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java index 0f68f4a8..068b987e 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java @@ -286,8 +286,8 @@ public void onBuildUrl_withDefaultRegion_shouldHaveDefaultUberDomain() { .setClientId(clientId) .build(); - String url = AuthUtils.buildUrl(redirectUri, ResponseType.TOKEN, loginConfiguration); - assertEquals("https://login.uber.com/oauth/v2/authorize?client_id=" + clientId + + String url = AuthUtils.buildUrl(redirectUri, ResponseType.TOKEN, loginConfiguration, ""); + assertEquals("https://auth.uber.com/oauth/v2/authorize?client_id=" + clientId + "&redirect_uri=" + redirectUri + "&response_type=token&scope=history&" + "show_fb=false&signup_params=eyJyZWRpcmVjdF90b19sb2dpbiI6dHJ1ZX0%3D%0A", url); } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index 295f87cf..574b96ce 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -150,7 +150,7 @@ public void onLoginLoad_withNullResponseType_shouldReturnErrorResultIntent() { @Test public void onLoginLoad_withSsoEnabled_andSupported_shouldExecuteSsoDeeplink() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, - loginConfiguration, ResponseType.TOKEN, false, true, true); + loginConfiguration, ResponseType.TOKEN, "", true, true, false); ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); @@ -166,7 +166,7 @@ public void onLoginLoad_withSsoEnabled_andSupported_shouldExecuteSsoDeeplink() { @Test public void onLoginLoad_withSsoEnabled_andNotSupported_shouldReturnErrorResultIntent() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, - loginConfiguration, ResponseType.TOKEN, false, true, true); + loginConfiguration, ResponseType.TOKEN, "", true, true, false); ActivityController controller = Robolectric.buildActivity(LoginActivity.class).newIntent(intent); loginActivity = controller.get(); @@ -184,92 +184,51 @@ public void onLoginLoad_withSsoEnabled_andNotSupported_shouldReturnErrorResultIn } @Test - public void onLoginLoad_withResponseTypeCode_andForceWebview_shouldLoadWebview() { + public void onLoginLoad_withResponseTypeCode_shouldLoadChrometab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.CODE, true); - - loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); - ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); - - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration); - assertThat(webview.getLastLoadedUrl()).isEqualTo(expectedUrl); - } - - @Test - public void onLoginLoad_withResponseTypeCode_andNotForceWebview_shouldLoadChrometab() { - Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.CODE, false); + ResponseType.CODE); ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); } @Test - public void onLoginLoad_withResponseTypeToken_andForceWebview_andGeneralScopes_shouldLoadWebview() { - Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.TOKEN, true); - - loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); - ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); - - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); - assertThat(webview.getLastLoadedUrl()).isEqualTo(expectedUrl); - } - - @Test - public void onLoginLoad_withResponseTypeToken_andNotForceWebview_andGeneralScopes_shouldLoadChrometab() { + public void onLoginLoad_withResponseTypeToken_andGeneralScopes_shouldLoadChrometab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.TOKEN, false); + ResponseType.TOKEN); ActivityController controller = Robolectric.buildActivity(LoginActivity.class).newIntent(intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration, ""); verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); } @Test - public void onLoginLoad_withResponseTypeToken_andForceWebview_andPrivilegedScopes_andRedirectToPlayStoreDisabled_shouldLoadWebview() { - loginConfiguration = new SessionConfiguration.Builder() - .setClientId(CLIENT_ID) - .setRedirectUri(REDIRECT_URI) - .setScopes(MIXED_SCOPES) - .build(); - Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.TOKEN, true); - - loginActivity = Robolectric.buildActivity(LoginActivity.class, intent).create().get(); - ShadowWebView webview = Shadows.shadowOf(loginActivity.webView); - - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); - assertThat(webview.getLastLoadedUrl()).isEqualTo(expectedUrl); - } - - @Test - public void onLoginLoad_withResponseTypeToken_andNotForceWebview_andPrivilegedScopes_andRedirectToPlayStoreDisabled_shouldLoadChrometab() { + public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToPlayStoreDisabled_shouldLoadChrometab() { loginConfiguration = new SessionConfiguration.Builder() .setClientId(CLIENT_ID) .setRedirectUri(REDIRECT_URI) .setScopes(MIXED_SCOPES) .build(); Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.TOKEN, false); + ResponseType.TOKEN); ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); loginActivity.customTabsHelper = customTabsHelper; controller.create(); - String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.TOKEN, loginConfiguration, ""); verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); } @@ -282,7 +241,7 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP .setScopes(MIXED_SCOPES) .build(); Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), - new ArrayList(), loginConfiguration, ResponseType.TOKEN, true, false, true); + new ArrayList(), loginConfiguration, ResponseType.TOKEN, "", false, true, false); ShadowActivity shadowActivity = shadowOf(Robolectric.buildActivity(LoginActivity.class, intent).create().get()); diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java index 66063414..94b1d76f 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java @@ -157,7 +157,7 @@ public void login_withRedirectToSdkFlowSsoSupported_shouldLoginActivityWithSsoPa final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList<>(productPriority), sessionConfiguration, - ResponseType.TOKEN, false, true, true); + ResponseType.TOKEN, true, true); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -176,7 +176,7 @@ public void login_withoutAppPriority_shouldLoginActivityWithSsoParams() { final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.TOKEN, false, true, true); + ResponseType.TOKEN, true, true); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -206,7 +206,7 @@ public void login_withSsoNotSupported_andAuthCodeFlowEnabled_shouldLoginWithAuth final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.CODE, false, false, false); + ResponseType.CODE, false, false); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -224,7 +224,7 @@ public void login_withSsoNotSupported_andAuthCodeFlowDisabled_shouldLoginWithImp final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.TOKEN, false, false, true); + ResponseType.TOKEN, false, true); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -247,7 +247,7 @@ public void loginForImplicitGrant_withoutLegacyModeBlocking_shouldLoginWithImpli final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.TOKEN, false, false, false); + ResponseType.TOKEN, false, false); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -270,7 +270,7 @@ public void loginForAuthorizationCode_withoutLegacyModeBlocking_shouldLoginWithA final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.CODE, false, false, false); + ResponseType.CODE, false, false); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -301,7 +301,7 @@ public void login_whenOnlyCustomScopes_shouldLogin() { final Intent resultIntent = intentCaptor.getValue(); validateLoginIntentFields(resultIntent, new ArrayList(), sessionConfiguration, - ResponseType.CODE, false, false, false); + ResponseType.CODE, false, false); assertThat(codeCaptor.getValue()).isEqualTo(REQUEST_CODE_LOGIN_DEFAULT); } @@ -516,14 +516,12 @@ private void validateLoginIntentFields( @NonNull List expectedProductPriority, @NonNull SessionConfiguration expectedSessionConfiguration, @NonNull ResponseType expectedResponseType, - boolean expectedForceWebview, boolean expectedSsoEnabled, boolean expectedRedirectToPlayStoreEnabled) { assertThat(loginIntent.getSerializableExtra(EXTRA_SESSION_CONFIGURATION)).isEqualTo(expectedSessionConfiguration); assertThat(loginIntent.getSerializableExtra(EXTRA_RESPONSE_TYPE)).isEqualTo(expectedResponseType); assertThat((ArrayList) loginIntent.getSerializableExtra(EXTRA_PRODUCT_PRIORITY)) .containsAll(expectedProductPriority); - assertThat(loginIntent.getBooleanExtra(EXTRA_FORCE_WEBVIEW, false)).isEqualTo(expectedForceWebview); assertThat(loginIntent.getBooleanExtra(EXTRA_SSO_ENABLED, false)).isEqualTo(expectedSsoEnabled); assertThat(loginIntent.getBooleanExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, false)) .isEqualTo(expectedRedirectToPlayStoreEnabled); diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 10633d09..e73eb45d 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -17,7 +17,7 @@ def versions = [ androidTest: '0.5', androidxVersion: '1.0.0', - uberJava: '0.8.1-SNAPSHOT', + uberJava: '0.8.1', ] def network = [ diff --git a/gradle/verification.gradle b/gradle/verification.gradle index 39c9b5dc..c712c3fa 100644 --- a/gradle/verification.gradle +++ b/gradle/verification.gradle @@ -2,13 +2,13 @@ subprojects { buildscript { repositories { google() - jcenter() + mavenCentral() } } repositories { google() - jcenter() + mavenCentral() maven { url 'https://maven.google.com' } diff --git a/rides-android/build.gradle b/rides-android/build.gradle index 17fa454f..99267c46 100644 --- a/rides-android/build.gradle +++ b/rides-android/build.gradle @@ -17,7 +17,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() maven { url deps.build.repositories.plugins } } diff --git a/samples/login-sample/build.gradle b/samples/login-sample/build.gradle index 92ab2e3e..7463dfc6 100644 --- a/samples/login-sample/build.gradle +++ b/samples/login-sample/build.gradle @@ -18,7 +18,7 @@ buildscript { apply from: rootProject.file('gradle/dependencies.gradle') repositories { google() - jcenter() + mavenCentral() } dependencies { diff --git a/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java b/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java index 9df846c5..ac28664a 100644 --- a/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java +++ b/samples/login-sample/src/main/java/com/uber/sdk/android/samples/LoginSampleActivity.java @@ -45,6 +45,7 @@ import com.uber.sdk.android.rides.samples.R; import com.uber.sdk.core.auth.AccessToken; import com.uber.sdk.core.auth.AccessTokenStorage; +import com.uber.sdk.core.auth.ProfileHint; import com.uber.sdk.core.auth.Scope; import com.uber.sdk.core.client.Session; import com.uber.sdk.core.client.SessionConfiguration; @@ -84,17 +85,23 @@ public class LoginSampleActivity extends AppCompatActivity { private Button customButton; private AccessTokenStorage accessTokenStorage; private LoginManager loginManager; - private SessionConfiguration configuration; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); - configuration = new SessionConfiguration.Builder() + SessionConfiguration configuration = new SessionConfiguration.Builder() .setClientId(CLIENT_ID) .setRedirectUri(REDIRECT_URI) .setScopes(Arrays.asList(Scope.PROFILE, Scope.RIDE_WIDGETS)) + .setProfileHint(new ProfileHint + .Builder() + .email("john@doe.com") + .firstName("John") + .lastName("Doe") + .phone("1234567890") + .build()) .build(); validateConfiguration(configuration); diff --git a/samples/request-button-sample/build.gradle b/samples/request-button-sample/build.gradle index d75ac96a..013c8df0 100644 --- a/samples/request-button-sample/build.gradle +++ b/samples/request-button-sample/build.gradle @@ -18,7 +18,7 @@ buildscript { apply from: rootProject.file('gradle/dependencies.gradle') repositories { google() - jcenter() + mavenCentral() } dependencies { From 1bc2e7e7370fb0a21fa1df2ae32f1d57812c2126 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 9 May 2023 22:33:37 -0700 Subject: [PATCH 029/193] added unit tests --- .../sdk/android/core/auth/LoginActivity.java | 28 +++--- .../sdk/android/core/auth/LoginManager.java | 3 +- .../sdk/android/core/auth/AuthUtilsTest.java | 18 ++++ .../android/core/auth/LoginActivityTest.java | 94 ++++++++++++++++++- .../android/core/auth/LoginManagerTest.java | 20 ++++ 5 files changed, 147 insertions(+), 16 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index bd112beb..839938b1 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -285,7 +285,6 @@ protected void loadUrl() { } return; } - String requestUri = intent.getStringExtra(EXTRA_REQUEST_URI); boolean isRedirectToPlayStoreEnabled = intent.getBooleanExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, false); if (responseType == ResponseType.CODE) { @@ -422,8 +421,14 @@ private boolean isParFlow(Intent intent) { return intent.getBooleanExtra(EXTRA_PAR_FLOW, false); } - private void addProgressIndicator() { + /** + * Adds progress spinner to the top of the activity + */ + @VisibleForTesting + void addProgressIndicator() { progressBarLayoutContainer = new LinearLayout(this); + progressBarLayoutContainer.setId(R.id.progress_spinner); + progressBarLayoutContainer.setGravity(Gravity.CENTER); progressBarLayoutContainer.setLayoutParams( new LinearLayout.LayoutParams( @@ -442,10 +447,13 @@ private void addProgressIndicator() { rootLayout.addView(progressBarLayoutContainer); } - private void removeProgressBar() { - ViewGroup rootLayout = findViewById(android.R.id.content); + /** + * Removes progress spinner from the activity + */ + @VisibleForTesting void removeProgressIndicator() { if (progressBarLayoutContainer.getParent() != null) { ((ViewGroup) progressBarLayoutContainer.getParent()).removeView(progressBarLayoutContainer); + progressBarLayoutContainer = null; } } @@ -555,20 +563,14 @@ class LoginPARCallback implements LoginPushedAuthorizationRequest.Callback { @Override public void onSuccess(String requestUri) { - removeProgressBar(); + removeProgressIndicator(); loginInternal(requestUri); } - private void removeProgressBar() { - if (progressBarLayoutContainer.getParent() != null) { - ((ViewGroup) progressBarLayoutContainer.getParent()).removeView(progressBarLayoutContainer); - } - } - @Override public void onError(LoginPARRequestException e) { - removeProgressBar(); - LoginActivity.this.onError(AuthenticationError.INVALID_FLOW_ERROR); + removeProgressIndicator(); + loginInternal(""); } } } diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index dbeca7fc..c14ac48b 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -234,7 +234,8 @@ public void loginForAuthorizationCode(@NonNull Activity activity) { * * @param activity to start Activity on. */ - private void loginForImplicitGrantWithFallback(@NonNull Activity activity) { + @VisibleForTesting + void loginForImplicitGrantWithFallback(@NonNull Activity activity) { if (!legacyUriRedirectHandler.checkValidState(activity, this)) { return; } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java index 068b987e..524f2624 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/AuthUtilsTest.java @@ -291,4 +291,22 @@ public void onBuildUrl_withDefaultRegion_shouldHaveDefaultUberDomain() { "&redirect_uri=" + redirectUri + "&response_type=token&scope=history&" + "show_fb=false&signup_params=eyJyZWRpcmVjdF90b19sb2dpbiI6dHJ1ZX0%3D%0A", url); } + + @Test + public void onBuildUrl_whenRequestUriIsNotEmpty_shouldHaveRequestUriInQueryParams() { + String clientId = "clientId1234"; + String redirectUri = "localHost1234"; + + SessionConfiguration loginConfiguration = new SessionConfiguration.Builder() + .setRedirectUri(redirectUri) + .setScopes(Arrays.asList(Scope.HISTORY)) + .setClientId(clientId) + .build(); + + String url = AuthUtils.buildUrl(redirectUri, ResponseType.TOKEN, loginConfiguration, "requestUri"); + assertEquals("https://auth.uber.com/oauth/v2/authorize?client_id=" + clientId + + "&redirect_uri=" + redirectUri + "&response_type=token&scope=history&" + + "show_fb=false&signup_params=eyJyZWRpcmVjdF90b19sb2dpbiI6dHJ1ZX0%3D%0A" + + "&request_uri=requestUri", url); + } } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index 574b96ce..c3052c31 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -25,6 +25,8 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; +import android.os.Bundle; +import android.widget.LinearLayout; import androidx.browser.customtabs.CustomTabsIntent; import com.google.common.collect.ImmutableList; @@ -33,17 +35,19 @@ import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.utils.CustomTabsHelper; import com.uber.sdk.core.auth.AccessToken; +import com.uber.sdk.core.auth.ProfileHint; import com.uber.sdk.core.auth.Scope; import com.uber.sdk.core.client.SessionConfiguration; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.robolectric.Robolectric; -import org.robolectric.Shadows; +import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; -import org.robolectric.shadows.ShadowWebView; import java.util.ArrayList; import java.util.Set; @@ -60,6 +64,7 @@ /** * Tests {@link LoginActivity} */ + public class LoginActivityTest extends RobolectricTestBase { private static final String REDIRECT_URI = "localHost1234"; @@ -249,6 +254,78 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP assertThat(signupDeeplinkIntent.getData().toString()).isEqualTo(SIGNUP_DEEPLINK_URL); } + @Test + @Config(shadows = ShadowLoginPARDispatcher.class ) + public void onLoginLoad_whenPARFlowEnabled_shouldAddProgressIndicator_andLoadCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), + loginConfiguration + .newBuilder() + .setProfileHint(new ProfileHint + .Builder() + .firstName("test") + .build()) + .build(), + ResponseType.CODE, true); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + LoginActivity loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, "requestUri"); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + + @Test + @Config(shadows = ShadowLoginPARDispatcherWithError.class ) + public void onLoginLoad_whenPARFlowEnabled_andErrorResponse_shouldAddProgressIndicator_andLoadCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, + ResponseType.CODE, true); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + LoginActivity loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + + @Test + public void onLoginLoad_whenPARFlowEnabled_andProfileHintEmpty_shouldAddProgressIndicator_andLoadCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, + ResponseType.CODE, true); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + verify(loginActivity).addProgressIndicator(); + verify(loginActivity).removeProgressIndicator(); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + + @Test + public void onLoginLoad_whenPARFlowDisabled_shouldNotAddProgressIndicator_andLoadCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, + ResponseType.CODE, false); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + verify(loginActivity, never()).addProgressIndicator(); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + @Test public void onTokenReceived_shouldReturnAccessTokenResult() { String tokenString = "accessToken1234"; @@ -300,6 +377,19 @@ public void onCodeReceived_shouldReturnResultIntentWithCode() { assertThat(shadowActivity.isFinishing()).isTrue(); } + @Test + public void addProgressIndicator_shouldAddProgressIndicator() { + loginActivity.addProgressIndicator(); + assertThat(loginActivity.progressBarLayoutContainer).isNotNull(); + } + + @Test + public void removeProgressIndicator_shouldRemoveProgressIndicator() { + loginActivity.addProgressIndicator(); + loginActivity.removeProgressIndicator(); + assertThat(loginActivity.progressBarLayoutContainer).isNull(); + } + private AuthenticationError getErrorFromIntent(Intent intent) { return AuthenticationError.fromString(intent.getStringExtra(LoginManager.EXTRA_ERROR)); } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java index 94b1d76f..37b0f0af 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginManagerTest.java @@ -511,6 +511,26 @@ public void getSession_withoutAccessTokenOrToken_fails() { loginManager.getSession(); } + @Test + public void loginForAuthorizationCode_shouldEnablePARFlow() { + loginManager.loginForAuthorizationCode(activity); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + + verify(activity).startActivityForResult(intentCaptor.capture(), anyInt()); + Intent value = intentCaptor.getValue(); + assertThat((ResponseType)value.getSerializableExtra(EXTRA_RESPONSE_TYPE)).isEqualTo(ResponseType.CODE); + } + + @Test + public void loginForImplicitGrantWithFallback_shouldEnablePARFlow() { + loginManager.loginForImplicitGrantWithFallback(activity); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + + verify(activity).startActivityForResult(intentCaptor.capture(), anyInt()); + Intent value = intentCaptor.getValue(); + assertThat((ResponseType)value.getSerializableExtra(EXTRA_RESPONSE_TYPE)).isEqualTo(ResponseType.TOKEN); + } + private void validateLoginIntentFields( @NonNull Intent loginIntent, @NonNull List expectedProductPriority, From 547e1ac943dfe13f5242c4cd35faf04ee6bb2741 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 9 May 2023 22:56:09 -0700 Subject: [PATCH 030/193] removed id for progress spineer --- .../main/java/com/uber/sdk/android/core/auth/LoginActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 839938b1..2205b287 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -427,7 +427,6 @@ private boolean isParFlow(Intent intent) { @VisibleForTesting void addProgressIndicator() { progressBarLayoutContainer = new LinearLayout(this); - progressBarLayoutContainer.setId(R.id.progress_spinner); progressBarLayoutContainer.setGravity(Gravity.CENTER); progressBarLayoutContainer.setLayoutParams( From f7f76f373020fe4166d31d05e0c1fbcf24e6bb63 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 9 May 2023 23:06:33 -0700 Subject: [PATCH 031/193] added shadow classes used in unit tests --- .../core/auth/ShadowLoginPARDispatcher.java | 17 +++++++++++++++++ .../auth/ShadowLoginPARDispatcherWithError.java | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java create mode 100644 core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java new file mode 100644 index 00000000..787f36b6 --- /dev/null +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java @@ -0,0 +1,17 @@ +package com.uber.sdk.android.core.auth; + +import com.uber.sdk.core.client.SessionConfiguration; +import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(LoginPARDispatcher.class) +public class ShadowLoginPARDispatcher { + @Implementation + public static void dispatchPAR(SessionConfiguration sessionConfiguration, + ResponseType responseType, + LoginActivity.LoginPARCallback callback) { + callback.onSuccess("requestUri"); + } +} \ No newline at end of file diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java new file mode 100644 index 00000000..afbae944 --- /dev/null +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java @@ -0,0 +1,17 @@ +package com.uber.sdk.android.core.auth; + +import com.uber.sdk.core.client.SessionConfiguration; +import com.uber.sdk.core.client.internal.LoginPARRequestException; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(LoginPARDispatcher.class) +public class ShadowLoginPARDispatcherWithError { + @Implementation + public static void dispatchPAR(SessionConfiguration sessionConfiguration, + ResponseType responseType, + LoginActivity.LoginPARCallback callback) { + callback.onError(null); + } +} \ No newline at end of file From 2a20934d0bbe899d352c02337656c5ed30e79545 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Fri, 19 May 2023 00:45:16 -0700 Subject: [PATCH 032/193] created new methods for newIntent to maintain backward compatibility --- .../sdk/android/core/auth/LoginActivity.java | 113 ++++++++++++------ .../sdk/android/core/auth/LoginManager.java | 11 +- .../android/core/auth/LoginActivityTest.java | 75 +++++++++--- 3 files changed, 142 insertions(+), 57 deletions(-) diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index 2205b287..eb87e99c 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -33,6 +33,7 @@ import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsIntent; import android.text.TextUtils; +import android.util.Log; import android.view.Gravity; import android.view.ViewGroup; import android.webkit.WebResourceError; @@ -48,6 +49,7 @@ import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.install.SignupDeeplink; import com.uber.sdk.android.core.utils.CustomTabsHelper; +import com.uber.sdk.core.auth.ProfileHint; import com.uber.sdk.core.client.SessionConfiguration; import com.uber.sdk.core.client.internal.LoginPARRequestException; import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; @@ -70,8 +72,6 @@ public class LoginActivity extends Activity { static final String EXTRA_REQUEST_URI = "REQUEST_URI"; - static final String EXTRA_PAR_FLOW = "PAR_FLOW"; - static final String ERROR = "error"; private ArrayList productPriority; @@ -112,27 +112,58 @@ public static Intent newIntent( /** * Create an {@link Intent} to pass to this activity * - * @param context the {@link Context} for the intent + * @param context the {@link Context} for the intent * @param sessionConfiguration to be used for gather clientId - * @param responseType that is expected - * @param isParFlow specifies whether to send user's profile info to Uber as part of login/signup flow + * @param responseType that is expected + * @param forceWebview Forced to use old webview instead of chrometabs * @return an intent that can be passed to this activity + * + * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, SessionConfiguration, ResponseType, String)} */ @NonNull + @Deprecated public static Intent newIntent( @NonNull Context context, @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType, - boolean isParFlow) { + boolean forceWebview) { - return newIntent(context, - new ArrayList(), - sessionConfiguration, - responseType, - "", - false, - false, - isParFlow); + return newIntent(context, new ArrayList(), sessionConfiguration, responseType, forceWebview, false, false); + } + + /** + * Create an {@link Intent} to pass to this activity + * + * @param context the {@link Context} for the intent + * @param productPriority dictates the order of which Uber applications should be used for SSO. + * @param sessionConfiguration to be used for gather clientId + * @param responseType that is expected + * @param forceWebview Forced to use old webview instead of chrometabs + * @param isSsoEnabled specifies whether to attempt login with SSO + * @return an intent that can be passed to this activity + * + * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, ArrayList, SessionConfiguration, ResponseType, String, boolean, boolean)} + */ + @NonNull + @Deprecated + static Intent newIntent( + @NonNull Context context, + @NonNull ArrayList productPriority, + @NonNull SessionConfiguration sessionConfiguration, + @NonNull ResponseType responseType, + boolean forceWebview, + boolean isSsoEnabled, + boolean isRedirectToPlayStoreEnabled) { + + final Intent data = new Intent(context, LoginActivity.class) + .putExtra(EXTRA_PRODUCT_PRIORITY, productPriority) + .putExtra(EXTRA_SESSION_CONFIGURATION, sessionConfiguration) + .putExtra(EXTRA_RESPONSE_TYPE, responseType) + .putExtra(EXTRA_FORCE_WEBVIEW, forceWebview) + .putExtra(EXTRA_SSO_ENABLED, isSsoEnabled) + .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled); + + return data; } /** @@ -157,7 +188,6 @@ public static Intent newIntent( responseType, requestUri, false, - false, false); } @@ -170,7 +200,6 @@ public static Intent newIntent( * @param responseType that is expected * @param isSsoEnabled specifies whether to attempt login with SSO * @param isRedirectToPlayStoreEnabled specifies whether to redirect to Play Store if Uber app is not installed - * @param isParFlow specifies whether to send user's profile info to Uber as part of login/signup flow * @return an intent that can be passed to this activity */ @NonNull @@ -181,8 +210,7 @@ static Intent newIntent( @NonNull ResponseType responseType, String requestUri, boolean isSsoEnabled, - boolean isRedirectToPlayStoreEnabled, - boolean isParFlow) { + boolean isRedirectToPlayStoreEnabled) { final Intent data = new Intent(context, LoginActivity.class) .putExtra(EXTRA_PRODUCT_PRIORITY, productPriority) @@ -190,8 +218,7 @@ static Intent newIntent( .putExtra(EXTRA_REQUEST_URI, requestUri) .putExtra(EXTRA_RESPONSE_TYPE, responseType) .putExtra(EXTRA_SSO_ENABLED, isSsoEnabled) - .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled) - .putExtra(EXTRA_PAR_FLOW, isParFlow); + .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled); return data; } @@ -239,24 +266,30 @@ protected void onNewIntent(Intent intent) { } protected void init() { + Log.d("yyyy", "intent is " + getIntent().getData()); if (getIntent().getData() != null) { handleResponse(getIntent().getData()); + } else if (isParFlow(getIntent())) { + handleParFlow(); } else { - sessionConfiguration = (SessionConfiguration) getIntent().getSerializableExtra(EXTRA_SESSION_CONFIGURATION); - responseType = (ResponseType) getIntent().getSerializableExtra(EXTRA_RESPONSE_TYPE); - if (isParFlow(getIntent())) { - addProgressIndicator(); - LoginPARDispatcher.dispatchPAR( - sessionConfiguration, - responseType, - new LoginPARCallback(responseType) - ); - } else { - loadUrl(); - } + loadUrl(); } } + /** + * Initiates Pushed Authorization Request + */ + @VisibleForTesting + void handleParFlow() { + responseType = (ResponseType) getIntent().getSerializableExtra(EXTRA_RESPONSE_TYPE); + addProgressIndicator(); + LoginPARDispatcher.dispatchPAR( + sessionConfiguration, + responseType, + new LoginPARCallback(responseType) + ); + } + @Override protected void onDestroy() { super.onDestroy(); @@ -265,7 +298,8 @@ protected void onDestroy() { protected void loadUrl() { Intent intent = getIntent(); - + sessionConfiguration = (SessionConfiguration) getIntent().getSerializableExtra(EXTRA_SESSION_CONFIGURATION); + responseType = (ResponseType) getIntent().getSerializableExtra(EXTRA_RESPONSE_TYPE); productPriority = (ArrayList) intent.getSerializableExtra(EXTRA_PRODUCT_PRIORITY); if (!validateRequestParams()) { @@ -418,7 +452,18 @@ private void redirectToInstallApp(@NonNull Activity activity) { } private boolean isParFlow(Intent intent) { - return intent.getBooleanExtra(EXTRA_PAR_FLOW, false); + sessionConfiguration = (SessionConfiguration) getIntent().getSerializableExtra(EXTRA_SESSION_CONFIGURATION); + if (sessionConfiguration == null) { + return false; + } + ProfileHint profileHint = sessionConfiguration.getProfileHint(); + return (profileHint != null && + !(TextUtils.isEmpty(profileHint.getEmail()) && + TextUtils.isEmpty(profileHint.getFirstName()) && + TextUtils.isEmpty(profileHint.getLastName()) && + TextUtils.isEmpty(profileHint.getEmail()) + ) + ); } /** diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index c14ac48b..779d5714 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -29,7 +29,6 @@ import androidx.annotation.VisibleForTesting; import android.util.Log; -import android.widget.LinearLayout; import com.uber.sdk.android.core.SupportedAppType; import com.uber.sdk.android.core.UberSdk; @@ -104,8 +103,6 @@ public class LoginManager { @Deprecated private boolean redirectForAuthorizationCode = false; - private LinearLayout progressBarLayoutContainer; - /** * @param accessTokenStorage to store access token. * @param loginCallback callback to be called when {@link LoginManager#onActivityResult(Activity, int, int, Intent)} @@ -187,10 +184,9 @@ public void login(final @NonNull Activity activity) { productFlowPriority, sessionConfiguration, ResponseType.TOKEN, - "", - true, + false, true, - false); + true); activity.startActivityForResult(intent, requestCode); } else if (ssoDeeplink.isSupported(SsoDeeplink.FlowVersion.DEFAULT)) { ssoDeeplink.execute(SsoDeeplink.FlowVersion.DEFAULT); @@ -253,8 +249,7 @@ private void launchOnboardingFlow(Activity activity, responseType, "", false, - isRedirectToPlayStoreEnabled, - true); + isRedirectToPlayStoreEnabled); activity.startActivityForResult(intent, requestCode); } diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index c3052c31..94863677 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.widget.LinearLayout; import androidx.browser.customtabs.CustomTabsIntent; import com.google.common.collect.ImmutableList; @@ -41,10 +40,8 @@ import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; @@ -155,7 +152,7 @@ public void onLoginLoad_withNullResponseType_shouldReturnErrorResultIntent() { @Test public void onLoginLoad_withSsoEnabled_andSupported_shouldExecuteSsoDeeplink() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, - loginConfiguration, ResponseType.TOKEN, "", true, true, false); + loginConfiguration, ResponseType.TOKEN, true, true, false); ActivityController controller = Robolectric.buildActivity(LoginActivity.class, intent); loginActivity = controller.get(); @@ -171,7 +168,7 @@ public void onLoginLoad_withSsoEnabled_andSupported_shouldExecuteSsoDeeplink() { @Test public void onLoginLoad_withSsoEnabled_andNotSupported_shouldReturnErrorResultIntent() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), productPriority, - loginConfiguration, ResponseType.TOKEN, "", true, true, false); + loginConfiguration, ResponseType.TOKEN, true, true, false); ActivityController controller = Robolectric.buildActivity(LoginActivity.class).newIntent(intent); loginActivity = controller.get(); @@ -246,7 +243,7 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP .setScopes(MIXED_SCOPES) .build(); Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), - new ArrayList(), loginConfiguration, ResponseType.TOKEN, "", false, true, false); + new ArrayList(), loginConfiguration, ResponseType.TOKEN, true, false, true); ShadowActivity shadowActivity = shadowOf(Robolectric.buildActivity(LoginActivity.class, intent).create().get()); @@ -256,7 +253,7 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP @Test @Config(shadows = ShadowLoginPARDispatcher.class ) - public void onLoginLoad_whenPARFlowEnabled_shouldAddProgressIndicator_andLoadCustomTab() { + public void onLoginLoad_whenProfileHintProvided_shouldAddProgressIndicator_andLoadCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration .newBuilder() @@ -279,7 +276,7 @@ public void onLoginLoad_whenPARFlowEnabled_shouldAddProgressIndicator_andLoadCus @Test @Config(shadows = ShadowLoginPARDispatcherWithError.class ) - public void onLoginLoad_whenPARFlowEnabled_andErrorResponse_shouldAddProgressIndicator_andLoadCustomTab() { + public void onLoginLoad_whenProfileHintProvided_andErrorResponse_shouldAddProgressIndicator_andLoadCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.CODE, true); @@ -294,26 +291,35 @@ public void onLoginLoad_whenPARFlowEnabled_andErrorResponse_shouldAddProgressInd } @Test - public void onLoginLoad_whenPARFlowEnabled_andProfileHintEmpty_shouldAddProgressIndicator_andLoadCustomTab() { + public void onLoginLoad_whenProfileHintIsNull_shouldNotAddProgressIndicator_andLoadCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.CODE, true); + ResponseType.CODE); ActivityController controller = Robolectric .buildActivity(LoginActivity.class, intent); loginActivity = spy(controller.get()); loginActivity.customTabsHelper = customTabsHelper; loginActivity.onCreate(new Bundle()); - verify(loginActivity).addProgressIndicator(); - verify(loginActivity).removeProgressIndicator(); + verify(loginActivity, never()).addProgressIndicator(); + verify(loginActivity, never()).removeProgressIndicator(); String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); } @Test - public void onLoginLoad_whenPARFlowDisabled_shouldNotAddProgressIndicator_andLoadCustomTab() { - Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, - ResponseType.CODE, false); + public void onLoginLoad_whenProfileHintHasEmptyFields_shouldNotAddProgressIndicator_andLoadCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), + loginConfiguration.newBuilder() + .setProfileHint( + new ProfileHint.Builder() + .firstName("") + .lastName("") + .email("") + .phone("") + .build() + ).build(), + ResponseType.CODE); ActivityController controller = Robolectric .buildActivity(LoginActivity.class, intent); @@ -326,6 +332,45 @@ public void onLoginLoad_whenPARFlowDisabled_shouldNotAddProgressIndicator_andLoa eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); } + @Test + @Config(shadows = ShadowLoginPARDispatcher.class) + public void handleParFlow_whenProfileHintIsValid_thenAddProgressIndicator_andLaunchCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), + loginConfiguration + .newBuilder() + .setProfileHint(new ProfileHint + .Builder() + .firstName("test") + .build()) + .build(), + ResponseType.CODE, true); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + LoginActivity loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, "requestUri"); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + + @Test + public void handleParFlow_whenProfileHintIsNull_thenAddProgressIndicator_andLaunchCustomTab() { + Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), + loginConfiguration, + ResponseType.CODE); + + ActivityController controller = Robolectric + .buildActivity(LoginActivity.class, intent); + LoginActivity loginActivity = spy(controller.get()); + loginActivity.customTabsHelper = customTabsHelper; + loginActivity.onCreate(new Bundle()); + String expectedUrl = AuthUtils.buildUrl(REDIRECT_URI, ResponseType.CODE, loginConfiguration, ""); + verify(customTabsHelper).openCustomTab(any(LoginActivity.class), any(CustomTabsIntent.class), + eq(Uri.parse(expectedUrl)), any(CustomTabsHelper.BrowserFallback.class)); + } + @Test public void onTokenReceived_shouldReturnAccessTokenResult() { String tokenString = "accessToken1234"; From 50eab00ec5438df9a0bbec7e50447427e6cb31f2 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Thu, 25 May 2023 12:47:31 -0700 Subject: [PATCH 033/193] addressed comments --- core-android/build.gradle | 1 - .../uber/sdk/android/core/auth/AuthUtils.java | 2 +- .../sdk/android/core/auth/LoginActivity.java | 53 ++++++------------- .../sdk/android/core/auth/LoginManager.java | 1 - .../android/core/auth/LoginPARDispatcher.java | 27 ---------- .../core/auth/ShadowLoginPARDispatcher.java | 16 ++++-- .../ShadowLoginPARDispatcherWithError.java | 16 ++++-- gradle/dependencies.gradle | 12 ++--- 8 files changed, 43 insertions(+), 85 deletions(-) delete mode 100644 core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java diff --git a/core-android/build.gradle b/core-android/build.gradle index 3a7bc3c5..fe0d2556 100644 --- a/core-android/build.gradle +++ b/core-android/build.gradle @@ -60,7 +60,6 @@ dependencies { implementation deps.support.appCompat implementation deps.support.annotations implementation deps.support.chrometabs - implementation deps.network.moshi testImplementation deps.test.junit testImplementation deps.test.assertj diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java index 19ac1787..108b5aae 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/AuthUtils.java @@ -259,7 +259,7 @@ static String buildUrl( @NonNull String redirectUri, @NonNull ResponseType responseType, @NonNull SessionConfiguration configuration, - @NonNull String requestUri) { + String requestUri) { final String CLIENT_ID_PARAM = "client_id"; final String ENDPOINT = "auth"; diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java index eb87e99c..e14843d2 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginActivity.java @@ -29,11 +29,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.browser.customtabs.CustomTabsIntent; import android.text.TextUtils; -import android.util.Log; import android.view.Gravity; import android.view.ViewGroup; import android.webkit.WebResourceError; @@ -44,6 +40,10 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.browser.customtabs.CustomTabsIntent; + import com.uber.sdk.android.core.BuildConfig; import com.uber.sdk.android.core.R; import com.uber.sdk.android.core.SupportedAppType; @@ -106,7 +106,12 @@ public static Intent newIntent( @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType) { - return newIntent(context, sessionConfiguration, responseType, ""); + return newIntent(context, + new ArrayList(), + sessionConfiguration, + responseType, + false, + false); } /** @@ -118,7 +123,7 @@ public static Intent newIntent( * @param forceWebview Forced to use old webview instead of chrometabs * @return an intent that can be passed to this activity * - * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, SessionConfiguration, ResponseType, String)} + * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, SessionConfiguration, ResponseType)} */ @NonNull @Deprecated @@ -142,7 +147,7 @@ public static Intent newIntent( * @param isSsoEnabled specifies whether to attempt login with SSO * @return an intent that can be passed to this activity * - * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, ArrayList, SessionConfiguration, ResponseType, String, boolean, boolean)} + * This method has been deprecated. Please use {@link LoginActivity#newIntent(Context, ArrayList, SessionConfiguration, ResponseType, boolean, boolean)} */ @NonNull @Deprecated @@ -166,31 +171,6 @@ static Intent newIntent( return data; } - /** - * Create an {@link Intent} to pass to this activity - * - * @param context the {@link Context} for the intent - * @param sessionConfiguration to be used for gather clientId - * @param responseType that is expected - * @param requestUri to be used to prefill user's profile hint information - * @return an intent that can be passed to this activity - */ - @NonNull - public static Intent newIntent( - @NonNull Context context, - @NonNull SessionConfiguration sessionConfiguration, - @NonNull ResponseType responseType, - @NonNull String requestUri) { - - return newIntent(context, - new ArrayList(), - sessionConfiguration, - responseType, - requestUri, - false, - false); - } - /** * Create an {@link Intent} to pass to this activity * @@ -208,14 +188,12 @@ static Intent newIntent( @NonNull ArrayList productPriority, @NonNull SessionConfiguration sessionConfiguration, @NonNull ResponseType responseType, - String requestUri, boolean isSsoEnabled, boolean isRedirectToPlayStoreEnabled) { final Intent data = new Intent(context, LoginActivity.class) .putExtra(EXTRA_PRODUCT_PRIORITY, productPriority) .putExtra(EXTRA_SESSION_CONFIGURATION, sessionConfiguration) - .putExtra(EXTRA_REQUEST_URI, requestUri) .putExtra(EXTRA_RESPONSE_TYPE, responseType) .putExtra(EXTRA_SSO_ENABLED, isSsoEnabled) .putExtra(EXTRA_REDIRECT_TO_PLAY_STORE_ENABLED, isRedirectToPlayStoreEnabled); @@ -266,7 +244,6 @@ protected void onNewIntent(Intent intent) { } protected void init() { - Log.d("yyyy", "intent is " + getIntent().getData()); if (getIntent().getData() != null) { handleResponse(getIntent().getData()); } else if (isParFlow(getIntent())) { @@ -283,11 +260,11 @@ protected void init() { void handleParFlow() { responseType = (ResponseType) getIntent().getSerializableExtra(EXTRA_RESPONSE_TYPE); addProgressIndicator(); - LoginPARDispatcher.dispatchPAR( + new LoginPushedAuthorizationRequest( sessionConfiguration, - responseType, + responseType.name(), new LoginPARCallback(responseType) - ); + ).execute(); } @Override diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java index 779d5714..7b15b748 100644 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java +++ b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginManager.java @@ -247,7 +247,6 @@ private void launchOnboardingFlow(Activity activity, productFlowPriority, sessionConfiguration, responseType, - "", false, isRedirectToPlayStoreEnabled); activity.startActivityForResult(intent, requestCode); diff --git a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java b/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java deleted file mode 100644 index 0f71e806..00000000 --- a/core-android/src/main/java/com/uber/sdk/android/core/auth/LoginPARDispatcher.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.uber.sdk.android.core.auth; - -import com.uber.sdk.core.auth.internal.OAuth2Service; -import com.uber.sdk.core.client.SessionConfiguration; -import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; - -import retrofit2.Retrofit; -import retrofit2.converter.moshi.MoshiConverterFactory; - -public class LoginPARDispatcher { - - public static void dispatchPAR(SessionConfiguration sessionConfiguration, - ResponseType responseType, - LoginActivity.LoginPARCallback callback) { - new LoginPushedAuthorizationRequest( - new Retrofit.Builder() - .baseUrl(sessionConfiguration.getLoginHost()) - .addConverterFactory(MoshiConverterFactory.create()) - .build() - .create(OAuth2Service.class), - sessionConfiguration.getProfileHint(), - sessionConfiguration.getClientId(), - responseType.name(), - callback - ).execute(); - } -} diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java index 787f36b6..26427dbe 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java @@ -6,12 +6,20 @@ import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(LoginPARDispatcher.class) +@Implements(LoginPushedAuthorizationRequest.class) public class ShadowLoginPARDispatcher { + + private LoginPushedAuthorizationRequest.Callback callback; + + @Implementation + public void __constructor__ (SessionConfiguration sessionConfiguration, + String responseType, + LoginPushedAuthorizationRequest.Callback callback) { + this.callback = callback; + } + @Implementation - public static void dispatchPAR(SessionConfiguration sessionConfiguration, - ResponseType responseType, - LoginActivity.LoginPARCallback callback) { + public void execute() { callback.onSuccess("requestUri"); } } \ No newline at end of file diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java index afbae944..abe13b67 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java @@ -2,16 +2,24 @@ import com.uber.sdk.core.client.SessionConfiguration; import com.uber.sdk.core.client.internal.LoginPARRequestException; +import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(LoginPARDispatcher.class) +@Implements(LoginPushedAuthorizationRequest.class) public class ShadowLoginPARDispatcherWithError { + private LoginPushedAuthorizationRequest.Callback callback; + + @Implementation + public void __constructor__ (SessionConfiguration sessionConfiguration, + String responseType, + LoginPushedAuthorizationRequest.Callback callback) { + this.callback = callback; + } + @Implementation - public static void dispatchPAR(SessionConfiguration sessionConfiguration, - ResponseType responseType, - LoginActivity.LoginPARCallback callback) { + public void execute() { callback.onError(null); } } \ No newline at end of file diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index e73eb45d..da957467 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -20,16 +20,12 @@ def versions = [ uberJava: '0.8.1', ] -def network = [ - moshi: 'com.squareup.moshi:moshi:1.9.2', -] - def build = [ gradleVersion: '4.9', buildToolsVersion: '28.0.2', compileSdkVersion: 28, ci: 'true' == System.getenv('CI'), - minSdkVersion: 16, + minSdkVersion: 15, targetSdkVersion: 28, repositories: [ @@ -40,8 +36,7 @@ def build = [ android: 'com.android.tools.build:gradle:3.3.2', release: 'net.researchgate:gradle-release:2.1.2', github: 'co.riiid:gradle-github-plugin:0.4.2', - cobertura: 'net.saliman:gradle-cobertura-plugin:2.3.1', - buildConfig: 'gradle.plugin.de.fuerstenau:BuildConfigPlugin:1.1.8' + cobertura: 'net.saliman:gradle-cobertura-plugin:2.3.1' ] ] @@ -77,6 +72,5 @@ ext.deps = [ "support": support, "test": test, "versions": versions, - "uber": uber, - "network": network + "uber": uber ] From c5720e5a4a98a16857d4cd3ade391455fda92fe3 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Thu, 25 May 2023 13:30:30 -0700 Subject: [PATCH 034/193] renamed ShadowLoginPARDispatch to ShadowLoginPushedAUthorizationRequest --- .../com/uber/sdk/android/core/auth/LoginActivityTest.java | 6 +++--- ...cher.java => ShadowLoginPushedAuthorizationRequest.java} | 2 +- ... => ShadowLoginPushedAuthorizationRequestWithError.java} | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) rename core-android/src/test/java/com/uber/sdk/android/core/auth/{ShadowLoginPARDispatcher.java => ShadowLoginPushedAuthorizationRequest.java} (93%) rename core-android/src/test/java/com/uber/sdk/android/core/auth/{ShadowLoginPARDispatcherWithError.java => ShadowLoginPushedAuthorizationRequestWithError.java} (86%) diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java index 94863677..335e17cf 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/LoginActivityTest.java @@ -252,7 +252,7 @@ public void onLoginLoad_withResponseTypeToken_andPrivilegedScopes_andRedirectToP } @Test - @Config(shadows = ShadowLoginPARDispatcher.class ) + @Config(shadows = ShadowLoginPushedAuthorizationRequest.class ) public void onLoginLoad_whenProfileHintProvided_shouldAddProgressIndicator_andLoadCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration @@ -275,7 +275,7 @@ public void onLoginLoad_whenProfileHintProvided_shouldAddProgressIndicator_andLo } @Test - @Config(shadows = ShadowLoginPARDispatcherWithError.class ) + @Config(shadows = ShadowLoginPushedAuthorizationRequestWithError.class ) public void onLoginLoad_whenProfileHintProvided_andErrorResponse_shouldAddProgressIndicator_andLoadCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration, ResponseType.CODE, true); @@ -333,7 +333,7 @@ public void onLoginLoad_whenProfileHintHasEmptyFields_shouldNotAddProgressIndica } @Test - @Config(shadows = ShadowLoginPARDispatcher.class) + @Config(shadows = ShadowLoginPushedAuthorizationRequest.class) public void handleParFlow_whenProfileHintIsValid_thenAddProgressIndicator_andLaunchCustomTab() { Intent intent = LoginActivity.newIntent(Robolectric.setupActivity(Activity.class), loginConfiguration diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequest.java similarity index 93% rename from core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java rename to core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequest.java index 26427dbe..280180c3 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcher.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequest.java @@ -7,7 +7,7 @@ import org.robolectric.annotation.Implements; @Implements(LoginPushedAuthorizationRequest.class) -public class ShadowLoginPARDispatcher { +public class ShadowLoginPushedAuthorizationRequest { private LoginPushedAuthorizationRequest.Callback callback; diff --git a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequestWithError.java similarity index 86% rename from core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java rename to core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequestWithError.java index abe13b67..a247b409 100644 --- a/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPARDispatcherWithError.java +++ b/core-android/src/test/java/com/uber/sdk/android/core/auth/ShadowLoginPushedAuthorizationRequestWithError.java @@ -1,14 +1,13 @@ package com.uber.sdk.android.core.auth; import com.uber.sdk.core.client.SessionConfiguration; -import com.uber.sdk.core.client.internal.LoginPARRequestException; import com.uber.sdk.core.client.internal.LoginPushedAuthorizationRequest; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @Implements(LoginPushedAuthorizationRequest.class) -public class ShadowLoginPARDispatcherWithError { +public class ShadowLoginPushedAuthorizationRequestWithError { private LoginPushedAuthorizationRequest.Callback callback; @Implementation From 8447c674efdec83dea9332f46c43733c68e5b7f2 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Thu, 25 May 2023 16:10:09 -0700 Subject: [PATCH 035/193] upgrading minSdk version to 26 --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index da957467..9c3f9292 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -25,7 +25,7 @@ def build = [ buildToolsVersion: '28.0.2', compileSdkVersion: 28, ci: 'true' == System.getenv('CI'), - minSdkVersion: 15, + minSdkVersion: 26, targetSdkVersion: 28, repositories: [ From 9d9fd9d87eff590acf0e73adc45cc8653441805c Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Thu, 25 May 2023 16:21:13 -0700 Subject: [PATCH 036/193] Update README.md added documentation for using profile prefill feature --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index e4eb2d81..295ecb92 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,24 @@ user1Manager.setAccessToken(accessToken); user2Manager.setAccessToken(accessToken2); ``` +### Prefilling User Information +If you would like text fields during signup to be pre-populated with user information you can do so using the ProfileHint API. Partial information is accepted. You will need to supply a ProfileHint object when creating SessionConfiguration instance. + +``` +SessionConfiguration configuration = new SessionConfiguration.Builder() + .setClientId(CLIENT_ID) + .setRedirectUri(REDIRECT_URI) + .setScopes(Arrays.asList(Scope.PROFILE, Scope.RIDE_WIDGETS)) + .setProfileHint(new ProfileHint + .Builder() + .email("john@doe.com") + .firstName("John") + .lastName("Doe") + .phone("1234567890") + .build()) + .build(); +``` + ## Making an API Request The Android Uber SDK uses a dependency on the Java Uber SDK for API requests. After authentication is complete, create a `Session` to use the Uber API. From c5b73eabb39f94cd175422a53deaa1367e2a6e76 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Fri, 26 May 2023 10:53:15 -0700 Subject: [PATCH 037/193] Update gradle.properties preparing for the next release --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index d7ce2d91..5de72bc0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.3 -VERSION_NAME=0.10.3 +version=0.10.4 +VERSION_NAME=0.10.4 POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 66d1efd1f51d552cdc62cb16f4746b3dee129edb Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Fri, 26 May 2023 12:12:23 -0700 Subject: [PATCH 038/193] updated changelog file --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 185849f8..c87f242d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +v0.10.4 - 05/26/2023 +------------- + +### Added + - [Issue #194](https://github.com/uber/rides-android-sdk/issues/194) Integrated Login Pushed authorization request flow + - [Issue #193](https://github.com/uber/rides-android-sdk/issues/193) Deprecating embedded webviews + - Updated java version to 1.8 + - Updated login endpoint to auth.uber.com from login.uber.com + - Replaced jcenter with mavenCentral + v0.10.3 - 08/19/2021 ------------- From 590b69f76fc61193b0b8f72c79a660cb3a335047 Mon Sep 17 00:00:00 2001 From: Saurabh Lalwani Date: Tue, 30 May 2023 12:47:57 -0700 Subject: [PATCH 039/193] bumping up the version to prepare for next release --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 5de72bc0..a768ed2d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ GROUP=com.uber.sdk #Version is managed by Gradle Release Plugin -version=0.10.4 -VERSION_NAME=0.10.4 +version=0.10.5-SNAPSHOT +VERSION_NAME=0.10.5-SNAPSHOT POM_URL=https\://developer.uber.com POM_SCM_URL=https\://github.com/uber/rides-android-sdk/ From 7748df8c10ffcb0457fc03897d827b1fff0d4b29 Mon Sep 17 00:00:00 2001 From: lalwani Date: Mon, 7 Aug 2023 14:32:52 -0700 Subject: [PATCH 040/193] created login demo using applink --- samples/login-with-auth-code-demo/.gitignore | 3 + .../login-with-auth-code-demo/build.gradle | 70 ++++++++ .../gradle.properties | 3 + samples/login-with-auth-code-demo/lint.xml | 30 ++++ .../src/main/AndroidManifest.xml | 46 +++++ .../sdk/android/samples/DemoActivity.java | 169 ++++++++++++++++++ .../samples/auth/AuthUriAssembler.java | 63 +++++++ .../sdk/android/samples/auth/PkceUtil.java | 47 +++++ .../android/samples/model/AccessToken.java | 89 +++++++++ .../android/samples/network/AuthService.java | 40 +++++ .../network/AuthorizationCodeGrantFlow.java | 102 +++++++++++ .../network/TokenRequestFlowCallback.java | 41 +++++ .../drawable-hdpi/uber_sample_ic_launcher.png | Bin 0 -> 3333 bytes .../drawable-mdpi/uber_sample_ic_launcher.png | Bin 0 -> 2061 bytes .../uber_sample_ic_launcher.png | Bin 0 -> 4771 bytes .../uber_sample_ic_launcher.png | Bin 0 -> 8075 bytes .../uber_sample_ic_launcher.png | Bin 0 -> 10580 bytes .../src/main/res/layout/activity_sample.xml | 47 +++++ .../src/main/res/menu/menu_main.xml | 36 ++++ .../src/main/res/values/dimens.xml | 26 +++ .../src/main/res/values/strings.xml | 38 ++++ settings.gradle | 1 + 22 files changed, 851 insertions(+) create mode 100644 samples/login-with-auth-code-demo/.gitignore create mode 100644 samples/login-with-auth-code-demo/build.gradle create mode 100644 samples/login-with-auth-code-demo/gradle.properties create mode 100644 samples/login-with-auth-code-demo/lint.xml create mode 100644 samples/login-with-auth-code-demo/src/main/AndroidManifest.xml create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/DemoActivity.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/AuthUriAssembler.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/PkceUtil.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/model/AccessToken.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthService.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthorizationCodeGrantFlow.java create mode 100644 samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/TokenRequestFlowCallback.java create mode 100755 samples/login-with-auth-code-demo/src/main/res/drawable-hdpi/uber_sample_ic_launcher.png create mode 100755 samples/login-with-auth-code-demo/src/main/res/drawable-mdpi/uber_sample_ic_launcher.png create mode 100755 samples/login-with-auth-code-demo/src/main/res/drawable-xhdpi/uber_sample_ic_launcher.png create mode 100644 samples/login-with-auth-code-demo/src/main/res/drawable-xxhdpi/uber_sample_ic_launcher.png create mode 100755 samples/login-with-auth-code-demo/src/main/res/drawable-xxxhdpi/uber_sample_ic_launcher.png create mode 100644 samples/login-with-auth-code-demo/src/main/res/layout/activity_sample.xml create mode 100644 samples/login-with-auth-code-demo/src/main/res/menu/menu_main.xml create mode 100644 samples/login-with-auth-code-demo/src/main/res/values/dimens.xml create mode 100644 samples/login-with-auth-code-demo/src/main/res/values/strings.xml diff --git a/samples/login-with-auth-code-demo/.gitignore b/samples/login-with-auth-code-demo/.gitignore new file mode 100644 index 00000000..2bdf0f6a --- /dev/null +++ b/samples/login-with-auth-code-demo/.gitignore @@ -0,0 +1,3 @@ +/build +/debug +/release \ No newline at end of file diff --git a/samples/login-with-auth-code-demo/build.gradle b/samples/login-with-auth-code-demo/build.gradle new file mode 100644 index 00000000..ad04453a --- /dev/null +++ b/samples/login-with-auth-code-demo/build.gradle @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.2' + } +} + +apply plugin: 'com.android.application' +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "com.uber.sdk.android.samples" + minSdkVersion 26 + //noinspection ExpiredTargetSdkVersion + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + buildConfigField "String", "CLIENT_ID", "\"${loadSecret("UBER_CLIENT_ID")}\"" //Add your client id to gradle.properties + buildConfigField "String", "REDIRECT_URI", "\"${loadSecret("UBER_REDIRECT_URI")}\"" //Add your redirect uri to gradle.properties + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' + implementation 'com.squareup.moshi:moshi:1.2.0' +} + +/** + * Loads property from gradle.properties and ~/.gradle/gradle.properties + * Use to look up confidential information like keys that shoudln't be stored publicly + * @param name to lookup + * @return the value of the property or "MISSING" + */ +def loadSecret(String name) { + return hasProperty(name) ? getProperty(name) : "MISSING" +} diff --git a/samples/login-with-auth-code-demo/gradle.properties b/samples/login-with-auth-code-demo/gradle.properties new file mode 100644 index 00000000..a1c9d5e1 --- /dev/null +++ b/samples/login-with-auth-code-demo/gradle.properties @@ -0,0 +1,3 @@ +description=Login to Uber Sample +UBER_CLIENT_ID=insert_your_client_id_here +UBER_REDIRECT_URI=insert_your_redirect_uri_here \ No newline at end of file diff --git a/samples/login-with-auth-code-demo/lint.xml b/samples/login-with-auth-code-demo/lint.xml new file mode 100644 index 00000000..96c865ba --- /dev/null +++ b/samples/login-with-auth-code-demo/lint.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/samples/login-with-auth-code-demo/src/main/AndroidManifest.xml b/samples/login-with-auth-code-demo/src/main/AndroidManifest.xml new file mode 100644 index 00000000..3e477291 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/DemoActivity.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/DemoActivity.java new file mode 100644 index 00000000..c2b65ca1 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/DemoActivity.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Button; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import com.uber.sdk.android.rides.samples.BuildConfig; +import com.uber.sdk.android.rides.samples.R; +import com.uber.sdk.android.samples.auth.AuthUriAssembler; +import com.uber.sdk.android.samples.auth.PkceUtil; +import com.uber.sdk.android.samples.model.AccessToken; +import com.uber.sdk.android.samples.network.AuthorizationCodeGrantFlow; +import com.uber.sdk.android.samples.network.TokenRequestFlowCallback; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; + + +public class DemoActivity extends AppCompatActivity { + private static final String LOG_TAG = DemoActivity.class.getSimpleName(); + public static final String CLIENT_ID = BuildConfig.CLIENT_ID; + public static final String REDIRECT_URI = BuildConfig.REDIRECT_URI; + + public static final String BASE_URL = "https://auth.uber.com"; + private static final String ACCESS_TOKEN_SHARED_PREFERENCES = ".demoActivityStorage"; + private static final String ACCESS_TOKEN = ".access_token"; + private static final String EXTRA_CODE_RECEIVED = "CODE_RECEIVED"; + private static final int CUSTOM_BUTTON_REQUEST_CODE = 1111; + private static final String CODE_VERIFIER = PkceUtil.generateCodeVerifier(); + + private SharedPreferences sharedPreferences; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sample); + sharedPreferences = getApplicationContext() + .getSharedPreferences(ACCESS_TOKEN_SHARED_PREFERENCES, Context.MODE_PRIVATE); + Button appLinkButton = findViewById(R.id.applink_uber_button); + appLinkButton.setOnClickListener(v -> { + Intent intent = new Intent(Intent.ACTION_VIEW); + String codeChallenge; + try { + codeChallenge = PkceUtil.generateCodeChallange(CODE_VERIFIER); + } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + intent.setData( + AuthUriAssembler.assemble( + CLIENT_ID, + "profile", + "code", + codeChallenge, + REDIRECT_URI + ) + ); + startActivityForResult(intent, CUSTOM_BUTTON_REQUEST_CODE); + }); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(LOG_TAG, String.format("onActivityResult requestCode:[%s] resultCode [%s]", + requestCode, resultCode)); + if (data != null) { + final String authorizationCode = data.getStringExtra(EXTRA_CODE_RECEIVED); + if (authorizationCode != null) { + handleAuthCode(authorizationCode); + } + } + } + + private void handleAuthCode(String authCode) { + new AuthorizationCodeGrantFlow( + BASE_URL, + CLIENT_ID, + REDIRECT_URI, + authCode, + CODE_VERIFIER + ).execute(new TokenRequestFlowCallback() { + @Override + public void onSuccess(AccessToken accessToken) { + sharedPreferences.edit() + .putString(ACCESS_TOKEN, accessToken.getToken()) + .apply(); + } + + @Override + public void onFailure(Throwable throwable) { + Toast.makeText( + DemoActivity.this, + getString(R.string.authorization_code_error_message, throwable.getMessage()), + Toast.LENGTH_LONG + ).show(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + + if (id == R.id.action_clear) { + sharedPreferences.edit().clear().apply(); + Toast.makeText(this, "AccessToken cleared", Toast.LENGTH_SHORT).show(); + return true; + } else if (id == R.id.action_copy) { + String accessToken = sharedPreferences.getString(ACCESS_TOKEN, ""); + + String message = accessToken.isEmpty() ? "No AccessToken stored" : "AccessToken copied to clipboard"; + if (!accessToken.isEmpty()) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("UberDemoAccessToken", accessToken); + clipboard.setPrimaryClip(clip); + } + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/AuthUriAssembler.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/AuthUriAssembler.java new file mode 100644 index 00000000..9193ce9c --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/AuthUriAssembler.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.auth; + +import android.net.Uri; + +import androidx.annotation.NonNull; + +import com.uber.sdk.android.rides.samples.BuildConfig; + +import java.util.Locale; + +public class AuthUriAssembler { + static final String CLIENT_ID_PARAM = "client_id"; + static final String HTTPS = "https"; + static final String PATH = "oauth/v2/universal/authorize"; + static final String REDIRECT_PARAM = "redirect_uri"; + static final String RESPONSE_TYPE_PARAM = "response_type"; + static final String SCOPE_PARAM = "scope"; + static final String PLATFORM_PARAM = "sdk"; + static final String SDK_VERSION_PARAM = "sdk_version"; + static final String CODE_CHALLENGE_PARAM = "code_challenge"; + public static Uri assemble( + @NonNull String clientId, + @NonNull String scopes, + @NonNull String responseType, + @NonNull String codeChallenge, + @NonNull String redirectUri) { + + Uri.Builder builder = new Uri.Builder(); + builder.scheme(HTTPS) + .authority("auth.uber.com") + .appendEncodedPath(PATH) + .appendQueryParameter(CLIENT_ID_PARAM, clientId) + .appendQueryParameter(RESPONSE_TYPE_PARAM, responseType.toLowerCase(Locale.US)) + .appendQueryParameter(PLATFORM_PARAM, "android") + .appendQueryParameter(REDIRECT_PARAM, redirectUri) + .appendQueryParameter(SDK_VERSION_PARAM, BuildConfig.VERSION_NAME) + .appendQueryParameter(SCOPE_PARAM, scopes) + .appendQueryParameter(CODE_CHALLENGE_PARAM, codeChallenge); + return builder.build(); + } +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/PkceUtil.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/PkceUtil.java new file mode 100644 index 00000000..e29d26f1 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/auth/PkceUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.auth; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +public class PkceUtil { + public static String generateCodeVerifier() { + SecureRandom secureRandom = new SecureRandom(); + byte[] codeVerifier = new byte[32]; + secureRandom.nextBytes(codeVerifier); + return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier); + } + + public static String generateCodeChallange(String codeVerifier) throws UnsupportedEncodingException, NoSuchAlgorithmException { + byte[] bytes = codeVerifier.getBytes(StandardCharsets.US_ASCII); + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(bytes, 0, bytes.length); + byte[] digest = messageDigest.digest(); + return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); + } +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/model/AccessToken.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/model/AccessToken.java new file mode 100644 index 00000000..20280c52 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/model/AccessToken.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.model; + +import java.util.Objects; + +public class AccessToken { + private final long expires_in; + private final String scopes; + private final String access_token; + private final String refresh_token; + private final String token_type; + + /** + * @param expiresIn the time that the access token expires. + * @param scopes space delimited list of Scopes. + * @param token the Uber API access token. + * @param refreshToken the Uber API refresh token. + * @param tokenType the Uber API token type. + */ + public AccessToken( + long expiresIn, + String scopes, + String token, + String refreshToken, + String tokenType) { + this.expires_in = expiresIn; + this.scopes = scopes; + this.access_token = token; + this.refresh_token = refreshToken; + this.token_type = tokenType; + } + + /** + * Gets the raw token used to make API requests + * + * @return the raw token. + */ + public String getToken() { + return access_token; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AccessToken that = (AccessToken) o; + + if (expires_in != that.expires_in) return false; + if (!Objects.equals(scopes, that.scopes)) return false; + if (!Objects.equals(access_token, that.access_token)) + return false; + if (!Objects.equals(refresh_token, that.refresh_token)) + return false; + return Objects.equals(token_type, that.token_type); + + } + + @Override + public int hashCode() { + int result = (int) (expires_in ^ (expires_in >>> 32)); + result = 31 * result + (scopes != null ? scopes.hashCode() : 0); + result = 31 * result + (access_token != null ? access_token.hashCode() : 0); + result = 31 * result + (refresh_token != null ? refresh_token.hashCode() : 0); + result = 31 * result + (token_type != null ? token_type.hashCode() : 0); + return result; + } +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthService.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthService.java new file mode 100644 index 00000000..db372aae --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthService.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.network; + +import com.uber.sdk.android.samples.model.AccessToken; + +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +public interface AuthService { + @FormUrlEncoded + @POST("/oauth/v2/token") + Call token(@Field("client_id") String clientId, + @Field("code_verifier") String codeVerifier, + @Field("grant_type") String grantType, + @Field("redirect_uri") String redirectUri, + @Field("code") String authCode); +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthorizationCodeGrantFlow.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthorizationCodeGrantFlow.java new file mode 100644 index 00000000..c94fe571 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/AuthorizationCodeGrantFlow.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.network; + +import androidx.annotation.NonNull; + +import com.uber.sdk.android.samples.model.AccessToken; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.moshi.MoshiConverterFactory; + +public class AuthorizationCodeGrantFlow { + private final static String GRANT_TYPE = "authorization_code"; + + private final AuthService authService; + private final String clientId; + private final String redirectUri; + private final String authCode; + private final String codeVerifier; + + /** + * @param baseUrl domain/authority to send the oauth token request + * @param clientId oauth clientId of the app + * @param redirectUri redirectUri configured as part of the oauth flow + * @param authCode authCode that was delivered as part of redirectUri when user was authenticated + * @param codeVerifier code verifier that was generated as part of code challenge-verifier pair + */ + public AuthorizationCodeGrantFlow( + String baseUrl, + String clientId, + String redirectUri, + String authCode, + String codeVerifier + ) { + this.authService = createOAuthService(baseUrl); + this.clientId = clientId; + this.redirectUri = redirectUri; + this.authCode = authCode; + this.codeVerifier = codeVerifier; + } + + public void execute(TokenRequestFlowCallback callback) { + authService.token( + clientId, + codeVerifier, + GRANT_TYPE, + redirectUri, + authCode + ).enqueue( + new Callback() { + @Override + public void onResponse( + @NonNull Call call, + @NonNull Response response) { + if (response.isSuccessful()) { + callback.onSuccess(response.body()); + } else { + onFailure(call, new RuntimeException("Token request failed with code " + response.code())); + } + } + + @Override + public void onFailure( + @NonNull Call call, + @NonNull Throwable t) { + callback.onFailure(t); + } + } + ); + } + + private static AuthService createOAuthService(String baseUrl) { + return new Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(MoshiConverterFactory.create()) + .build() + .create(AuthService.class); + } +} diff --git a/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/TokenRequestFlowCallback.java b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/TokenRequestFlowCallback.java new file mode 100644 index 00000000..01b5c16d --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/java/com/uber/sdk/android/samples/network/TokenRequestFlowCallback.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.sdk.android.samples.network; + +import com.uber.sdk.android.samples.model.AccessToken; + +public interface TokenRequestFlowCallback { + /** + * Called when token request finishes successfully + * + * @param accessToken {@link AccessToken} object containing oauth tokens + */ + void onSuccess(AccessToken accessToken); + + /** + * Called when token request finishes with a failure + * + * @param error throwable containing reason for the failure + */ + void onFailure(Throwable error); +} \ No newline at end of file diff --git a/samples/login-with-auth-code-demo/src/main/res/drawable-hdpi/uber_sample_ic_launcher.png b/samples/login-with-auth-code-demo/src/main/res/drawable-hdpi/uber_sample_ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..26369aeb942afaf764e9141a5a2f253fc0b27c0e GIT binary patch literal 3333 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 literal 0 HcmV?d00001 diff --git a/samples/login-with-auth-code-demo/src/main/res/layout/activity_sample.xml b/samples/login-with-auth-code-demo/src/main/res/layout/activity_sample.xml new file mode 100644 index 00000000..0ad1ae47 --- /dev/null +++ b/samples/login-with-auth-code-demo/src/main/res/layout/activity_sample.xml @@ -0,0 +1,47 @@ + + + + + + +