diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5372cf..45ec2392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ * Change `BrowserSwitchClient#parseResult` parameters * Remove `deliverResult`, `getResult`, `captureResult`, `clearActiveRequests`, `getResultFromCache`, and `deliverResultFromCache` from `BrowserSwitchClient` * Add `BrowserSwitchRequest` and `BrowserSwitchPendingRequest` + * Convert `BrowserSwitchResult` to sealed class and add `BrowserSwitchResultInfo` + * Remove `BrowserSwitchStatus` ## 2.6.1 diff --git a/README.md b/README.md index 8c022618..ec498178 100644 --- a/README.md +++ b/README.md @@ -88,14 +88,16 @@ override fun onResume() { fun handleReturnToAppFromBrowser(intent: Intent) { // fetch stored pending request - storedPendingRequest()?.let { startedRequest -> - val browserSwitchResult = browserSwitchClient.parseResult(startedRequest, intent) - if (browserSwitchResult != null) { - // handle successful browser switch result - // clear stored pending request - } else { - // user did not complete browser switch - // allow user to complete browser switch, or clear stored pending request + fetchPendingRequestFromPersistentStorage()?.let { startedRequest -> + when (val browserSwitchResult = browserSwitchClient.parseResult(startedRequest, intent)) { + is BrowserSwitchResult.Success -> { + // handle successful browser switch result + // clear stored pending request + } + is BrowserSwitchResult.NoResult -> { + // user did not complete browser switch + // allow user to complete browser switch, or clear stored pending request + } } } } diff --git a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchClient.java b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchClient.java index 8274c82d..45ca0b99 100644 --- a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchClient.java +++ b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchClient.java @@ -110,18 +110,19 @@ private boolean isValidRequestCode(int requestCode) { * {@link BrowserSwitchClient#start(ComponentActivity, BrowserSwitchOptions)} * @param intent the intent to return to your application containing a deep link result from the * browser flow - * @return a {@link BrowserSwitchResult} if the browser switch was successfully completed, or - * null if the user returned to the app without completing the browser switch + * @return a {@link BrowserSwitchResult.Success} if the browser switch was successfully + * completed, or {@link BrowserSwitchResult.NoResult} if no result can be found for the given + * {@link BrowserSwitchPendingRequest.Started}. A {@link BrowserSwitchResult.NoResult} will be + * returned if the user returns to the app without completing the browser switch flow. */ - @Nullable public BrowserSwitchResult parseResult(@NonNull BrowserSwitchPendingRequest.Started pendingRequest, @Nullable Intent intent) { - BrowserSwitchResult result = null; if (intent != null && intent.getData() != null) { Uri deepLinkUrl = intent.getData(); if (pendingRequest.getBrowserSwitchRequest().matchesDeepLinkUrlScheme(deepLinkUrl)) { - result = new BrowserSwitchResult(BrowserSwitchStatus.SUCCESS, pendingRequest.getBrowserSwitchRequest(), deepLinkUrl); + BrowserSwitchResultInfo resultInfo = new BrowserSwitchResultInfo(pendingRequest.getBrowserSwitchRequest(), deepLinkUrl); + return new BrowserSwitchResult.Success(resultInfo); } } - return result; + return BrowserSwitchResult.NoResult.INSTANCE; } } diff --git a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchOptions.java b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchOptions.java index 75823c16..ff498519 100644 --- a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchOptions.java +++ b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchOptions.java @@ -2,9 +2,7 @@ import android.net.Uri; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentActivity; import org.json.JSONObject; @@ -26,7 +24,7 @@ public class BrowserSwitchOptions { * Set browser switch metadata. * * @param metadata JSONObject containing metadata that will be persisted and returned in a - * {@link BrowserSwitchResult} when the app has re-entered the foreground + * {@link BrowserSwitchResultInfo} when the app has re-entered the foreground * @return {@link BrowserSwitchOptions} reference to instance to allow setter invocations to be chained */ public BrowserSwitchOptions metadata(@Nullable JSONObject metadata) { diff --git a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.kt b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.kt new file mode 100644 index 00000000..b01b461d --- /dev/null +++ b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.kt @@ -0,0 +1,19 @@ +package com.braintreepayments.api + +/** + * The result of a browser switch obtained from [BrowserSwitchClient.parseResult] + */ +sealed class BrowserSwitchResult { + + /** + * The browser switch was successfully completed. See [resultInfo] for details. + */ + class Success(val resultInfo: BrowserSwitchResultInfo) : BrowserSwitchResult() + + /** + * No browser switch result was found. This is the expected result when a user cancels the + * browser switch flow without completing by closing the browser, or navigates back to the app + * without completing the browser switch flow. + */ + object NoResult : BrowserSwitchResult() +} diff --git a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.java b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResultInfo.java similarity index 66% rename from browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.java rename to browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResultInfo.java index 1f93fdfd..b77f2e98 100644 --- a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResult.java +++ b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchResultInfo.java @@ -8,44 +8,28 @@ import org.json.JSONObject; /** - * The result of the browser switch. + * Details of a successful {@link BrowserSwitchResult} */ -public class BrowserSwitchResult { +public class BrowserSwitchResultInfo { - private static final String KEY_STATUS = "status"; private static final String KEY_DEEP_LINK_URL = "deepLinkUrl"; private static final String KEY_BROWSER_SWITCH_REQUEST = "browserSwitchRequest"; - private final int status; private final Uri deepLinkUrl; private final BrowserSwitchRequest request; - static BrowserSwitchResult fromJson(String json) throws JSONException { + static BrowserSwitchResultInfo fromJson(String json) throws JSONException { JSONObject jsonObject = new JSONObject(json); - int status = jsonObject.getInt(KEY_STATUS); String deepLinkUrl = jsonObject.getString(KEY_DEEP_LINK_URL); String browserSwitchRequest = jsonObject.getString(KEY_BROWSER_SWITCH_REQUEST); - return new BrowserSwitchResult(status, BrowserSwitchRequest.fromJson(browserSwitchRequest), Uri.parse(deepLinkUrl)); + return new BrowserSwitchResultInfo(BrowserSwitchRequest.fromJson(browserSwitchRequest), Uri.parse(deepLinkUrl)); } - BrowserSwitchResult(@BrowserSwitchStatus int status, BrowserSwitchRequest request) { - this(status, request, null); - } - - BrowserSwitchResult(@BrowserSwitchStatus int status, BrowserSwitchRequest request, Uri deepLinkUrl) { - this.status = status; + BrowserSwitchResultInfo(BrowserSwitchRequest request, Uri deepLinkUrl) { this.request = request; this.deepLinkUrl = deepLinkUrl; } - /** - * @return The {@link BrowserSwitchStatus} of the browser switch - */ - @BrowserSwitchStatus - public int getStatus() { - return status; - } - /** * @return A {@link JSONObject} containing metadata persisted through the browser switch */ @@ -79,7 +63,6 @@ public Uri getDeepLinkUrl() { public String toJson() throws JSONException { JSONObject result = new JSONObject(); - result.put(KEY_STATUS, status); result.put(KEY_DEEP_LINK_URL, deepLinkUrl.toString()); result.put(KEY_BROWSER_SWITCH_REQUEST, request.toJson()); return result.toString(); diff --git a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchStatus.java b/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchStatus.java deleted file mode 100644 index 6b6e2790..00000000 --- a/browser-switch/src/main/java/com/braintreepayments/api/BrowserSwitchStatus.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.braintreepayments.api; - -import androidx.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The status of a {@link BrowserSwitchResult}. - */ -@Retention(RetentionPolicy.SOURCE) -@IntDef({BrowserSwitchStatus.SUCCESS, BrowserSwitchStatus.CANCELED}) -public @interface BrowserSwitchStatus { - - /** - * Browser switch is considered a success when a user is deep linked back into the app. - */ - int SUCCESS = 1; - - /** - * Browser switch is considered canceled when a user re-enters the app without deep link. - */ - int CANCELED = 2; -} \ No newline at end of file diff --git a/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchClientUnitTest.java b/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchClientUnitTest.java index eddea454..fbf7efaf 100644 --- a/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchClientUnitTest.java +++ b/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchClientUnitTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -191,13 +190,12 @@ public void parseResult_whenActiveRequestMatchesDeepLinkResultURLScheme_returnsB Intent intent = new Intent(Intent.ACTION_VIEW, deepLinkUrl); BrowserSwitchResult browserSwitchResult = sut.parseResult(new BrowserSwitchPendingRequest.Started(request), intent); - assertNotNull(browserSwitchResult); - assertEquals(BrowserSwitchStatus.SUCCESS, browserSwitchResult.getStatus()); - assertEquals(deepLinkUrl, browserSwitchResult.getDeepLinkUrl()); + assertTrue(browserSwitchResult instanceof BrowserSwitchResult.Success); + assertEquals(deepLinkUrl, ((BrowserSwitchResult.Success) browserSwitchResult).getResultInfo().getDeepLinkUrl()); } @Test - public void parseResult_whenDeepLinkResultURLSchemeDoesntMatch_returnsNull() { + public void parseResult_whenDeepLinkResultURLSchemeDoesntMatch_returnsNoResult() { BrowserSwitchClient sut = new BrowserSwitchClient(browserSwitchInspector, customTabsInternalClient); @@ -209,11 +207,11 @@ public void parseResult_whenDeepLinkResultURLSchemeDoesntMatch_returnsNull() { Intent intent = new Intent(Intent.ACTION_VIEW, deepLinkUrl); BrowserSwitchResult browserSwitchResult = sut.parseResult(new BrowserSwitchPendingRequest.Started(request), intent); - assertNull(browserSwitchResult); + assertTrue(browserSwitchResult instanceof BrowserSwitchResult.NoResult); } @Test - public void parseResult_whenIntentIsNull_returnsNull() { + public void parseResult_whenIntentIsNull_returnsNoResult() { BrowserSwitchClient sut = new BrowserSwitchClient(browserSwitchInspector, customTabsInternalClient); @@ -222,6 +220,6 @@ public void parseResult_whenIntentIsNull_returnsNull() { new BrowserSwitchRequest(123, browserSwitchDestinationUrl, requestMetadata, "fake-url-scheme", false); BrowserSwitchResult browserSwitchResult = sut.parseResult(new BrowserSwitchPendingRequest.Started(request), null); - assertNull(browserSwitchResult); + assertTrue(browserSwitchResult instanceof BrowserSwitchResult.NoResult); } } diff --git a/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultUnitTest.java b/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultInfoUnitTest.java similarity index 78% rename from browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultUnitTest.java rename to browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultInfoUnitTest.java index cf226f27..50ab4e1d 100644 --- a/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultUnitTest.java +++ b/browser-switch/src/test/java/com/braintreepayments/api/BrowserSwitchResultInfoUnitTest.java @@ -12,7 +12,7 @@ import org.skyscreamer.jsonassert.JSONAssert; @RunWith(RobolectricTestRunner.class) -public class BrowserSwitchResultUnitTest { +public class BrowserSwitchResultInfoUnitTest { @Test public void toJSON_serializesResult() throws JSONException { @@ -24,11 +24,11 @@ public void toJSON_serializesResult() throws JSONException { new BrowserSwitchRequest(123, requestUrl, requestMetadata, returnUrlScheme, true); Uri deepLinkUrl = Uri.parse("example.return.url.scheme://success/ok"); - BrowserSwitchResult sut = new BrowserSwitchResult(BrowserSwitchStatus.SUCCESS, request, deepLinkUrl); + BrowserSwitchResultInfo + sut = new BrowserSwitchResultInfo(request, deepLinkUrl); - BrowserSwitchResult sutSerialized = BrowserSwitchResult.fromJson(sut.toJson()); + BrowserSwitchResultInfo sutSerialized = BrowserSwitchResultInfo.fromJson(sut.toJson()); - assertEquals(BrowserSwitchStatus.SUCCESS, sutSerialized.getStatus()); assertEquals(deepLinkUrl, sutSerialized.getDeepLinkUrl()); assertEquals(123, sutSerialized.getRequestCode()); diff --git a/demo/src/main/java/com/braintreepayments/api/demo/ComposeActivity.kt b/demo/src/main/java/com/braintreepayments/api/demo/ComposeActivity.kt index e434c338..55509574 100644 --- a/demo/src/main/java/com/braintreepayments/api/demo/ComposeActivity.kt +++ b/demo/src/main/java/com/braintreepayments/api/demo/ComposeActivity.kt @@ -21,6 +21,7 @@ import com.braintreepayments.api.BrowserSwitchClient import com.braintreepayments.api.BrowserSwitchOptions import com.braintreepayments.api.BrowserSwitchPendingRequest import com.braintreepayments.api.BrowserSwitchResult +import com.braintreepayments.api.BrowserSwitchResultInfo import com.braintreepayments.api.demo.utils.PendingRequestStore import com.braintreepayments.api.demo.viewmodel.BrowserSwitchViewModel import org.json.JSONObject @@ -48,11 +49,13 @@ class ComposeActivity : ComponentActivity() { override fun onResume() { super.onResume() PendingRequestStore.get(this)?.let { startedRequest -> - val browserSwitchResult = browserSwitchClient.parseResult(startedRequest, intent) - browserSwitchResult?.let { result -> - viewModel.browserSwitchResult = result - } ?: run { - viewModel.browserSwitchError = Exception("User did not complete browser switch") + when (val browserSwitchResult = + browserSwitchClient.parseResult(startedRequest, intent)) { + is BrowserSwitchResult.Success -> viewModel.browserSwitchResult = + browserSwitchResult.resultInfo + + is BrowserSwitchResult.NoResult -> viewModel.browserSwitchError = + Exception("User did not complete browser switch") } PendingRequestStore.clear(this) } @@ -106,7 +109,7 @@ fun BrowserSwitchButton(onClick: () -> Unit) { } @Composable -fun BrowserSwitchSuccess(result: BrowserSwitchResult) { +fun BrowserSwitchSuccess(result: BrowserSwitchResultInfo) { result.deepLinkUrl?.let { returnUrl -> val color = returnUrl.getQueryParameter("color") val selectedColorString = "Selected color: $color" diff --git a/demo/src/main/java/com/braintreepayments/api/demo/DemoActivitySingleTop.java b/demo/src/main/java/com/braintreepayments/api/demo/DemoActivitySingleTop.java index 87a61b67..fc34a1bf 100644 --- a/demo/src/main/java/com/braintreepayments/api/demo/DemoActivitySingleTop.java +++ b/demo/src/main/java/com/braintreepayments/api/demo/DemoActivitySingleTop.java @@ -46,8 +46,8 @@ protected void onNewIntent(Intent intent) { BrowserSwitchPendingRequest.Started pendingRequest = PendingRequestStore.Companion.get(this); if (pendingRequest != null) { BrowserSwitchResult result = browserSwitchClient.parseResult(pendingRequest, intent); - if (result != null) { - Objects.requireNonNull(getDemoFragment()).onBrowserSwitchResult(result); + if (result instanceof BrowserSwitchResult.Success) { + Objects.requireNonNull(getDemoFragment()).onBrowserSwitchResult(((BrowserSwitchResult.Success) result).getResultInfo()); } PendingRequestStore.Companion.clear(this); } diff --git a/demo/src/main/java/com/braintreepayments/api/demo/DemoFragment.java b/demo/src/main/java/com/braintreepayments/api/demo/DemoFragment.java index 1ccc09e4..249092e3 100644 --- a/demo/src/main/java/com/braintreepayments/api/demo/DemoFragment.java +++ b/demo/src/main/java/com/braintreepayments/api/demo/DemoFragment.java @@ -15,8 +15,7 @@ import com.braintreepayments.api.BrowserSwitchException; import com.braintreepayments.api.BrowserSwitchOptions; -import com.braintreepayments.api.BrowserSwitchResult; -import com.braintreepayments.api.BrowserSwitchStatus; +import com.braintreepayments.api.BrowserSwitchResultInfo; import org.json.JSONException; import org.json.JSONObject; @@ -100,25 +99,17 @@ private void clearTextViews() { mMetadataTextView.setText(""); } - public void onBrowserSwitchResult(BrowserSwitchResult result) { - String resultText = null; + public void onBrowserSwitchResult(BrowserSwitchResultInfo result) { String selectedColorText = ""; - int statusCode = result.getStatus(); - switch (statusCode) { - case BrowserSwitchStatus.SUCCESS: - resultText = "Browser Switch Successful"; - - Uri returnUrl = result.getDeepLinkUrl(); - if (returnUrl != null) { - String color = returnUrl.getQueryParameter("color"); - selectedColorText = String.format("Selected color: %s", color); - } - break; - case BrowserSwitchStatus.CANCELED: - resultText = "Browser Switch Cancelled by User"; - break; + String resultText = "Browser Switch Successful"; + + Uri returnUrl = result.getDeepLinkUrl(); + if (returnUrl != null) { + String color = returnUrl.getQueryParameter("color"); + selectedColorText = String.format("Selected color: %s", color); } + mBrowserSwitchStatusTextView.setText(resultText); mSelectedColorTextView.setText(selectedColorText); diff --git a/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/BrowserSwitchViewModel.kt b/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/BrowserSwitchViewModel.kt index 29ecf0fe..2837f2fe 100644 --- a/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/BrowserSwitchViewModel.kt +++ b/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/BrowserSwitchViewModel.kt @@ -1,9 +1,7 @@ package com.braintreepayments.api.demo.viewmodel import androidx.lifecycle.ViewModel -import com.braintreepayments.api.BrowserSwitchClient -import com.braintreepayments.api.BrowserSwitchPendingRequest -import com.braintreepayments.api.BrowserSwitchResult +import com.braintreepayments.api.BrowserSwitchResultInfo import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -14,7 +12,7 @@ class BrowserSwitchViewModel : ViewModel() { private val _uiState = MutableStateFlow(UiState()) val uiState: StateFlow = _uiState.asStateFlow() - var browserSwitchResult : BrowserSwitchResult? + var browserSwitchResult : BrowserSwitchResultInfo? get() = _uiState.value.browserSwitchResult set(value) { _uiState.update { it.copy(browserSwitchResult = value) } diff --git a/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/UiState.kt b/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/UiState.kt index fbdade51..987aff27 100644 --- a/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/UiState.kt +++ b/demo/src/main/java/com/braintreepayments/api/demo/viewmodel/UiState.kt @@ -1,8 +1,8 @@ package com.braintreepayments.api.demo.viewmodel -import com.braintreepayments.api.BrowserSwitchResult +import com.braintreepayments.api.BrowserSwitchResultInfo data class UiState ( - val browserSwitchResult: BrowserSwitchResult? = null, + val browserSwitchResult: BrowserSwitchResultInfo? = null, val browserSwitchError: Exception? = null, ) \ No newline at end of file diff --git a/v3_MIGRATION_GUIDE.md b/v3_MIGRATION_GUIDE.md index 7f371205..686907bb 100644 --- a/v3_MIGRATION_GUIDE.md +++ b/v3_MIGRATION_GUIDE.md @@ -67,13 +67,15 @@ class MyActivity : ComponentActivity() { fun handleReturnToAppFromBrowser(intent: Intent) { // fetch stored pending request fetchPendingRequestFromPersistentStorage()?.let { startedRequest -> - val browserSwitchResult = browserSwitchClient.parseResult(startedRequest, intent) - if (browserSwitchResult != null) { - // handle successful browser switch result - // clear stored pending request - } else { - // user did not complete browser switch - // allow user to complete browser switch, or clear stored pending request + when (val browserSwitchResult = browserSwitchClient.parseResult(startedRequest, intent)) { + is BrowserSwitchResult.Success -> { + // handle successful browser switch result + // clear stored pending request + } + is BrowserSwitchResult.NoResult -> { + // user did not complete browser switch + // allow user to complete browser switch, or clear stored pending request + } } } }