Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert BrowserSwitchResult to Sealed Class #88

Merged
merged 9 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 BrowsrSwitchResult.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
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -14,7 +12,7 @@ class BrowserSwitchViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

var browserSwitchResult : BrowserSwitchResult?
var browserSwitchResult : BrowserSwitchResultInfo?
get() = _uiState.value.browserSwitchResult
set(value) {
_uiState.update { it.copy(browserSwitchResult = value) }
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
)
16 changes: 9 additions & 7 deletions v3_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 BrowsrSwitchResult.Success -> {
sarahkoop marked this conversation as resolved.
Show resolved Hide resolved
// 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
}
}
}
}
Expand Down
Loading