Skip to content

Commit

Permalink
Rename launchers
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahkoop committed Dec 21, 2023
1 parent 44995a1 commit f427f47
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -1,70 +1,62 @@
package com.braintreepayments.api;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import androidx.activity.ComponentActivity;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.ActivityResultRegistry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;

import java.util.UUID;

class BrowserSwitchLauncher {
public class BrowserSwitchLauncher {

private static final String BROWSER_SWITCH_RESULT = "com.braintreepayments.api.BrowserSwitch.RESULT";
private BrowserSwitchLauncherCallback callback;
private InternalBrowserSwitchLauncher internalBrowserSwitchLauncher;
private BrowserSwitchResult browserSwitchResult;

ActivityResultLauncher<BrowserSwitchOptions> activityLauncher;
private Intent handledIntent;

BrowserSwitchLauncher(@NonNull ComponentActivity activity,
@NonNull BrowserSwitchLauncherCallback callback) {
this(activity.getActivityResultRegistry(), activity, callback);
public BrowserSwitchLauncher(Fragment fragment, BrowserSwitchLauncherCallback callback) {
this.callback = callback;
this.internalBrowserSwitchLauncher = new InternalBrowserSwitchLauncher(fragment, browserSwitchResult -> {
this.browserSwitchResult = browserSwitchResult;
});
}

BrowserSwitchLauncher(@NonNull Fragment fragment,
@NonNull BrowserSwitchLauncherCallback callback) {
this(fragment.getActivity().getActivityResultRegistry(), fragment.getViewLifecycleOwner(),
callback);
public BrowserSwitchLauncher(ComponentActivity activity, BrowserSwitchLauncherCallback callback) {
this.callback = callback;
this.internalBrowserSwitchLauncher = new InternalBrowserSwitchLauncher(activity, browserSwitchResult -> {
// In the case of a true user cancellation (user closes the browser without completing),
// this result will be delivered before handleReturnToAppFromBrowser is invoked in
// onResume. After a successful result is delivered, the browser activity will be closed
// in the background, delivering a false cancel result.
this.browserSwitchResult = browserSwitchResult;
});
}

BrowserSwitchLauncher(ActivityResultRegistry registry, LifecycleOwner lifecycleOwner,
BrowserSwitchLauncherCallback callback) {
activityLauncher = registry.register(BROWSER_SWITCH_RESULT, lifecycleOwner,
new BrowserSwitchActivityResultContract(), callback::onResult);
public void launch(BrowserSwitchOptions browserSwitchOptions) throws BrowserSwitchException {
internalBrowserSwitchLauncher.launch(browserSwitchOptions);
}

void launch(BrowserSwitchOptions browserSwitchOptions) throws BrowserSwitchException {
try {
activityLauncher.launch(browserSwitchOptions);
} catch (ActivityNotFoundException e) {
throw new BrowserSwitchException(e.getMessage());
}
}

void clearActiveRequests(@NonNull Context context) {
BrowserSwitchPersistentStore.getInstance().clearActiveRequest(context.getApplicationContext());
}

@Nullable
public BrowserSwitchResult parseResult(@NonNull Context context, int requestCode, @Nullable Intent intent) {
public void handleReturnToAppFromBrowser(@NonNull Context context, int requestId, @NonNull Intent intent) {
BrowserSwitchResult result = null;
if (intent != null && intent.getData() != null) {
BrowserSwitchRequest request =
BrowserSwitchPersistentStore.getInstance().getActiveRequest(context.getApplicationContext());
if (request != null && request.getRequestCode() == requestCode) {
Uri deepLinkUrl = intent.getData();
if (request.matchesDeepLinkUrlScheme(deepLinkUrl)) {
result = new BrowserSwitchResult(BrowserSwitchStatus.SUCCESS, request, deepLinkUrl);
}
}
// If browser-switch is re-launched from the same activity, a false previous success result
// may be re-delivered even if the user cancels this time, check if we have already handled
// the intent before delivering a success result again
if (!intent.equals(handledIntent)) {
// Handle successful deep link back to app first, to avoid false cancel result (above)
result = internalBrowserSwitchLauncher.parseResult(context, requestId, intent);
}
if (result == null) {
// If the app was not successfully resumed via deep link, check if there was a true
// cancel result
result = this.browserSwitchResult;
}
if (result != null) {
callback.onResult(result);
internalBrowserSwitchLauncher.clearActiveRequests(context);
this.browserSwitchResult = null;
handledIntent = intent;
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.braintreepayments.api;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import androidx.activity.ComponentActivity;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.ActivityResultRegistry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;

class InternalBrowserSwitchLauncher {

private static final String BROWSER_SWITCH_RESULT = "com.braintreepayments.api.BrowserSwitch.RESULT";

ActivityResultLauncher<BrowserSwitchOptions> activityLauncher;

InternalBrowserSwitchLauncher(@NonNull ComponentActivity activity,
@NonNull BrowserSwitchLauncherCallback callback) {
this(activity.getActivityResultRegistry(), activity, callback);
}

InternalBrowserSwitchLauncher(@NonNull Fragment fragment,
@NonNull BrowserSwitchLauncherCallback callback) {
this(fragment.getActivity().getActivityResultRegistry(), fragment.getViewLifecycleOwner(),
callback);
}

InternalBrowserSwitchLauncher(ActivityResultRegistry registry, LifecycleOwner lifecycleOwner,
BrowserSwitchLauncherCallback callback) {
activityLauncher = registry.register(BROWSER_SWITCH_RESULT, lifecycleOwner,
new BrowserSwitchActivityResultContract(), callback::onResult);
}

void launch(BrowserSwitchOptions browserSwitchOptions) throws BrowserSwitchException {
try {
activityLauncher.launch(browserSwitchOptions);
} catch (ActivityNotFoundException e) {
throw new BrowserSwitchException(e.getMessage());
}
}

void clearActiveRequests(@NonNull Context context) {
BrowserSwitchPersistentStore.getInstance().clearActiveRequest(context.getApplicationContext());
}

@Nullable
public BrowserSwitchResult parseResult(@NonNull Context context, int requestCode, @Nullable Intent intent) {
BrowserSwitchResult result = null;
if (intent != null && intent.getData() != null) {
BrowserSwitchRequest request =
BrowserSwitchPersistentStore.getInstance().getActiveRequest(context.getApplicationContext());
if (request != null && request.getRequestCode() == requestCode) {
Uri deepLinkUrl = intent.getData();
if (request.matchesDeepLinkUrlScheme(deepLinkUrl)) {
result = new BrowserSwitchResult(BrowserSwitchStatus.SUCCESS, request, deepLinkUrl);
}
}
}
return result;
}
}

This file was deleted.

16 changes: 15 additions & 1 deletion demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@
android:icon="@mipmap/ic_launcher"
android:label="Browser Switch Demo"
android:theme="@style/AppTheme">
<activity
android:name=".LauncherActivityNoButton"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<data android:scheme="launcher-activity-no-button" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>

<activity
android:name=".LauncherActivity"
android:exported="true" >
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ import androidx.appcompat.app.AppCompatActivity
import com.braintreepayments.api.BrowserSwitchOptions
import com.braintreepayments.api.BrowserSwitchResult
import com.braintreepayments.api.BrowserSwitchStatus
import com.braintreepayments.api.PublicBrowserSwitchLauncher
import com.braintreepayments.api.BrowserSwitchLauncher
import org.json.JSONException
import org.json.JSONObject

class LauncherActivity : AppCompatActivity() {

private lateinit var launcher: PublicBrowserSwitchLauncher
private lateinit var launcher: BrowserSwitchLauncher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_launcher)

launcher = PublicBrowserSwitchLauncher(this) { browserSwitchResult ->
onBrowserSwitchResult(browserSwitchResult)
}
launcher =
BrowserSwitchLauncher(this) { browserSwitchResult ->
onBrowserSwitchResult(browserSwitchResult)
}
val launchButton = findViewById<Button>(R.id.browser_switch_launcher)
launchButton.setOnClickListener { launchBrowserSwitch() }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.braintreepayments.api.demo

import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import com.braintreepayments.api.BrowserSwitchOptions
import com.braintreepayments.api.BrowserSwitchResult
import com.braintreepayments.api.BrowserSwitchStatus
import com.braintreepayments.api.BrowserSwitchLauncher
import org.json.JSONException
import org.json.JSONObject

class LauncherActivityNoButton : AppCompatActivity() {

private lateinit var launcher: BrowserSwitchLauncher
private var browserSwitchHandled = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_launcher_no_button)
launcher =
BrowserSwitchLauncher(this) { browserSwitchResult ->
onBrowserSwitchResult(browserSwitchResult)
browserSwitchHandled = true
}
launcher.handleReturnToAppFromBrowser(this, 1, intent)

if (!browserSwitchHandled) {
launchBrowserSwitch()
}
}

override fun onResume() {
super.onResume()
launcher.handleReturnToAppFromBrowser(this, 1, intent)
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
setIntent(intent)
}
}
private fun launchBrowserSwitch() {
val metadata: JSONObject? = buildMetadataObject()
val url: Uri = buildBrowserSwitchUrl()

val browserSwitchOptions = BrowserSwitchOptions()
.requestCode(1)
.metadata(metadata)
.url(url)
.returnUrlScheme("launcher-activity-no-button")
launcher.launch(browserSwitchOptions)
}

fun onBrowserSwitchResult(result: BrowserSwitchResult) {
var resultText: String? = null
var selectedColorText = ""
val statusCode = result.status
when (statusCode) {
BrowserSwitchStatus.SUCCESS -> {
resultText = "Browser Switch Successful"
val returnUrl = result.deepLinkUrl
if (returnUrl != null) {
val color = returnUrl.getQueryParameter("color")
selectedColorText = String.format("Selected color: %s", color)
}
}

BrowserSwitchStatus.CANCELED -> resultText = "Browser Switch Cancelled by User"
}
var metadataOutput: String? = null
val requestMetadata = result.requestMetadata
if (requestMetadata != null) {
try {
val metadataValue = result.requestMetadata!!.getString("testKey")
metadataOutput = String.format("%s=%s", "testKey", metadataValue)
} catch (ignore: JSONException) {
// do nothing
}
}
Toast.makeText(this, resultText, Toast.LENGTH_LONG).show()
}
private fun buildMetadataObject(): JSONObject? {
try {
return JSONObject().put("testKey", "testValue")
} catch (ignore: JSONException) {
// do nothing
}
return null
}

private fun buildBrowserSwitchUrl(): Uri {
val url = "https://braintree.github.io/popup-bridge-example/" +
"this_launches_in_popup.html?popupBridgeReturnUrlPrefix=" +
"launcher-activity-no-button" + "://"
return Uri.parse(url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ protected void onCreate(Bundle savedInstanceState) {

Button launcherButton = findViewById(R.id.launcher_button);
launcherButton.setOnClickListener(this::launchBrowserSwitchLauncher);

Button launcherWithoutButton = findViewById(R.id.launcher_without_button);
launcherWithoutButton.setOnClickListener(this::launchBrowserSwitchLauncherWithoutButton);
}

public void launchStandardBrowserSwitch(View view) {
Expand All @@ -38,4 +41,9 @@ public void launchBrowserSwitchLauncher(View view) {
Intent intent = new Intent(this, LauncherActivity.class);
startActivity(intent);
}

public void launchBrowserSwitchLauncherWithoutButton(View view) {
Intent intent = new Intent(this, LauncherActivityNoButton.class);
startActivity(intent);
}
}
Loading

0 comments on commit f427f47

Please sign in to comment.