# sap-cdc-sdk-android **Repository Path**: mirrors_SAP/sap-cdc-sdk-android ## Basic Information - **Project Name**: sap-cdc-sdk-android - **Description**: SAP Customer Data Cloud SDK for Android. The SDK provides an interface to integrate SAP Customer Data Cloud services in your Android application. - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-12-11 - **Last Updated**: 2026-03-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [![REUSE status](https://api.reuse.software/badge/github.com/SAP/sap-customer-data-cloud-sdk-for-android)](https://api.reuse.software/info/github.com/SAP/sap-customer-data-cloud-sdk-for-android) # Description SAP Customer Data Cloud SDK for Android is a comprehensive solution for integrating SAP Customer Data Cloud services into Android applications. The SDK provides a modern, type-safe Kotlin DSL for authentication flows, session management, and user account operations with powerful callback chaining and transformation capabilities. **Key Features:** - Modern Kotlin DSL with coroutine support - Advanced callback system with side-effects and overrides - Biometric authentication integration - Web Screen-Sets support with JavaScript bridge - Social provider authentication (Google, Facebook, WeChat, Line) - Two-factor authentication and OTP support - Passkey authentication - Push notifications for authentication # Requirements - **Android API Level:** 24+ (Android 7.0) - **Kotlin:** 1.9+ - **Java:** 11+ - **Gradle:** 8.0+ # Implementation Add the SDK dependency to your app's `build.gradle.kts`: ```kotlin dependencies { implementation("com.sap.oss.cdc-android-sdk:cdc-android-sdk:0.3.0") } ``` The library is available on [MavenCentral](https://search.maven.org/artifact/com.sap.oss.cdc-android-sdk/cdc-android-sdk). # SDK Setup ## SiteConfig The `SiteConfig` class manages site-specific configuration data required for SDK initialization. It automatically retrieves configuration parameters from your app's `strings.xml` file. ```kotlin // Initialize the configuration object val siteConfig = SiteConfig(context) ``` **Required configuration in `strings.xml`:** ```xml YOUR_API_KEY_HERE YOUR_API_DOMAIN_HERE YOUR_CNAME_HERE ``` **Multi-flavor Support:** The SDK supports multiple build flavors by placing flavor-specific `strings.xml` files in corresponding directories (e.g., `src/debug/res/values/strings.xml`, `src/release/res/values/strings.xml`). ## AuthenticationService The `AuthenticationService` is the primary interface for all authentication operations. Initialize it with your `SiteConfig`: ```kotlin // Initialize authentication service val authenticationService = AuthenticationService(siteConfig) ``` **Core Methods:** - `authenticate()` - Handle authentication flows (login, register, social, etc.) - `account()` - Manage user account data (get/set profile information) - `session()` - Manage user sessions and security levels # Kotlin DSL AuthCallbacks The SDK provides a powerful Kotlin DSL for authentication callbacks with support for response transformation, side-effects, and multiple event handlers. ## Basic Callback Usage ```kotlin suspend fun login(email: String, password: String) { val credentials = Credentials(email, password) authenticationService.authenticate().login() .credentials(credentials) { onSuccess = { authSuccess -> // Handle successful login navigateToMainScreen() } onError = { authError -> // Handle login error showError(authError.message) } onTwoFactorRequired = { context -> // Handle TFA requirement navigateToTwoFactorScreen(context) } } } ``` ## Advanced DSL Features ### Callback Registration Patterns There are two distinct patterns for registering callbacks, each with different registration order requirements: #### Pattern 1: Side Effect Pattern - Register User Callbacks FIRST When wrapping SDK calls to add side effects (e.g., state management, analytics), register the user's callbacks **FIRST**, then add your side effects: ```kotlin // ✅ CORRECT: Side Effect Pattern suspend fun login(credentials: Credentials, authCallbacks: AuthCallbacks.() -> Unit) { authenticationService.authenticate().login() .credentials(credentials) { // 1. Register user's callbacks FIRST authCallbacks() // 2. Add your side effects AFTER doOnSuccess { authSuccess -> updateLocalState(authSuccess) analytics.track("login_success") } } } ``` **Why this order?** Your side effects execute before the user's callbacks, allowing you to prepare state or log events before their code runs. #### Pattern 2: Override Pattern - Register User Callbacks AFTER When transforming responses with overrides, set up the override **FIRST**, then register user callbacks **AFTER**: ```kotlin // ✅ CORRECT: Override Pattern suspend fun linkToProvider( linkingContext: LinkingContext, authCallbacks: AuthCallbacks.() -> Unit ) { signIn(parameters) { // 1. Set up override transformation FIRST doOnAnyAndOverride { authResult -> when (authResult) { is AuthResult.Success -> transformSuccess(authResult) else -> authResult } } // 2. Register user callbacks AFTER override authCallbacks() } } ``` **Why this order?** The override transforms the data first, then the user's callbacks receive the transformed data. #### Pattern Comparison Both patterns use the **same DSL syntax** - only the **order differs**: ```kotlin // Pattern 1: Side Effects (callbacks FIRST, side effects AFTER) credentials(credentials) { authCallbacks() // User callbacks FIRST doOnSuccess { /* side */ } // Side effects AFTER } // Pattern 2: Overrides (override FIRST, callbacks AFTER) signIn(parameters) { doOnAnyAndOverride { /* transform */ } // Override FIRST authCallbacks() // User callbacks AFTER } ``` #### Common Mistakes ```kotlin // ❌ WRONG: Side Effect Pattern with callbacks in wrong order authenticationService.authenticate().login() .credentials(credentials) { doOnSuccess { /* side effect */ } authCallbacks() // Side effects run before user code - incorrect } // ❌ WRONG: Override Pattern with callbacks in wrong order signIn(parameters) { authCallbacks() // User callbacks registered first doOnAnyAndOverride { /* transform */ } // Override after - won't transform } ``` ### Side Effects vs. Overrides: Understanding the Difference The SDK provides two distinct callback mechanisms with different purposes and execution behaviors: #### Side Effects with `doOn*` Methods **Purpose:** Execute additional logic **alongside** your main callbacks without modifying the response data. **When to use:** Logging, analytics, state management, notifications - any action that should happen in addition to your main logic. **Registration order:** User callbacks FIRST with `authCallbacks()`, then side effects AFTER. **Execution order:** Side effects execute **before** main callbacks, but both receive the **same original data**. ```kotlin // Wrapped SDK call with side effects suspend fun login(credentials: Credentials, authCallbacks: AuthCallbacks.() -> Unit) { authenticationService.authenticate().login() .credentials(credentials) { // 1. Register user's callbacks FIRST authCallbacks() // 2. Add side effect AFTER registration doOnSuccess { authSuccess -> analytics.track("login_success", authSuccess.userData) updateLocalCache(authSuccess) // This doesn't change what the main callback receives } } } // Usage - user's callback receives original data login(credentials) { onSuccess = { authSuccess -> // Receives the same original authSuccess as the side effect navigateToMainScreen() } } ``` #### Response Transformation with Override Methods **Purpose:** **Transform or modify** the response data before it reaches your main callbacks. **When to use:** Data transformation, localization, enrichment, filtering - when you need to change what your main callbacks receive. **Registration order:** Override FIRST with `doOnSuccessAndOverride {}`, then user callbacks AFTER with `authCallbacks()`. **Execution order:** Overrides execute **first** and transform the data, then main callbacks receive the **transformed data**. ```kotlin // Wrapped SDK call with override suspend fun linkToProvider( linkingContext: LinkingContext, authCallbacks: AuthCallbacks.() -> Unit ) { signIn(parameters) { // 1. Set up override transformation FIRST doOnSuccessAndOverride { authSuccess -> // Transform the response authSuccess.copy( userData = authSuccess.userData + mapOf( "loginTime" to System.currentTimeMillis(), "deviceInfo" to getDeviceInfo() ) ) } // 2. Register user callbacks AFTER override authCallbacks() } } // Usage - user's callback receives transformed data linkToProvider(linkingContext) { onSuccess = { transformedSuccess -> // This receives the enhanced data with loginTime and deviceInfo handleLogin(transformedSuccess) } } ``` ### Execution Flow Comparison **With Side Effects:** ``` 1. API Response arrives 2. doOnSuccess executes (original data) 3. onSuccess executes (same original data) ``` **With Overrides:** ``` 1. API Response arrives 2. doOnSuccessAndOverride executes and transforms data 3. onSuccess executes (transformed data) ``` **Combined Usage:** ```kotlin authenticationService.authenticate().login() .credentials(credentials) { // 1. Override transforms the data first doOnSuccessAndOverride { authSuccess -> authSuccess.copy(userData = authSuccess.userData + ("enhanced" to true)) } // 2. Side effect executes with transformed data doOnSuccess { transformedSuccess -> analytics.track("login", transformedSuccess.userData) // Has "enhanced" = true } // 3. Main callback receives the same transformed data onSuccess = { transformedSuccess -> handleLogin(transformedSuccess) // Also has "enhanced" = true } } ``` ### Universal Override for All Callback Types Handle any callback type with a single universal override that can **transform the result type**. Since this is an override, it must be registered **FIRST**, then user callbacks **AFTER**. **Key Feature:** The universal override can change the result type (e.g., Error → Success), and the SDK's internal callback router will invoke only the callbacks matching the **final transformed type**. ```kotlin // Wrapped SDK call with universal override for error recovery suspend fun loginWithErrorRecovery( credentials: Credentials, authCallbacks: AuthCallbacks.() -> Unit ) { authenticationService.authenticate().login() .credentials(credentials) { // 1. Universal override FIRST - can transform ANY result type to ANY other type doOnAnyAndOverride { authResult -> when (authResult) { is AuthResult.Error -> { // Example: Attempt recovery via alternative endpoint val recoveryAttempt = tryAlternativeAuthEndpoint(credentials) if (recoveryAttempt != null) { // Transform Error → Success // Only onSuccess callback will execute (not onError) AuthResult.Success(recoveryAttempt) } else { // Keep as Error // Only onError callback will execute authResult } } is AuthResult.Success -> { // Enrich success data val enriched = authResult.authSuccess.copy( userData = authResult.authSuccess.userData + ("loginTime" to System.currentTimeMillis()) ) AuthResult.Success(enriched) } // Pass through other types unchanged else -> authResult } } // 2. User callbacks AFTER override authCallbacks() } } // Usage - callbacks receive the TRANSFORMED result loginWithErrorRecovery(credentials) { onSuccess = { authSuccess -> // Executes if original succeeded OR if error was recovered navigateToMainScreen() } onError = { authError -> // Only executes if recovery also failed showError(authError.message) } } ``` **How the callback router works:** 1. **Universal override executes first** and can transform the result type 2. **SDK's internal router** evaluates the **final result type** after transformation 3. **Only matching callbacks execute** based on the final type: - If final result is `AuthResult.Success` → only `onSuccess` executes - If final result is `AuthResult.Error` → only `onError` executes - If final result is `AuthResult.TwoFactorRequired` → only `onTwoFactorRequired` executes This routing mechanism ensures type safety and prevents callbacks from executing for the wrong result type. ### Context Update Callbacks Context update callbacks are specifically designed to handle **interrupted authentication flows** that require additional steps to complete. These flows are "interrupted" because they cannot complete immediately and need user intervention or additional data. **Purpose:** Handle multi-step authentication scenarios where the SDK provides enriched context data to help you resolve the interruption and continue the flow. **Common interrupted flows:** - **Account Linking:** When a social login conflicts with an existing account - **Two-Factor Authentication:** When TFA is required for login/registration - **Registration Missing Fields:** When required profile fields are missing during registration **How it works:** 1. Initial authentication attempt fails with a specific interruption error 2. SDK enriches the context with additional data needed for resolution 3. Context update callback receives the enriched data 4. You use this data to present appropriate UI and continue the flow 5. Flow completion triggers normal success/error callbacks ```kotlin authenticationService.authenticate().login() .credentials(credentials) { // Handle the interruption - basic context onTwoFactorRequired = { context -> // Initial interruption - minimal context data navigateToTwoFactorScreen(context) } // Handle enriched context - enhanced data for flow completion onTwoFactorContextUpdated = { enrichedContext -> // SDK-enriched context with additional data: // - Available email addresses for TFA // - Phone numbers registered for SMS // - QR codes for authenticator apps // - Tokens needed for flow continuation updateTwoFactorUI(enrichedContext) // Use enriched data to help user complete the flow if (enrichedContext.emails?.isNotEmpty() == true) { showEmailTwoFactorOption(enrichedContext.emails!!) } if (enrichedContext.phones?.isNotEmpty() == true) { showSMSTwoFactorOption(enrichedContext.phones!!) } } } ``` **Example: Account Linking Flow** ```kotlin authenticationService.authenticate().provider().signIn(activity, provider) { onLinkingRequired = { context -> // Basic linking context - account conflict detected showAccountLinkingScreen(context) } onLinkingContextUpdated = { enrichedContext -> // Enriched context with detailed conflicting account info // - Existing account details // - Available linking options // - Tokens for linking continuation updateLinkingUI(enrichedContext.conflictingAccounts) // Present user with clear linking choices showLinkingOptions(enrichedContext) } } ``` These callbacks work **in addition to** standard callbacks, providing you with the detailed information needed to guide users through complex authentication scenarios seamlessly. ## Real-World Example: Complete Authentication Flow ```kotlin class AuthenticationFlowDelegate(context: Context) { private val authenticationService = AuthenticationService(SiteConfig(context)) suspend fun login(credentials: Credentials, authCallbacks: AuthCallbacks.() -> Unit) { authenticationService.authenticate().login() .credentials(credentials) { // Apply user's callbacks first authCallbacks() // Add state management side-effect doOnSuccess { authSuccess -> try { val accountData = Json.decodeFromString(authSuccess.jsonData) _userAccount.value = accountData _isAuthenticated.value = true } catch (e: Exception) { // Handle parsing errors gracefully } } } } } // Usage authFlowDelegate.login(credentials) { onSuccess = { navigateToMainScreen() } onError = { error -> showErrorMessage(error.message) } onTwoFactorRequired = { context -> navigateToTwoFactorScreen(context) } } ``` This powerful DSL system allows you to: - **Chain multiple callbacks** for the same event type - **Transform responses** before they reach your UI - **Add side effects** for analytics, logging, and state management - **Handle complex flows** with enriched context data - **Maintain clean separation** between business logic and UI logic # Session Event Bus The SDK provides a lifecycle-aware event bus for session and messaging events. This allows your app to react to session changes (expiration, refresh, verification) and push notifications across your entire application in a decoupled, type-safe manner. ## Event Types ### Session Events **Core Session Events (Always Available):** - `SessionEvent.SessionExpired` - Session has expired - `SessionEvent.SessionRefreshed` - Session was successfully refreshed - `SessionEvent.VerifySession` - Session verification requested **Session Validation Events (Opt-In):** These events only fire if you enable session validation when initializing the AuthenticationService: ```kotlin val authenticationService = AuthenticationService(siteConfig) .registerForSessionValidation( config = SessionValidationConfig( intervalMinutes = 20L, // Check session every 20 minutes enabled = true // Enable validation ) ) ``` **Validation Events:** - `SessionEvent.ValidationStarted` - Session validation has started - `SessionEvent.ValidationSucceeded` - Session validation succeeded - `SessionEvent.ValidationFailed` - Session validation failed (includes reason) ⚠️ **Note:** Session validation adds periodic background checks to ensure session validity. Only enable if your app requires this level of session monitoring. ### Message Events Push notification and Firebase messaging events: - `MessageEvent.TokenReceived` - FCM token received - `MessageEvent.RemoteMessageReceived` - Push notification received - `MessageEvent.NotificationActionReceived` - Notification action triggered ## Usage ### Lifecycle-Aware Subscription (Activities/Fragments) For UI components that implement `LifecycleOwner` (Activities, Fragments), use lifecycle-aware subscriptions that automatically clean up: ```kotlin class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize event bus (only needed once in your app) if (!CDCEventBusProvider.isInitialized()) { CDCEventBusProvider.initialize() } // Subscribe with automatic lifecycle management subscribeToSessionEvents { event -> when (event) { is SessionEvent.SessionExpired -> { // Handle session expiration navigateToLogin() } is SessionEvent.SessionRefreshed -> { // Handle session refresh updateSessionUI() } is SessionEvent.ValidationFailed -> { // Handle validation failure showSessionWarning(event.reason) } else -> { /* Handle other events */ } } } } // No cleanup needed - automatically unsubscribes when lifecycle ends } ``` ### Manual Subscription (ViewModels/Services) For components without lifecycle support (ViewModels, Services), use manual subscriptions with explicit cleanup: ```kotlin class MainActivityViewModel( private val authenticationFlowDelegate: AuthenticationFlowDelegate ) : ViewModel() { private var sessionEventSubscription: EventSubscription? = null init { // Initialize event bus if (!CDCEventBusProvider.isInitialized()) { CDCEventBusProvider.initialize() } // Manual subscription requires explicit cleanup sessionEventSubscription = subscribeToSessionEventsManual { event -> when (event) { is SessionEvent.SessionExpired -> handleSessionExpired() is SessionEvent.SessionRefreshed -> handleSessionRefreshed() is SessionEvent.ValidationSucceeded -> handleValidationSucceeded() else -> { /* Handle other events */ } } } } private fun handleSessionExpired() { authenticationFlowDelegate.handleSessionExpired() // Navigate to login screen } override fun onCleared() { // Important: Unsubscribe to prevent memory leaks sessionEventSubscription?.unsubscribe() sessionEventSubscription = null super.onCleared() } } ``` ## Event Scopes Events can be scoped for targeted distribution: - `EventScope.GLOBAL` - All subscribers receive the event (default) - `EventScope.SCOPED` - Only subscribers in a specific scope receive the event ```kotlin // Emit to all subscribers (default) emitSessionExpired(sessionId) // Emit to specific scope emitSessionExpired(sessionId, scope = EventScope.SCOPED) ``` ## Benefits ✅ **Decoupled Architecture** - Components don't need direct references to each other ✅ **Lifecycle-Aware** - Automatic cleanup prevents memory leaks in Activities/Fragments ✅ **Type-Safe** - Compile-time event type checking with sealed classes ✅ **Flexible** - Works with Activities, Fragments, ViewModels, and Services ✅ **Testable** - Easy to mock and test event handling ✅ **Thread-Safe** - Events dispatched on appropriate coroutine dispatchers ## Common Use Cases ### 1. Global Session Expiration Handling Handle session expiration across your entire app from a single ViewModel: ```kotlin class MainActivityViewModel : ViewModel() { init { subscribeToSessionEventsManual { event -> if (event is SessionEvent.SessionExpired) { // Clear app state clearUserData() // Navigate to login navigateToLogin() } } } } ``` ### 2. Push Notification Handling React to push notifications throughout your app: ```kotlin subscribeToMessageEvents { event -> when (event) { is MessageEvent.RemoteMessageReceived -> { // Show in-app notification showInAppNotification(event.data) } is MessageEvent.NotificationActionReceived -> { // Handle notification action handleNotificationAction(event.action, event.data) } else -> { /* Handle other events */ } } } ``` ### 3. Session Monitoring Monitor session health with validation events: ```kotlin subscribeToSessionEvents { event -> when (event) { is SessionEvent.ValidationStarted -> showLoadingIndicator() is SessionEvent.ValidationSucceeded -> hideLoadingIndicator() is SessionEvent.ValidationFailed -> { hideLoadingIndicator() showSessionWarning("Session validation failed: ${event.reason}") } else -> { /* Handle other events */ } } } ``` ## Important Notes ⚠️ **Initialization:** Call `CDCEventBusProvider.initialize()` once in your app (e.g., in MainActivity or Application class) ⚠️ **Manual Subscriptions:** Always call `unsubscribe()` on manual subscriptions to prevent memory leaks ⚠️ **Thread Safety:** Event handlers run on background threads (IO dispatcher) for manual subscriptions. Use appropriate dispatchers for UI updates. ⚠️ **Validation Events:** Only subscribe to validation events if you've enabled session validation via `registerForSessionValidation()` # Social Provider Authentication The SDK provides a flexible system for integrating social login providers (Google, Facebook, WeChat, Line, etc.) through the `IAuthenticationProvider` interface. The SDK is **intentionally decoupled** from third-party social SDKs, allowing you to use any version of social provider SDKs without compatibility issues. ## Architecture Overview **Key Principle:** The SDK doesn't include social provider SDKs (Facebook SDK, Google Sign-In, etc.). Instead, you implement the authentication logic in your app and provide the results to the SDK through a standardized interface. ``` Your App → Social Provider SDK → IAuthenticationProvider → CDC SDK → CDC Server ``` ## IAuthenticationProvider Interface Implement this interface to integrate any social provider that requires a native SDK: ```kotlin interface IAuthenticationProvider { fun getProvider(): String // Provider identifier (e.g., "facebook", "google") suspend fun signIn(hostActivity: ComponentActivity?): AuthenticatorProviderResult suspend fun signOut(hostActivity: ComponentActivity?) fun dispose() // Clean up resources } ``` ## AuthenticatorProviderResult This is the key object that bridges your provider implementation with the CDC SDK. It contains the authentication token in a JSON format that the CDC server validates. ```kotlin AuthenticatorProviderResult( provider = "facebook", // Provider identifier type = ProviderType.NATIVE, // Provider type providerSessions = providerSessionJSON // JSON with auth token(s) ) ``` ### Provider Session Format The `providerSessions` parameter is a JSON string containing authentication tokens. **The exact structure varies by provider** (Facebook uses `authToken`, Google uses `idToken`, etc.). **Important:** Each social provider has its own specific JSON structure required by CDC servers. Refer to the example implementations in the app module for the exact format: - **Facebook:** `app/.../provider/FacebookAuthenticationProvider.kt` - **Google:** `app/.../provider/GoogleAuthenticationProvider.kt` - **WeChat, Line:** Similar pattern with provider-specific token fields ## Provider Types ### 1. NATIVE Providers Social providers using their official native SDKs (Facebook, Google, WeChat, Line). **When to use:** When you want the best user experience with native UI and full SDK features. **Implementation required:** - Implement `IAuthenticationProvider` interface - Use the provider's native SDK for authentication - Generate correct `providerSessions` JSON - Return `AuthenticatorProviderResult` with `ProviderType.NATIVE` **Example providers in app module:** - `FacebookAuthenticationProvider.kt` - Facebook Login SDK - `GoogleAuthenticationProvider.kt` - Credential Manager API ### 2. WEB Providers Social providers without native SDKs, handled through OAuth in WebView. **When to use:** For providers like LinkedIn, Twitter, or any OAuth provider without a native Android SDK. **Implementation:** Use the built-in `WebAuthenticationProvider` - **no custom implementation needed**. ```kotlin // For providers without native SDKs (e.g., LinkedIn) val linkedInProvider = WebAuthenticationProvider( "linkedin", siteConfig, currentSession ) // Register and use like any other provider authenticationProviderMap["linkedin"] = linkedInProvider ``` The SDK automatically handles the OAuth flow in a WebView and extracts the session. ## Using Social Login Once you have implemented your authentication providers, use them through the SDK's provider API: ```kotlin // Create your provider instance (native or web) val facebookProvider = FacebookAuthenticationProvider() // Your implementation // OR val linkedInProvider = WebAuthenticationProvider("linkedin", siteConfig, session) // Execute social login authenticationService.authenticate().provider().signIn( hostActivity = activity, authenticationProvider = facebookProvider ) { onSuccess = { authSuccess -> navigateToMainScreen() } onError = { authError -> showError(authError.message) } onLinkingRequired = { context -> // Account conflict - user needs to link accounts showAccountLinkingScreen(context) } } ``` ### Provider Management How you manage and register providers depends on your app's architecture. The example app demonstrates one approach using a provider registry pattern in `AuthenticationFlowDelegate.kt`, but you can organize this however fits your architecture best. ## Complete Implementation Examples The example app includes complete, working implementations for reference: ### Files to Reference: - **Native Providers:** - `app/src/main/java/com/sap/cdc/bitsnbytes/feature/provider/FacebookAuthenticationProvider.kt` - `app/src/main/java/com/sap/cdc/bitsnbytes/feature/provider/GoogleAuthenticationProvider.kt` - **Provider Registration & Usage:** - `app/src/main/java/com/sap/cdc/bitsnbytes/feature/auth/AuthenticationFlowDelegate.kt` These files show: - How to integrate native SDKs (Facebook SDK, Google Credential Manager) - How to construct correct `providerSessions` JSON for each provider - How to handle errors with `ProviderException` - How to register and use providers in authentication flows ## Benefits of This Architecture ✅ **SDK Independence** - Use any version of social provider SDKs ✅ **Flexibility** - Add custom providers or use web-based providers easily ✅ **Maintainability** - Update social SDKs without SDK updates ✅ **Consistency** - Standardized interface for all providers ✅ **Testing** - Easy to mock providers for testing ✅ **Future-Proof** - New social providers can be added anytime ## Important Notes ⚠️ **Provider Session JSON:** Each provider requires a specific JSON structure. Always refer to the example implementations for the exact format. ⚠️ **Provider Names:** Use the exact provider name expected by CDC servers (e.g., "facebook", "google", not "Facebook" or "GOOGLE"). ⚠️ **Dependencies:** Add social provider SDKs to your **app's** `build.gradle.kts`, not the CDC SDK. The SDK remains decoupled. ⚠️ **WebAuthenticationProvider:** For providers without native SDKs (LinkedIn, Twitter, etc.), use the built-in `WebAuthenticationProvider` - no custom implementation needed. # Web Screen-Sets Web Screen-Sets provide customizable web-based authentication UI that integrates seamlessly with your Android app through the `WebBridgeJS` component. ## Basic Usage ```kotlin // 1. Create WebBridge instance val webBridgeJS = WebBridgeJS(authenticationService) // 2. Configure with obfuscation (optional) webBridgeJS.addConfig( WebBridgeJSConfig.Builder() .obfuscate(true) .build() ) // 3. Attach to WebView webBridgeJS.attachBridgeTo(webView) // 4. Set native social providers (optional) webBridgeJS.setNativeSocialProviders(authenticationProviderMap) // 5. Register event callbacks using ScreenSetsCallbacks webBridgeJS.attachCallbacks(ScreenSetsCallbacks().apply { onLoad = { event -> // Handle screen-set load println("ScreenSet loaded: ${event.screenSetId}") } onLogin = { event -> // Handle login success navigateToMainScreen() } onError = { error -> // Handle errors showError(error.message) } onFieldChanged = { event -> // Handle form field changes validateField(event) } }) // 6. Load screen-set webBridgeJS.load(webView, screenSetUrl) // 7. Clean up when done webBridgeJS.detachBridgeFrom(webView) webBridgeJS.detachCallbacks() ``` ## Available Events The `ScreenSetsCallbacks` provides handlers for all screen-set lifecycle events: **Lifecycle Events:** - `onBeforeScreenLoad` - Before screen loads - `onLoad` - Screen loaded and ready - `onAfterScreenLoad` - After screen fully rendered - `onHide` - Screen hidden or dismissed **Form Events:** - `onFieldChanged` - Form field value changed - `onBeforeValidation` - Before form validation - `onAfterValidation` - After form validation - `onBeforeSubmit` - Before form submission - `onSubmit` - Form submitted - `onAfterSubmit` - After form submission **Authentication Events:** - `onLoginStarted` - Login process initiated - `onLogin` - Login successful - `onLogout` - User logged out - `onAddConnection` - Social account connected - `onRemoveConnection` - Social account disconnected - `onCanceled` - User canceled the flow **Error Handling:** - `onError` - Error occurred - `onAnyEvent` - Universal handler for all events **Benefits:** - **Flexibility:** Customize authentication UI using HTML, CSS, and JavaScript - **Consistency:** Maintain consistent branding across platforms - **Reduced Development:** Leverage existing web technologies - **Native Integration:** Seamless communication between web and native code - **Type-Safe Events:** Strongly-typed event handling with ScreenSetsCallbacks # Example Application This repository includes a comprehensive example application demonstrating SDK integration with Jetpack Compose, MVVM architecture, and modern Android development practices. **See the [Example App Documentation](app/README.md) for:** - Complete architecture overview and patterns - Delegate-based state management approach - WebView integration in Compose - Real-world implementation examples - Step-by-step usage guides ## Navigation - 📱 [Example Application README](app/README.md) - Architecture, patterns, and implementation guide - 🔧 [Library Source Code](library/src) - SDK implementation details - 💬 [Contributing Guidelines](CONTRIBUTING.md) - How to contribute to this project # Support, Feedback, Contributing This project is open to feature requests/suggestions, bug reports, etc. via [GitHub issues](https://github.com/SAP/sap-customer-data-cloud-sdk-for-android/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, and additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md). # Security / Disclosure If you find any bug that may be a security problem, please follow our instructions at [in our security policy](https://github.com/SAP/sap-customer-data-cloud-sdk-for-android/security/policy) on how to report it. Please do not create GitHub issues for security-related doubts or problems. # Code of Conduct As members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to always abide by its [Code of Conduct](https://github.com/SAP/.github/blob/main/CODE_OF_CONDUCT.md). # Licensing Copyright 2024 SAP SE or an SAP affiliate company and sap-customer-data-cloud-sdk-for-android contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/SAP/sap-customer-data-cloud-sdk-for-android).