From 4f6a4eb71ed92139ee04533f6b39bfe35072991b Mon Sep 17 00:00:00 2001 From: Carlos Galan Cladera Date: Thu, 13 Feb 2025 11:25:15 +0100 Subject: [PATCH 01/20] feat(rich-consents): support authorization details Add support to `authorization_details` property in the Rich Consent record. --- ...ParcelableRichConsentRequestedDetails.java | 13 +++++++ .../GuardianRichConsentRequestedDetails.java | 15 +++++++- .../sdk/RichConsentRequestedDetails.java | 9 ++++- .../sdk/RichConsentsAPIClientTest.java | 30 ++++++++++++++- .../guardian/sdk/utils/MockWebService.java | 38 ++++++++++++------- 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java index 0aa5d6c..69edabc 100644 --- a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java +++ b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java @@ -10,6 +10,10 @@ import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; +import java.util.Collections; +import java.util.List; +import java.util.Map; + public class ParcelableRichConsentRequestedDetails implements RichConsentRequestedDetails, Parcelable { public static final Creator CREATOR = new Creator() { @Override @@ -30,11 +34,14 @@ public ParcelableRichConsentRequestedDetails[] newArray(int size) { private final String[] scope; @SerializedName("bindingMessage") private final String bindingMessage; + @SerializedName("authorizationDetails") + private List> authorizationDetails; public ParcelableRichConsentRequestedDetails(RichConsentRequestedDetails requestedDetails) { audience = requestedDetails.getAudience(); scope = requestedDetails.getScope(); bindingMessage = requestedDetails.getBindingMessage(); + authorizationDetails = requestedDetails.getAuthorizationDetails(); } protected ParcelableRichConsentRequestedDetails(Parcel in) { @@ -68,6 +75,11 @@ public String getBindingMessage() { return bindingMessage; } + @Override + public List> getAuthorizationDetails() { + return authorizationDetails; + } + @Override public int describeContents() { return 0; @@ -78,5 +90,6 @@ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(audience); dest.writeStringArray(scope); dest.writeString(bindingMessage); + dest.writeList(authorizationDetails); } } diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java index ad83456..7e1fe94 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java @@ -4,19 +4,27 @@ import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.Map; + public class GuardianRichConsentRequestedDetails implements RichConsentRequestedDetails { private final String audience; private final String[] scope; @SerializedName("binding_message") private final String bindingMessage; + @SerializedName("authorization_details") + private final List> authorizationDetails; public GuardianRichConsentRequestedDetails( String audience, String[] scope, - String bindingMessage) { + String bindingMessage, + List> authorizationDetails + ) { this.bindingMessage = bindingMessage; this.audience = audience; this.scope = scope; + this.authorizationDetails = authorizationDetails; } @@ -36,4 +44,9 @@ public String[] getScope() { public String getBindingMessage() { return bindingMessage; } + + @Override + public List> getAuthorizationDetails() { + return authorizationDetails; + } } diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java index 3750618..9aa9e8e 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java @@ -2,6 +2,9 @@ import androidx.annotation.NonNull; +import java.util.List; +import java.util.Map; + public interface RichConsentRequestedDetails { /** * Requested audience @@ -19,5 +22,9 @@ public interface RichConsentRequestedDetails { * CIBA binding message */ String getBindingMessage(); -} + /** + * Rich Authorization Details + */ + List> getAuthorizationDetails(); +} diff --git a/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java b/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java index 4141b5d..ae38cab 100644 --- a/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java +++ b/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java @@ -31,6 +31,9 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.interfaces.RSAKey; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -100,7 +103,32 @@ public void shouldFetchRichConsent() throws Exception { assertThat(capturedRichConsent.getRequestedDetails().getBindingMessage(), is(equalTo(BINDING_MESSAGE))); verifyNoMoreInteractions(fetchCallback); - assertThat(true, is(equalTo(true))); + } + + @Test + public void shouldFetchRichConsentWithRichAuthorizationDetails() throws Exception { + Map testDetails = new HashMap<>(); + testDetails.put("type", "test-type"); + List> expectedAuthorizationDetails = List.of(testDetails); + + mockAPI.willReturnRichConsent(CONSENT_ID, AUDIENCE, SCOPE, BINDING_MESSAGE, expectedAuthorizationDetails); + + richConsentsAPIClient + .fetch(CONSENT_ID, TRANSACTION_TOKEN, keyPair.getPrivate(), keyPair.getPublic()) + .start(fetchCallback); + + // RecordedRequest request = mockAPI.takeRequest(); + + ArgumentCaptor onSuccessCaptor = ArgumentCaptor.forClass(GuardianRichConsent.class); + verify(fetchCallback, timeout(100)).onSuccess(onSuccessCaptor.capture()); + + RichConsent capturedRichConsent = onSuccessCaptor.getValue(); + List> authorizationDetails = capturedRichConsent.getRequestedDetails().getAuthorizationDetails(); + assertThat(authorizationDetails, is(notNullValue())); + assertThat(authorizationDetails.isEmpty(), is(equalTo(false))); + assertThat(authorizationDetails.get(0).get("type"), is(equalTo(expectedAuthorizationDetails.get(0).get("type")))); + + verifyNoMoreInteractions(fetchCallback); } private void verifyDPoPAssertion(String assertion) { diff --git a/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/MockWebService.java b/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/MockWebService.java index 1d033d9..3e408e0 100644 --- a/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/MockWebService.java +++ b/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/MockWebService.java @@ -22,8 +22,13 @@ package com.auth0.android.guardian.sdk.utils; +import com.google.gson.Gson; + import java.io.IOException; import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -125,22 +130,29 @@ public MockWebService willReturnEnrollment(String id, String url, String issuer, return this; } - public MockWebService willReturnRichConsent(String id, String audience, String scope, String binding_message) { - String json = "" + - "{" + - " \"id\": \"" + id + "\"" + - " ,\"created_at\": " + Instant.now().minusSeconds(10).getEpochSecond() + - " ,\"expires_at\": " + Instant.now().plusSeconds(290).getEpochSecond() + - " ,\"requested_details\": {" + - " \"audience\": \"" + audience + "\"" + - " ,\"scope\": [\"" + scope + "\"]" + - " ,\"binding_message\": \"" + binding_message + "\"" + - " }" + - "}"; - server.enqueue(responseWithJSON(json, 200)); + public MockWebService willReturnRichConsent(String id, String audience, String scope, String binding_message, List> authorizationDetails) { + Gson gson = new Gson(); + Map consentObject = new HashMap<>(); + Map requestedDetailsObject = new HashMap<>(); + consentObject.put("id", id); + consentObject.put("created_at",Instant.now().minusSeconds(10).getEpochSecond() ); + consentObject.put("expires_at",Instant.now().plusSeconds(290).getEpochSecond() ); + requestedDetailsObject.put("audience", audience); + requestedDetailsObject.put("scope", List.of(scope)); + requestedDetailsObject.put("binding_message", binding_message); + if (authorizationDetails != null && !authorizationDetails.isEmpty()) { + requestedDetailsObject.put("authorization_details", authorizationDetails); + } + consentObject.put("requested_details", requestedDetailsObject); + + server.enqueue(responseWithJSON(gson.toJson(consentObject), 200)); return this; } + public MockWebService willReturnRichConsent(String id, String audience, String scope, String binding_message) { + return willReturnRichConsent(id, audience, scope, binding_message, null); + } + public MockWebService willReturnServerError(int statusCode, String errorCode, String error, String message) { String json = "" + "{" + From 2fc99c12b7ccee7e46d897b76435a4feb97c98e6 Mon Sep 17 00:00:00 2001 From: Carlos Galan Cladera Date: Fri, 14 Feb 2025 16:55:58 +0100 Subject: [PATCH 02/20] feat(rich-consents): authz details typed overload --- .../GuardianRichConsentRequestedDetails.java | 19 +++++++++++++++++++ .../sdk/RichConsentRequestedDetails.java | 8 ++++++++ .../sdk/RichConsentsAPIClientTest.java | 17 +++++++++++++---- ...IntentTestingAuthorizationDetailsType.java | 19 +++++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 guardian/src/test/java/com/auth0/android/guardian/sdk/utils/PaymentIntentTestingAuthorizationDetailsType.java diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java index 7e1fe94..86e91a2 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java @@ -2,10 +2,16 @@ import androidx.annotation.NonNull; +import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.sql.Array; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class GuardianRichConsentRequestedDetails implements RichConsentRequestedDetails { private final String audience; @@ -49,4 +55,17 @@ public String getBindingMessage() { public List> getAuthorizationDetails() { return authorizationDetails; } + + @Override + public List getAuthorizationDetails(String type, Class clazz) { + final Gson gson = new Gson(); + List types = new ArrayList<>(); + + for (Map item : authorizationDetails) { + if (Objects.equals(item.get("type"), type)) { + types.add(gson.fromJson(gson.toJson(gson.toJsonTree(item)), clazz)); + } + } + return types; + } } diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java index 9aa9e8e..b8e5038 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java @@ -27,4 +27,12 @@ public interface RichConsentRequestedDetails { * Rich Authorization Details */ List> getAuthorizationDetails(); + + /** + * Authorization Details Type + * @param type + * @param clazz + * @return + */ + List getAuthorizationDetails(String type, Class clazz); } diff --git a/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java b/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java index ae38cab..488d422 100644 --- a/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java +++ b/guardian/src/test/java/com/auth0/android/guardian/sdk/RichConsentsAPIClientTest.java @@ -15,6 +15,7 @@ import com.auth0.android.guardian.sdk.networking.Callback; import com.auth0.android.guardian.sdk.networking.RequestFactory; import com.auth0.android.guardian.sdk.utils.MockWebService; +import com.auth0.android.guardian.sdk.utils.PaymentIntentTestingAuthorizationDetailsType; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.google.gson.Gson; @@ -108,7 +109,8 @@ public void shouldFetchRichConsent() throws Exception { @Test public void shouldFetchRichConsentWithRichAuthorizationDetails() throws Exception { Map testDetails = new HashMap<>(); - testDetails.put("type", "test-type"); + testDetails.put("type", "payment-intent"); + testDetails.put("amount", 100); List> expectedAuthorizationDetails = List.of(testDetails); mockAPI.willReturnRichConsent(CONSENT_ID, AUDIENCE, SCOPE, BINDING_MESSAGE, expectedAuthorizationDetails); @@ -117,8 +119,6 @@ public void shouldFetchRichConsentWithRichAuthorizationDetails() throws Exceptio .fetch(CONSENT_ID, TRANSACTION_TOKEN, keyPair.getPrivate(), keyPair.getPublic()) .start(fetchCallback); - // RecordedRequest request = mockAPI.takeRequest(); - ArgumentCaptor onSuccessCaptor = ArgumentCaptor.forClass(GuardianRichConsent.class); verify(fetchCallback, timeout(100)).onSuccess(onSuccessCaptor.capture()); @@ -126,7 +126,16 @@ public void shouldFetchRichConsentWithRichAuthorizationDetails() throws Exceptio List> authorizationDetails = capturedRichConsent.getRequestedDetails().getAuthorizationDetails(); assertThat(authorizationDetails, is(notNullValue())); assertThat(authorizationDetails.isEmpty(), is(equalTo(false))); - assertThat(authorizationDetails.get(0).get("type"), is(equalTo(expectedAuthorizationDetails.get(0).get("type")))); + assertThat(authorizationDetails.get(0).get("type"), is(equalTo("payment-intent"))); + + PaymentIntentTestingAuthorizationDetailsType paymentIntent = capturedRichConsent + .getRequestedDetails() + .getAuthorizationDetails("payment-intent", PaymentIntentTestingAuthorizationDetailsType.class) + .get(0); + + assertThat(paymentIntent, is(notNullValue())); + assertThat(paymentIntent.getType(), is(equalTo("payment-intent"))); + assertThat(paymentIntent.getAmount(), is(equalTo(100))); verifyNoMoreInteractions(fetchCallback); } diff --git a/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/PaymentIntentTestingAuthorizationDetailsType.java b/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/PaymentIntentTestingAuthorizationDetailsType.java new file mode 100644 index 0000000..3ea7976 --- /dev/null +++ b/guardian/src/test/java/com/auth0/android/guardian/sdk/utils/PaymentIntentTestingAuthorizationDetailsType.java @@ -0,0 +1,19 @@ +package com.auth0.android.guardian.sdk.utils; + +public class PaymentIntentTestingAuthorizationDetailsType { + private final String type; + private final int amount; + + public PaymentIntentTestingAuthorizationDetailsType(String type, int amount) { + this.type = type; + this.amount = amount; + } + + public String getType() { + return type; + } + + public int getAmount() { + return amount; + } +} From 247bc97ebdd80ac6aa2877b26485203a8c920de8 Mon Sep 17 00:00:00 2001 From: Carlos Galan Cladera Date: Mon, 17 Feb 2025 14:06:14 +0100 Subject: [PATCH 03/20] fix(rich-consents): get authz details by type --- ...ParcelableRichConsentRequestedDetails.java | 27 +++++++++- .../sample/parcel/utils/ParcelableEntry.java | 52 +++++++++++++++++++ .../GuardianRichConsentRequestedDetails.java | 5 +- .../sdk/RichConsentRequestedDetails.java | 9 ++-- 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java diff --git a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java index 69edabc..9cf8d8b 100644 --- a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java +++ b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java @@ -6,13 +6,16 @@ import androidx.annotation.NonNull; import com.auth0.android.guardian.sdk.RichConsentRequestedDetails; +import com.auth0.guardian.sample.parcel.utils.ParcelableEntry; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; -import java.util.Collections; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class ParcelableRichConsentRequestedDetails implements RichConsentRequestedDetails, Parcelable { public static final Creator CREATOR = new Creator() { @@ -48,6 +51,17 @@ protected ParcelableRichConsentRequestedDetails(Parcel in) { audience = in.readString(); scope = in.createStringArray(); bindingMessage = in.readString(); + + List authorizationDetailsEntries = new ArrayList<>(); + in.readList(authorizationDetailsEntries, ParcelableEntry.class.getClassLoader()); + if (!authorizationDetailsEntries.isEmpty()) { + authorizationDetails = new ArrayList<>(); + for (ParcelableEntry entry : authorizationDetailsEntries) { + Map detail = new HashMap<>(); + detail.put(entry.getKey(), entry.getValue()); + authorizationDetails.add(detail); + } + } } public static ParcelableEnrollment fromJSON(String json) { @@ -80,6 +94,17 @@ public List> getAuthorizationDetails() { return authorizationDetails; } + @Override + public List getAuthorizationDetails(String type, Class clazz) { + List types = new ArrayList<>(); + for (Map item : authorizationDetails) { + if (Objects.equals(item.get("type"), type)) { + types.add(JSON.fromJson(JSON.toJsonTree(item), clazz)); + } + } + return types; + } + @Override public int describeContents() { return 0; diff --git a/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java b/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java new file mode 100644 index 0000000..de5cd04 --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java @@ -0,0 +1,52 @@ +package com.auth0.guardian.sample.parcel.utils; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +public class ParcelableEntry implements Parcelable { + private final String key; + private final Object value; + + public ParcelableEntry(String key, Object value) { + this.key = key; + this.value = value; + } + + protected ParcelableEntry(Parcel in) { + key = in.readString(); + value = in.readValue(getClass().getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ParcelableEntry createFromParcel(Parcel in) { + return new ParcelableEntry(in); + } + + @Override + public ParcelableEntry[] newArray(int size) { + return new ParcelableEntry[size]; + } + }; + + public String getKey() { + return key; + } + + public Object getValue() { + return value; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int i) { + dest.writeString(key); + dest.writeValue(value); + } +} diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java index 86e91a2..d584fb1 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/GuardianRichConsentRequestedDetails.java @@ -4,10 +4,7 @@ import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.sql.Array; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -63,7 +60,7 @@ public List getAuthorizationDetails(String type, Class clazz) { for (Map item : authorizationDetails) { if (Objects.equals(item.get("type"), type)) { - types.add(gson.fromJson(gson.toJson(gson.toJsonTree(item)), clazz)); + types.add(gson.fromJson(gson.toJsonTree(item), clazz)); } } return types; diff --git a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java index b8e5038..f7103ff 100644 --- a/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java +++ b/guardian/src/main/java/com/auth0/android/guardian/sdk/RichConsentRequestedDetails.java @@ -29,10 +29,11 @@ public interface RichConsentRequestedDetails { List> getAuthorizationDetails(); /** - * Authorization Details Type - * @param type - * @param clazz - * @return + * Rich Authorization Details Type + * + * @param type Type key + * @param clazz Class to cast the item + * @return The list of types found by the provided key. If none found, returns an empty list. */ List getAuthorizationDetails(String type, Class clazz); } From 2643cba6875fce7b954ace6120a8bbe4ee5e242d Mon Sep 17 00:00:00 2001 From: Carlos Galan Cladera Date: Mon, 17 Feb 2025 14:07:21 +0100 Subject: [PATCH 04/20] feat(app): consent fragments --- ...otificationWithConsentDetailsActivity.java | 46 ++++++---- .../consent/ConsentBasicDetailsFragment.java | 91 +++++++++++++++++++ .../ConsentPaymentInitiationFragment.java | 66 ++++++++++++++ ...vity_notification_with_consent_details.xml | 49 +--------- .../layout/fragment_consent_basic_details.xml | 57 ++++++++++++ .../fragment_consent_payment_initiation.xml | 73 +++++++++++++++ 6 files changed, 318 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentBasicDetailsFragment.java create mode 100644 app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java create mode 100644 app/src/main/res/layout/fragment_consent_basic_details.xml create mode 100644 app/src/main/res/layout/fragment_consent_payment_initiation.xml diff --git a/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java b/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java index 1a71f56..b1708f5 100644 --- a/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java +++ b/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java @@ -1,11 +1,14 @@ package com.auth0.guardian.sample; +import static android.view.View.VISIBLE; + import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Button; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -15,13 +18,10 @@ import com.auth0.android.guardian.sdk.ParcelableNotification; import com.auth0.android.guardian.sdk.RichConsent; import com.auth0.android.guardian.sdk.networking.Callback; +import com.auth0.guardian.sample.fragments.consent.ConsentBasicDetailsFragment; +import com.auth0.guardian.sample.payments.PaymentInitiationDetails; public class NotificationWithConsentDetailsActivity extends AppCompatActivity { - - private TextView bindingMessageText; - private TextView scopeText; - private TextView dateText; - private Guardian guardian; private ParcelableEnrollment enrollment; private ParcelableNotification notification; @@ -30,7 +30,7 @@ public class NotificationWithConsentDetailsActivity extends AppCompatActivity { static Intent getStartIntent(@NonNull Context context, @NonNull ParcelableNotification notification, @NonNull ParcelableEnrollment enrollment, - @NonNull ParcelableRichConsent consent) { + @NonNull ParcelableRichConsent consent) { if (!enrollment.getId().equals(notification.getEnrollmentId())) { final String message = String.format("Notification doesn't match enrollment (%s != %s)", notification.getEnrollmentId(), enrollment.getId()); @@ -58,15 +58,24 @@ protected void onCreate(Bundle savedInstanceState) { enrollment = intent.getParcelableExtra(Constants.ENROLLMENT); notification = intent.getParcelableExtra(Constants.NOTIFICATION); consentDetails = intent.getParcelableExtra(Constants.CONSENT); - +// paymentInitiationDetails = consentDetails +// .getRequestedDetails() +// .getAuthorizationDetails("payment_initiation", PaymentInitiationDetails.class).get(0); +// setupUI(); updateUI(); } private void setupUI() { - bindingMessageText = (TextView) findViewById(R.id.bindingMessage); - scopeText = (TextView) findViewById(R.id.scope); - dateText = (TextView) findViewById(R.id.dateText); +// bindingMessageText = (TextView) findViewById(R.id.bindingMessage); +// scopeText = (TextView) findViewById(R.id.scope); +// dateText = (TextView) findViewById(R.id.dateText); +// authorizationDetailsBlock = (LinearLayout) findViewById(R.id.richConsentPaymentInitiationDetails); +// +// if (paymentInitiationDetails != null) { +// authorizationDetailsBlock.setVisibility(VISIBLE); +// +// } Button rejectButton = (Button) findViewById(R.id.rejectButton); assert rejectButton != null; @@ -88,14 +97,15 @@ public void onClick(View v) { } private void updateUI() { - if (consentDetails != null) { - bindingMessageText.setText(consentDetails.getRequestedDetails().getBindingMessage()); - scopeText.setText(String.join(", ", consentDetails.getRequestedDetails().getScope())); - } else { - bindingMessageText.setText("N/A"); - scopeText.setText("N/A"); - } - dateText.setText(notification.getDate().toString()); + ConsentBasicDetailsFragment fragment = ConsentBasicDetailsFragment.newInstance( + consentDetails.getRequestedDetails().getBindingMessage(), + consentDetails.getRequestedDetails().getScope(), + notification.getDate().toString() + ); + + getSupportFragmentManager().beginTransaction() + .replace(R.id.consentDetailsFragmentContainer, fragment) + .commit(); } private void rejectRequested() { diff --git a/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentBasicDetailsFragment.java b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentBasicDetailsFragment.java new file mode 100644 index 0000000..fe821b3 --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentBasicDetailsFragment.java @@ -0,0 +1,91 @@ +package com.auth0.guardian.sample.fragments.consent; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.auth0.guardian.sample.R; + +/** + * A simple {@link Fragment} subclass. + * Use the {@link ConsentBasicDetailsFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class ConsentBasicDetailsFragment extends Fragment { + + private static final String BINDING_MESSAGE = "binding_message"; + private static final String SCOPE = "scope"; + private static final String DATE = "date"; + + private TextView bindingMessageText; + private TextView scopeText; + private TextView dateText; + + private String bindingMessage; + private String[] scope; + private String date; + + public ConsentBasicDetailsFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param bindingMessage Binding message. + * @param scope Scope. + * @param date Date. + * @return A new instance of fragment ConsentBasicDetailsFragment. + */ + public static ConsentBasicDetailsFragment newInstance(String bindingMessage, String[] scope, String date) { + ConsentBasicDetailsFragment fragment = new ConsentBasicDetailsFragment(); + Bundle args = new Bundle(); + args.putString(BINDING_MESSAGE, bindingMessage); + args.putStringArray(SCOPE, scope); + args.putString(DATE, date); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + bindingMessage = getArguments().getString(BINDING_MESSAGE); + scope = getArguments().getStringArray(SCOPE); + date = getArguments().getString(DATE); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_consent_basic_details, container, false); + } + + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + bindingMessageText = (TextView) view.findViewById(R.id.bindingMessage); + scopeText = (TextView) view.findViewById(R.id.scope); + dateText = (TextView) view.findViewById(R.id.dateText); + + updateUI(); + } + + private void updateUI() { + bindingMessageText.setText(bindingMessage); + scopeText.setText(String.join(", ", scope)); + dateText.setText(date); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java new file mode 100644 index 0000000..bd912ab --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java @@ -0,0 +1,66 @@ +package com.auth0.guardian.sample.fragments.consent; + +import android.os.Bundle; + +import androidx.fragment.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.auth0.guardian.sample.R; + +/** + * A simple {@link Fragment} subclass. + * Use the {@link ConsentPaymentInitiationFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class ConsentPaymentInitiationFragment extends Fragment { + + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + public ConsentPaymentInitiationFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ConsentPaymentInitiationFragment. + */ + // TODO: Rename and change types and number of parameters + public static ConsentPaymentInitiationFragment newInstance(String param1, String param2) { + ConsentPaymentInitiationFragment fragment = new ConsentPaymentInitiationFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_consent_payment_initiation, container, false); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_notification_with_consent_details.xml b/app/src/main/res/layout/activity_notification_with_consent_details.xml index 98602db..42a84e6 100644 --- a/app/src/main/res/layout/activity_notification_with_consent_details.xml +++ b/app/src/main/res/layout/activity_notification_with_consent_details.xml @@ -45,53 +45,10 @@ android:text="Do you approve this transaction?" android:textSize="34sp" /> - - - - - - - - - - - + android:layout_height="match_parent"/> diff --git a/app/src/main/res/layout/fragment_consent_basic_details.xml b/app/src/main/res/layout/fragment_consent_basic_details.xml new file mode 100644 index 0000000..66bfccc --- /dev/null +++ b/app/src/main/res/layout/fragment_consent_basic_details.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_consent_payment_initiation.xml b/app/src/main/res/layout/fragment_consent_payment_initiation.xml new file mode 100644 index 0000000..9f977f1 --- /dev/null +++ b/app/src/main/res/layout/fragment_consent_payment_initiation.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9c9ed492d013b591ae83badd1021f9632c79aca5 Mon Sep 17 00:00:00 2001 From: Carlos Galan Cladera Date: Tue, 18 Feb 2025 13:41:41 +0100 Subject: [PATCH 05/20] feat(app): payment initiation consent --- .../auth0/guardian/sample/MainActivity.java | 36 +---- .../guardian/sample/NotificationActivity.java | 113 ++++++++++--- ...otificationWithConsentDetailsActivity.java | 148 ------------------ .../sample/ParcelableRichConsent.java | 96 ------------ ...ParcelableRichConsentRequestedDetails.java | 120 -------------- .../AuthenticationRequestDetailsFragment.java | 116 ++++++++++++++ .../ConsentPaymentInitiationFragment.java | 83 +++++++--- .../sample/parcel/utils/ParcelableEntry.java | 52 ------ .../sample/payments/CreditorAccount.java | 19 +++ .../sample/payments/InstructedAmount.java | 19 +++ .../payments/PaymentInitiationDetails.java | 35 +++++ .../main/res/layout/activity_notification.xml | 71 +-------- ...vity_notification_with_consent_details.xml | 78 --------- ...ragment_authentication_request_details.xml | 78 +++++++++ .../layout/fragment_consent_basic_details.xml | 11 +- .../fragment_consent_payment_initiation.xml | 31 +++- .../GuardianRichConsentRequestedDetails.java | 6 +- 17 files changed, 465 insertions(+), 647 deletions(-) delete mode 100644 app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java delete mode 100644 app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsent.java delete mode 100644 app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java create mode 100644 app/src/main/java/com/auth0/guardian/sample/fragments/AuthenticationRequestDetailsFragment.java delete mode 100644 app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java create mode 100644 app/src/main/java/com/auth0/guardian/sample/payments/CreditorAccount.java create mode 100644 app/src/main/java/com/auth0/guardian/sample/payments/InstructedAmount.java create mode 100644 app/src/main/java/com/auth0/guardian/sample/payments/PaymentInitiationDetails.java delete mode 100644 app/src/main/res/layout/activity_notification_with_consent_details.xml create mode 100644 app/src/main/res/layout/fragment_authentication_request_details.xml diff --git a/app/src/main/java/com/auth0/guardian/sample/MainActivity.java b/app/src/main/java/com/auth0/guardian/sample/MainActivity.java index c346b6e..bb29e43 100644 --- a/app/src/main/java/com/auth0/guardian/sample/MainActivity.java +++ b/app/src/main/java/com/auth0/guardian/sample/MainActivity.java @@ -227,41 +227,7 @@ private void updateEnrollment(ParcelableEnrollment enrollment) { } private void onPushNotificationReceived(ParcelableNotification notification) { - Context context = this; - Intent standardNotificationActivityIntent = NotificationActivity.getStartIntent(context, notification, enrollment); - - if (notification.getTransactionLinkingId() == null) { - startActivity(standardNotificationActivityIntent); - } else { - try { - guardian.fetchConsent(notification, enrollment).start(new Callback() { - @Override - public void onSuccess(RichConsent consent) { - Intent intent = NotificationWithConsentDetailsActivity.getStartIntent( - context, - notification, - enrollment, - new ParcelableRichConsent(consent) - ); - startActivity(intent); - } - - @Override - public void onFailure(Throwable exception) { - if (exception instanceof GuardianException) { - GuardianException guardianException = (GuardianException) exception; - if (guardianException.isResourceNotFound()) { - startActivity(standardNotificationActivityIntent); - } - } - Log.e(TAG, "Error obtaining consent details", exception); - - } - }); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - Log.e(TAG, "Error requesting consent details", e); - } - } + startActivity(NotificationActivity.getStartIntent(this, notification, enrollment)); } @Override diff --git a/app/src/main/java/com/auth0/guardian/sample/NotificationActivity.java b/app/src/main/java/com/auth0/guardian/sample/NotificationActivity.java index d357254..b5e52e5 100644 --- a/app/src/main/java/com/auth0/guardian/sample/NotificationActivity.java +++ b/app/src/main/java/com/auth0/guardian/sample/NotificationActivity.java @@ -26,28 +26,38 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; +import android.util.Log; import android.view.View; import android.widget.Button; -import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; import com.auth0.android.guardian.sdk.Guardian; +import com.auth0.android.guardian.sdk.GuardianException; import com.auth0.android.guardian.sdk.ParcelableNotification; +import com.auth0.android.guardian.sdk.RichConsent; import com.auth0.android.guardian.sdk.networking.Callback; +import com.auth0.guardian.sample.fragments.AuthenticationRequestDetailsFragment; +import com.auth0.guardian.sample.fragments.consent.ConsentBasicDetailsFragment; +import com.auth0.guardian.sample.fragments.consent.ConsentPaymentInitiationFragment; +import com.auth0.guardian.sample.payments.PaymentInitiationDetails; + +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.List; public class NotificationActivity extends AppCompatActivity { - private TextView userText; - private TextView browserText; - private TextView osText; - private TextView locationText; - private TextView dateText; + private static final String TAG = NotificationActivity.class.getName(); private Guardian guardian; private ParcelableEnrollment enrollment; private ParcelableNotification notification; + private RichConsent richConsent; + static Intent getStartIntent(@NonNull Context context, @NonNull ParcelableNotification notification, @NonNull ParcelableEnrollment enrollment) { @@ -79,16 +89,38 @@ protected void onCreate(Bundle savedInstanceState) { setupUI(); + if (notification.getTransactionLinkingId() != null) { + try { + guardian.fetchConsent(notification, enrollment).start(new Callback() { + @Override + public void onSuccess(RichConsent response) { + richConsent = response; + updateUI(); + } + + @Override + public void onFailure(Throwable exception) { + if (exception instanceof GuardianException) { + GuardianException guardianException = (GuardianException) exception; + if (guardianException.isResourceNotFound()) { + // Render regular authentication request details + updateUI(); + } + } else { + Log.e(TAG, "Error requesting consent details", exception); + } + } + }); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + updateUI(); } private void setupUI() { - userText = (TextView) findViewById(R.id.userText); - browserText = (TextView) findViewById(R.id.browserText); - osText = (TextView) findViewById(R.id.osText); - locationText = (TextView) findViewById(R.id.locationText); - dateText = (TextView) findViewById(R.id.dateText); - + // TODO: spinner fragment Button rejectButton = (Button) findViewById(R.id.rejectButton); assert rejectButton != null; rejectButton.setOnClickListener(new View.OnClickListener() { @@ -109,17 +141,48 @@ public void onClick(View v) { } private void updateUI() { - userText.setText(enrollment.getUserId()); - browserText.setText( - String.format("%s, %s", - notification.getBrowserName(), - notification.getBrowserVersion())); - osText.setText( - String.format("%s, %s", - notification.getOsName(), - notification.getOsVersion())); - locationText.setText(notification.getLocation()); - dateText.setText(notification.getDate().toString()); + Fragment fragment; + if (richConsent == null) { + fragment = AuthenticationRequestDetailsFragment.newInstance( + enrollment.getUserId(), + + String.format("%s, %s", + notification.getBrowserName(), + notification.getBrowserVersion()), + String.format("%s, %s", + notification.getOsName(), + notification.getOsVersion()), + + notification.getLocation(), + notification.getDate().toString() + ); + } else { + List paymentInitiationDetailsList = richConsent + .getRequestedDetails() + .getAuthorizationDetails("payment_initiation", PaymentInitiationDetails.class); + + if (paymentInitiationDetailsList.isEmpty()) { + fragment = ConsentBasicDetailsFragment.newInstance( + richConsent.getRequestedDetails().getBindingMessage(), + richConsent.getRequestedDetails().getScope(), + notification.getDate().toString() + ); + } else { + PaymentInitiationDetails paymentDetails = paymentInitiationDetailsList.get(0); + fragment = ConsentPaymentInitiationFragment.newInstance( + richConsent.getRequestedDetails().getBindingMessage(), + paymentDetails.getRemittanceInformation(), + paymentDetails.getCreditorAccount().getAccountNumber(), + paymentDetails.getInstructedAmount().getCurrency(), + paymentDetails.getInstructedAmount().getAmount() + ); + } + } + + getSupportFragmentManager().beginTransaction() + .replace(R.id.authenticationDetailsFragmentContainer, fragment) + .commit(); + } private void rejectRequested() { diff --git a/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java b/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java deleted file mode 100644 index b1708f5..0000000 --- a/app/src/main/java/com/auth0/guardian/sample/NotificationWithConsentDetailsActivity.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.auth0.guardian.sample; - -import static android.view.View.VISIBLE; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import com.auth0.android.guardian.sdk.Guardian; -import com.auth0.android.guardian.sdk.ParcelableNotification; -import com.auth0.android.guardian.sdk.RichConsent; -import com.auth0.android.guardian.sdk.networking.Callback; -import com.auth0.guardian.sample.fragments.consent.ConsentBasicDetailsFragment; -import com.auth0.guardian.sample.payments.PaymentInitiationDetails; - -public class NotificationWithConsentDetailsActivity extends AppCompatActivity { - private Guardian guardian; - private ParcelableEnrollment enrollment; - private ParcelableNotification notification; - private RichConsent consentDetails; - - static Intent getStartIntent(@NonNull Context context, - @NonNull ParcelableNotification notification, - @NonNull ParcelableEnrollment enrollment, - @NonNull ParcelableRichConsent consent) { - if (!enrollment.getId().equals(notification.getEnrollmentId())) { - final String message = String.format("Notification doesn't match enrollment (%s != %s)", - notification.getEnrollmentId(), enrollment.getId()); - throw new IllegalArgumentException(message); - } - - Intent intent = new Intent(context, NotificationWithConsentDetailsActivity.class); - intent.putExtra(Constants.ENROLLMENT, enrollment); - intent.putExtra(Constants.NOTIFICATION, notification); - intent.putExtra(Constants.CONSENT, consent); - return intent; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_notification_with_consent_details); - - guardian = new Guardian.Builder() - .url(Uri.parse(getString(R.string.tenant_url))) - .enableLogging() - .build(); - - Intent intent = getIntent(); - enrollment = intent.getParcelableExtra(Constants.ENROLLMENT); - notification = intent.getParcelableExtra(Constants.NOTIFICATION); - consentDetails = intent.getParcelableExtra(Constants.CONSENT); -// paymentInitiationDetails = consentDetails -// .getRequestedDetails() -// .getAuthorizationDetails("payment_initiation", PaymentInitiationDetails.class).get(0); -// - setupUI(); - updateUI(); - } - - private void setupUI() { -// bindingMessageText = (TextView) findViewById(R.id.bindingMessage); -// scopeText = (TextView) findViewById(R.id.scope); -// dateText = (TextView) findViewById(R.id.dateText); -// authorizationDetailsBlock = (LinearLayout) findViewById(R.id.richConsentPaymentInitiationDetails); -// -// if (paymentInitiationDetails != null) { -// authorizationDetailsBlock.setVisibility(VISIBLE); -// -// } - - Button rejectButton = (Button) findViewById(R.id.rejectButton); - assert rejectButton != null; - rejectButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - rejectRequested(); - } - }); - - Button allowButton = (Button) findViewById(R.id.allowButton); - assert allowButton != null; - allowButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - allowRequested(); - } - }); - } - - private void updateUI() { - ConsentBasicDetailsFragment fragment = ConsentBasicDetailsFragment.newInstance( - consentDetails.getRequestedDetails().getBindingMessage(), - consentDetails.getRequestedDetails().getScope(), - notification.getDate().toString() - ); - - getSupportFragmentManager().beginTransaction() - .replace(R.id.consentDetailsFragmentContainer, fragment) - .commit(); - } - - private void rejectRequested() { - guardian - .reject(notification, enrollment) - .start(new DialogCallback<>(this, - R.string.progress_title_please_wait, - R.string.progress_message_reject, - new Callback() { - @Override - public void onSuccess(Void response) { - finish(); - } - - @Override - public void onFailure(Throwable exception) { - - } - })); - } - - private void allowRequested() { - guardian - .allow(notification, enrollment) - .start(new DialogCallback<>(this, - R.string.progress_title_please_wait, - R.string.progress_message_allow, - new Callback() { - @Override - public void onSuccess(Void response) { - finish(); - } - - @Override - public void onFailure(Throwable exception) { - - } - })); - } -} diff --git a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsent.java b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsent.java deleted file mode 100644 index ef419d6..0000000 --- a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsent.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.auth0.guardian.sample; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import com.auth0.android.guardian.sdk.RichConsent; -import com.auth0.android.guardian.sdk.RichConsentRequestedDetails; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.SerializedName; - -public class ParcelableRichConsent implements RichConsent, Parcelable { - public static final Creator CREATOR = new Creator() { - @Override - public ParcelableRichConsent createFromParcel(Parcel in) { - return new ParcelableRichConsent(in); - } - - @Override - public ParcelableRichConsent[] newArray(int size) { - return new ParcelableRichConsent[size]; - } - }; - - private static final Gson JSON = new GsonBuilder().create(); - - @SerializedName("id") - private final String id; - @SerializedName("requested_details") - private final ParcelableRichConsentRequestedDetails requestedDetails; - @SerializedName("created_at") - private final String createdAt; - @SerializedName("expires_at") - private final String expiresAt; - - public ParcelableRichConsent(RichConsent richConsent) { - this.id = richConsent.getId(); - this.createdAt = this.getCreatedAt(); - this.expiresAt = this.getExpiresAt(); - this.requestedDetails = new ParcelableRichConsentRequestedDetails(richConsent.getRequestedDetails()); - } - - protected ParcelableRichConsent(Parcel in) { - id = in.readString(); - createdAt = in.readString(); - expiresAt = in.readString(); - requestedDetails = in.readParcelable(ParcelableRichConsentRequestedDetails.class.getClassLoader()); - } - - public static ParcelableEnrollment fromJSON(String json) { - return JSON.fromJson(json, ParcelableEnrollment.class); - } - - public String toJSON() { - return JSON.toJson(this); - } - - @NonNull - @Override - public String getId() { - return id; - } - - @NonNull - @Override - public RichConsentRequestedDetails getRequestedDetails() { - return requestedDetails; - } - - @NonNull - @Override - public String getCreatedAt() { - return createdAt; - } - - @NonNull - @Override - public String getExpiresAt() { - return expiresAt; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(id); - dest.writeString(createdAt); - dest.writeString(expiresAt); - dest.writeParcelable(requestedDetails, flags); - } -} diff --git a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java b/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java deleted file mode 100644 index 9cf8d8b..0000000 --- a/app/src/main/java/com/auth0/guardian/sample/ParcelableRichConsentRequestedDetails.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.auth0.guardian.sample; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import com.auth0.android.guardian.sdk.RichConsentRequestedDetails; -import com.auth0.guardian.sample.parcel.utils.ParcelableEntry; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.SerializedName; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -public class ParcelableRichConsentRequestedDetails implements RichConsentRequestedDetails, Parcelable { - public static final Creator CREATOR = new Creator() { - @Override - public ParcelableRichConsentRequestedDetails createFromParcel(Parcel in) { - return new ParcelableRichConsentRequestedDetails(in); - } - - @Override - public ParcelableRichConsentRequestedDetails[] newArray(int size) { - return new ParcelableRichConsentRequestedDetails[size]; - } - }; - private static final Gson JSON = new GsonBuilder().create(); - - @SerializedName("audience") - private final String audience; - @SerializedName("scope") - private final String[] scope; - @SerializedName("bindingMessage") - private final String bindingMessage; - @SerializedName("authorizationDetails") - private List> authorizationDetails; - - public ParcelableRichConsentRequestedDetails(RichConsentRequestedDetails requestedDetails) { - audience = requestedDetails.getAudience(); - scope = requestedDetails.getScope(); - bindingMessage = requestedDetails.getBindingMessage(); - authorizationDetails = requestedDetails.getAuthorizationDetails(); - } - - protected ParcelableRichConsentRequestedDetails(Parcel in) { - audience = in.readString(); - scope = in.createStringArray(); - bindingMessage = in.readString(); - - List authorizationDetailsEntries = new ArrayList<>(); - in.readList(authorizationDetailsEntries, ParcelableEntry.class.getClassLoader()); - if (!authorizationDetailsEntries.isEmpty()) { - authorizationDetails = new ArrayList<>(); - for (ParcelableEntry entry : authorizationDetailsEntries) { - Map detail = new HashMap<>(); - detail.put(entry.getKey(), entry.getValue()); - authorizationDetails.add(detail); - } - } - } - - public static ParcelableEnrollment fromJSON(String json) { - return JSON.fromJson(json, ParcelableEnrollment.class); - } - - public String toJSON() { - return JSON.toJson(this); - } - - @NonNull - @Override - public String getAudience() { - return audience; - } - - @NonNull - @Override - public String[] getScope() { - return scope; - } - - @Override - public String getBindingMessage() { - return bindingMessage; - } - - @Override - public List> getAuthorizationDetails() { - return authorizationDetails; - } - - @Override - public List getAuthorizationDetails(String type, Class clazz) { - List types = new ArrayList<>(); - for (Map item : authorizationDetails) { - if (Objects.equals(item.get("type"), type)) { - types.add(JSON.fromJson(JSON.toJsonTree(item), clazz)); - } - } - return types; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(audience); - dest.writeStringArray(scope); - dest.writeString(bindingMessage); - dest.writeList(authorizationDetails); - } -} diff --git a/app/src/main/java/com/auth0/guardian/sample/fragments/AuthenticationRequestDetailsFragment.java b/app/src/main/java/com/auth0/guardian/sample/fragments/AuthenticationRequestDetailsFragment.java new file mode 100644 index 0000000..c8d804a --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/fragments/AuthenticationRequestDetailsFragment.java @@ -0,0 +1,116 @@ +package com.auth0.guardian.sample.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.auth0.guardian.sample.R; + +import org.w3c.dom.Text; + +/** + * A simple {@link Fragment} subclass. + * Use the {@link AuthenticationRequestDetailsFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class AuthenticationRequestDetailsFragment extends Fragment { + + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String USER = "user"; + private static final String BROWSER = "browser"; + private static final String OS = "os"; + private static final String LOCATION = "location"; + private static final String DATE = "date"; + + private TextView userText; + private TextView browserText; + private TextView osText; + private TextView locationText; + private TextView dateText; + + private String user; + private String browser; + private String os; + private String location; + private String date; + + public AuthenticationRequestDetailsFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param user User text + * @param browser Browser text + * @param os OS text + * @param location Location text + * @param date Date text + * + * @return A new instance of fragment AuthenticationRequestDetailsFragment. + */ + // TODO: Rename and change types and number of parameters + public static AuthenticationRequestDetailsFragment newInstance(String user, + String browser, + String os, + String location, + String date + ) { + AuthenticationRequestDetailsFragment fragment = new AuthenticationRequestDetailsFragment(); + Bundle args = new Bundle(); + args.putString(USER, user); + args.putString(BROWSER, browser); + args.putString(OS, os); + args.putString(LOCATION, location); + args.putString(DATE, date); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + user = getArguments().getString(USER); + browser = getArguments().getString(BROWSER); + os = getArguments().getString(OS); + location = getArguments().getString(LOCATION); + date = getArguments().getString(DATE); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_authentication_request_details, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + userText = view.findViewById(R.id.userText); + browserText = view.findViewById(R.id.browserText); + osText = view.findViewById(R.id.osText); + locationText = view.findViewById(R.id.locationText); + dateText = view.findViewById(R.id.dateText); + + updateUI(); + } + + private void updateUI() { + userText.setText(user); + browserText.setText(browser); + osText.setText(os); + locationText.setText(location); + dateText.setText(date); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java index bd912ab..66bad81 100644 --- a/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java +++ b/app/src/main/java/com/auth0/guardian/sample/fragments/consent/ConsentPaymentInitiationFragment.java @@ -1,12 +1,14 @@ package com.auth0.guardian.sample.fragments.consent; import android.os.Bundle; - -import androidx.fragment.app.Fragment; - import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import com.auth0.guardian.sample.R; @@ -17,14 +19,24 @@ */ public class ConsentPaymentInitiationFragment extends Fragment { - // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_PARAM1 = "param1"; - private static final String ARG_PARAM2 = "param2"; + private static final String BINDING_MESSAGE = "binding_message"; + private static final String REMITTANCE_INFO = "remittance_info"; + private static final String CREDITOR_ACCOUNT = "creditor_account"; + private static final String AMOUNT_CURRENCY = "amount_currency"; + private static final String AMOUNT = "amount"; + + private TextView bindingMessageText; + private TextView remittanceInfoText; + private TextView creditorAccountText; + private TextView amountCurrencyText; + private TextView amountText; - // TODO: Rename and change types of parameters - private String mParam1; - private String mParam2; + private String bindingMessage; + private String remittanceInfo; + private String creditorAccount; + private String amountCurrency; + private String amount; public ConsentPaymentInitiationFragment() { // Required empty public constructor @@ -34,16 +46,26 @@ public ConsentPaymentInitiationFragment() { * Use this factory method to create a new instance of * this fragment using the provided parameters. * - * @param param1 Parameter 1. - * @param param2 Parameter 2. + * @param bindingMessage Transaction binding message + * @param remittanceInfo Payment remittance info. + * @param creditorAccount Payment creditor account. + * @param amountCurrency Payment amount currencty. + * @param amount Payment amount + * * @return A new instance of fragment ConsentPaymentInitiationFragment. */ - // TODO: Rename and change types and number of parameters - public static ConsentPaymentInitiationFragment newInstance(String param1, String param2) { + public static ConsentPaymentInitiationFragment newInstance(String bindingMessage, + String remittanceInfo, + String creditorAccount, + String amountCurrency, + String amount) { ConsentPaymentInitiationFragment fragment = new ConsentPaymentInitiationFragment(); Bundle args = new Bundle(); - args.putString(ARG_PARAM1, param1); - args.putString(ARG_PARAM2, param2); + args.putString(BINDING_MESSAGE, bindingMessage); + args.putString(REMITTANCE_INFO, remittanceInfo); + args.putString(CREDITOR_ACCOUNT, creditorAccount); + args.putString(AMOUNT_CURRENCY, amountCurrency); + args.putString(AMOUNT, amount); fragment.setArguments(args); return fragment; } @@ -52,15 +74,38 @@ public static ConsentPaymentInitiationFragment newInstance(String param1, String public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { - mParam1 = getArguments().getString(ARG_PARAM1); - mParam2 = getArguments().getString(ARG_PARAM2); + bindingMessage = getArguments().getString(BINDING_MESSAGE); + remittanceInfo = getArguments().getString(REMITTANCE_INFO); + creditorAccount = getArguments().getString(CREDITOR_ACCOUNT); + amountCurrency = getArguments().getString(AMOUNT_CURRENCY); + amount = getArguments().getString(AMOUNT); } } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_consent_payment_initiation, container, false); } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + bindingMessageText = view.findViewById(R.id.bindingMessage); + remittanceInfoText = view.findViewById(R.id.paymentRemittanceInformationText); + creditorAccountText = view.findViewById(R.id.paymentAccountText); + amountCurrencyText = view.findViewById(R.id.paymentAmountCurrencyText); + amountText = view.findViewById(R.id.paymentAmountText); + + updateUI(); + } + + private void updateUI() { + bindingMessageText.setText(bindingMessage); + remittanceInfoText.setText(remittanceInfo); + creditorAccountText.setText(creditorAccount); + amountCurrencyText.setText(amountCurrency); + amountText.setText(amount); + } } \ No newline at end of file diff --git a/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java b/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java deleted file mode 100644 index de5cd04..0000000 --- a/app/src/main/java/com/auth0/guardian/sample/parcel/utils/ParcelableEntry.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.auth0.guardian.sample.parcel.utils; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -public class ParcelableEntry implements Parcelable { - private final String key; - private final Object value; - - public ParcelableEntry(String key, Object value) { - this.key = key; - this.value = value; - } - - protected ParcelableEntry(Parcel in) { - key = in.readString(); - value = in.readValue(getClass().getClassLoader()); - } - - public static final Creator CREATOR = new Creator() { - @Override - public ParcelableEntry createFromParcel(Parcel in) { - return new ParcelableEntry(in); - } - - @Override - public ParcelableEntry[] newArray(int size) { - return new ParcelableEntry[size]; - } - }; - - public String getKey() { - return key; - } - - public Object getValue() { - return value; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int i) { - dest.writeString(key); - dest.writeValue(value); - } -} diff --git a/app/src/main/java/com/auth0/guardian/sample/payments/CreditorAccount.java b/app/src/main/java/com/auth0/guardian/sample/payments/CreditorAccount.java new file mode 100644 index 0000000..e6f7b21 --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/payments/CreditorAccount.java @@ -0,0 +1,19 @@ +package com.auth0.guardian.sample.payments; + +public class CreditorAccount { + private final String iban; + private final String accountNumber; + + public CreditorAccount(String iban, String accountNumber) { + this.iban = iban; + this.accountNumber = accountNumber; + } + + public String getIban() { + return iban; + } + + public String getAccountNumber() { + return accountNumber; + } +} diff --git a/app/src/main/java/com/auth0/guardian/sample/payments/InstructedAmount.java b/app/src/main/java/com/auth0/guardian/sample/payments/InstructedAmount.java new file mode 100644 index 0000000..4576a48 --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/payments/InstructedAmount.java @@ -0,0 +1,19 @@ +package com.auth0.guardian.sample.payments; + +public class InstructedAmount { + private final String currency; + private final String amount; + + public InstructedAmount(String currency, String amount) { + this.currency = currency; + this.amount = amount; + } + + public String getCurrency() { + return currency; + } + + public String getAmount() { + return amount; + } +} diff --git a/app/src/main/java/com/auth0/guardian/sample/payments/PaymentInitiationDetails.java b/app/src/main/java/com/auth0/guardian/sample/payments/PaymentInitiationDetails.java new file mode 100644 index 0000000..f958ecf --- /dev/null +++ b/app/src/main/java/com/auth0/guardian/sample/payments/PaymentInitiationDetails.java @@ -0,0 +1,35 @@ +package com.auth0.guardian.sample.payments; + +public class PaymentInitiationDetails { + private final InstructedAmount instructedAmount; + private final String creditorName; + private final CreditorAccount creditorAccount; + private final String remittanceInformation; + + public PaymentInitiationDetails(InstructedAmount instructedAmount, String creditorName, CreditorAccount creditorAccount, String remittanceInformation) { + this.instructedAmount = instructedAmount; + this.creditorName = creditorName; + this.creditorAccount = creditorAccount; + this.remittanceInformation = remittanceInformation; + } + + public String getType() { + return "payment_initiation"; + } + + public InstructedAmount getInstructedAmount() { + return instructedAmount; + } + + public String getCreditorName() { + return creditorName; + } + + public CreditorAccount getCreditorAccount() { + return creditorAccount; + } + + public String getRemittanceInformation() { + return remittanceInformation; + } +} diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml index 95e796f..f7d1af3 100644 --- a/app/src/main/res/layout/activity_notification.xml +++ b/app/src/main/res/layout/activity_notification.xml @@ -36,75 +36,10 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent"/> diff --git a/app/src/main/res/layout/activity_notification_with_consent_details.xml b/app/src/main/res/layout/activity_notification_with_consent_details.xml deleted file mode 100644 index 42a84e6..0000000 --- a/app/src/main/res/layout/activity_notification_with_consent_details.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -