Skip to main content

Android SDK Setup Guide With SSO Authentication


Install the SDK

Octopus is available on Maven Central. Add the dependencies to your build.gradle file:

dependencies {
// Core SDK functionnalities
implementation("com.octopuscommunity:octopus-sdk:x.x.x")
// SDK UI Components (optional)
implementation("com.octopuscommunity:octopus-sdk-ui:x.x.x")
}

See the Octopus SDK GitHub Release section to get the latest published version.


Use the SDK

As early as possible in your code, you should initialize the OctopusSDK object.

This object is expecting two things:

  • the Octopus Community API key
  • the connection mode
warning

You are reading the SSO connection mode documentation, if your community is configured to use Octopus Authentication, please open the documentation for this mode.

You need to know the app managed fields (also called as associated fields) that your community is configured for.

As a reminder, every associated profile fields (nickname, picture and/or bio) of your users will be used in the Octopus Community profile of this user. The user will only be able to change it in your profile edition interface and the data will be synced to its community profile.
On the oposite, every dissociated profile fields will only be used as prefilled values during Octopus profile creation. After that, if a user changes its nickname in your app, it won't be reflected in Octopus Community, and the user will be able to change its community nickname in the community part.

At least one app managed field

If your community is having at least one associated field, you will have to create the SSO connection mode with the list of the associated fields.

Call the OctopusSDK initialization function in your Application's onCreate() block:

class YourApplication : Application() {

override fun onCreate() {
super.onCreate()

OctopusSDK.initialize(
context = this, // Application Context
apiKey = "YOUR_API_KEY",
connectionMode = ConnectionMode.SSO(
// The list of associated fields
appManagedFields = setOf(ProfileField.NICKNAME, ProfileField.AVATAR)
)
)
}
}

OR

No app managed fields

When there is no app managed fields (i.e. all fields are dissociated), the API is simpler since you only have to configure it in SSO connection mode.

Call the OctopusSDK initialization function in your Application's onCreate() block:

class YourApplication : Application() {

override fun onCreate() {
super.onCreate()

OctopusSDK.initialize(
context = this, // Application Context
apiKey = "YOUR_API_KEY",
connectionMode = ConnectionMode.SSO()
)
}
}

Since you’re using the sso connection mode, you’ll need to tell the SDK when your user is connected or disconnected.

The connectUser API lets you pass the userId. This userId is the string that will identify your user for Octopus. It needs to be unique and always refer to the same user.

You can also pass profile information (nickname, bio, picture, and whether the user is over 16 years old). These details will be used only to prefill the Octopus Community profile when the user performs their first action requiring authentication (such as creating a post or liking content). However, their community profile remains separate from yours, meaning any updates made to your profile will not be reflected in theirs, and vice versa.

Note that if you inform the SDK that a user is underage, they won't be able to create their community profile until they update their age in your app. This is why we strongly recommend passing null as the age information when a user is underaged (unless you are 100% certain the age is accurate). In this case, Octopus SDK will prompt the user to enter their age during the profile creation process.

Client User Token

For security reasons, to ensure that the connected user is legitimate, the API informing the SDK of a user’s connection includes a callback that provides a signed token for authentication. In other words, the SDK will request a token from you when needed to authenticate the user. Therefore, you must add a route like /generateOctopusSsoToken to your backend to generate this token. Follow the Generate a signed JWT for SSO guide for more information

Inform the SDK that your user is connected:

OctopusSDK.connectUser(
user = ClientUser(
userId = yourUser.id,
profile = ClientUser.Profile(
nickname = yourUser.name,
bio = yourUser.bio,
picture = yourUser.picture,
// Age Information:
// - LegalAgeReached = Your user is more than 16 years old
// - Underaged = Your user is less than 16 years old
// - null = You don't know
ageInformation = AgeInformation.LegalAgeReached
)
),
tokenProvider = {
// Return asynchronously this user token (suspended callback)
// by calling your /generateOctopusSsoToken
}
)

Inform the SDK that your user is disconnected:

OctopusSDK.disconnectUser()

Display the Octopus Community UI

Now that you have the SDK properly configured, you can add a button in your app that opens the Octopus Community UI.

Integrate Octopus composables navigation

Add the Octopus SDK composables to your navigation graph.

This function registers multiple composable() destinations in your NavGraphBuilder for the entire Octopus SDK navigation flow.

  1. Add the Octopus composables to your navigation graph within your actual NavHost:

    At least one app managed field

    If your community is having at least one associated field, you will have to call the octopusComposables with:

    • the block that will be called when OctopusSDK needs a logged in user. When this block is called, you should start to display your login process.
    • the block that will be called when the user tries to modify some fields related to its profile. When this block is called, you should open the profile edition. This block has a fieldToEdit: ProfileField optional parameter. It indicates the field that the user tapped to edit if there is one.
    val navController = rememberNavController()

    NavHost(navController) {
    composable<...> {
    }

    octopusComposables(
    navController = navController,
    onNavigateToLogin = {
    // Put the code here to open your login flow
    // example: navController.navigate(LoginScreen)
    },
    onNavigateToProfileEdit = { fieldToEdit ->
    // Put the code here to open your profile edition screen
    // `fieldToEdit` is the field that has been asked to be edited by the user. Null if the user tapped on "Edit my profile".
    // example: navController.navigate(ProfileScreen(focusNickname = fieldToEdit == ProfileField.NICKNAME)
    }
    )
    }

    OR

    No app managed fields

    When there is no app managed fields (i.e. all fields are dissociated), the API is simpler since it only requires a callback to display the login flow. When this block is called, you should start to display your login process.

    val navController = rememberNavController()

    NavHost(navController) {
    composable<...> {
    }

    octopusComposables(
    navController = navController,
    onNavigateToLogin = {
    // Put the code here to open your login flow
    // example: navController.navigate(LoginScreen)
    }
    )
    }
  2. Then, either:

    Embed the OctopusHomeContent or OctopusHomeScreen in your own composable
    NavHost(...) {
    composable<Community> {
    Box {
    OctopusTheme(...) {
    OctopusHomeContent(
    modifier = Modifier.fillMaxSize(),
    navController = navController
    )
    }
    }
    }

    octopusComposables(...)
    }

    OR

    Simply navigate to the Octopus Home route
    navController.navigate(OctopusDestination.Home)

Modify the theme

The Octopus SDK lets you modify its theme so its UI looks more like yours.

You can modify:

  • the colors:
    • the primary colors (a main color, a low contrast and a high contrast variations of the main color). Please note that the high contrast variation of the primary color is not used for the moment. If you do not pass a custom value for it, black/white default values will be used.
    • the color of the elements (mostly texts) displayed over the primary color. If you do not pass a custom value for it, white/black default value will be used.
  • the fonts. You can customize the styles (title1, body2, caption1...) used in the sdk. If you do not pass a custom value for it, default value will be used.
  • the logo. This is an image displayed on the Octopus home page and profile creation view. If you do not pass a custom image for it, Octopus logo value will be used.
  • the TopAppBar. You can customize the appearance, title, navigation icons, actions, and colors of the TopAppBar. If you do not pass a custom configuration for it, default TopAppBar will be used.

By default, Octopus will rely on your Application's MaterialTheme.colorScheme but you can customize the UI more precisely by surrounding composables with the OctopusTheme:

To do that, you can override the theme by passing it as environment object:

octopusComposables(
navController = navController,
container = { backStackEntry, content ->
OctopusTheme(
colorScheme = if (isSystemInDarkTheme()) {
octopusDarkColorScheme(
primary = yourDarkPrimaryColor, // Default: MaterialTheme.colorScheme.primary
primaryLow = lowContrastVersionOfYourPrimaryColor, // Default: MaterialTheme.colorScheme.primaryContainer
primaryHigh = highContrastVersionOfYourPrimaryColor, // Default: MaterialTheme.colorScheme.inversePrimary
onPrimary = yourDarkOnPrimaryColor, // Default: MaterialTheme.colorScheme.onPrimary
background = yourDarkBackgroundColor // Default: MaterialTheme.colorScheme.background
// See the complete list in the sources documentation
)
} else {
octopusLightColorScheme(
primary = yourLightPrimaryColor, // Default: MaterialTheme.colorScheme.primary
primaryLow = lowContrastVersionOfYourPrimaryColor, // Default: MaterialTheme.colorScheme.primaryContainer
primaryHigh = highContrastVersionOfYourPrimaryColor, // Default: MaterialThème.colorScheme.inversePrimary
onPrimary = yourLightOnPrimaryColor, // Default: MaterialTheme.colorScheme.onPrimary
background = yourLightBackgroundColor // Default: MaterialTheme.colorScheme.background
// See the complete list in the sources documentation
)
},
typography = OctopusTypographyDefaults.typography(
title1 = yourCustomTitle1Style, // Default: TextStyle(fontSize = 26.sp)
title2 = yourCustomTitle2Style // Default: TextStyle(fontSize = 22.sp)
// See the complete list in the sources documentation
),
drawables = OctopusDrawablesDefaults.drawables(
logo = painterResource(R.drawable.your_custom_logo), // Default: R.drawable.ic_octopus_logo
tint = yourLogoTintColor // Default: null (no image tinting)
),
topAppBar = OctopusTopAppBarDefaults.topAppBar(
title = { text ->
OctopusTopAppBarTitle(
text = text,
textStyle = yourCustomTitleStyle
)
},
colors = OctopusTopAppBarDefaults.colors(
containerColor = yourTopAppBarBackgroundColor,
contentColor = yourTopAppBarContentColor
)
// See the complete list in the sources documentation
)
) {
content()
}
}
)

All parameters of the Theme have default values. Only override the ones that you want to customize. On the following example, you are creating an OctopusTheme with default colors, default fonts except for the title1 and a custom logo:

OctopusTheme(
typography = OctopusTypographyDefaults.typography(
title1 = TextStyle(fontFamily = FontFamily.SansSerif, fontSize = 24.sp)
),
drawables = OctopusDrawablesDefaults.drawables(
logo = painterResource(R.drawable.your_custom_logo)
)
) {
OctopusHomeScreen(...)
}

Here is a summary of the impacts of the theme you choose: Light Mode Dark Mode

Here is a summary of the text styles used in the main screens of the SDK: Text Styles

Customize the TopAppBar

You can customize the appearance of the TopAppBar in the Octopus SDK screens by providing your own OctopusTopAppBar configuration when setting up the theme. We highly encourage you to choose a short text title (less than 18 characters).

OctopusTheme(
topAppBar = OctopusTopAppBarDefaults.topAppBar(
title = { text ->
OctopusTopAppBarTitle(
text = "Your TopAppBar Title", // Default: text
logo = OctopusDrawable(painterResource(R.drawable.your_logo)), // Default: OctopusTheme.drawables.logo
textStyle = yourTopAppBarTextStyle // Default: LocalTextStyle.current
)
},
colors = OctopusTopAppBarDefaults.colors(
containerColor = yourTopAppBarBackgroundColor, // Default: OctopusTheme.colorScheme.background
contentColor = yourTopAppBarContentColor // Default: OctopusTheme.colorScheme.gray900
),
navigationIcon = { type, onClick ->
IconButton(onClick = onClick) {
Icon(
imageVector = when (type) {
NavigationIconType.Back -> YourBackIcon
NavigationIconType.Close -> YourCloseIcon
},
contentDescription = when (type) {
NavigationIconType.Back -> "Navigate back"
NavigationIconType.Close -> "Close"
}
)
}
}
// See the complete list in the sources documentation
)
)
info

The OctopusTopAppBar will automatically use your theme's color scheme if no specific colors are provided, ensuring consistency across your app.

Advanced Screen-Based Theming

For more complex theming scenarios, you can customize any aspect of the Octopus theme (colors, typography, drawables, TopAppBar) based on the current screen:

octopusComposables(
navController = navController,
container = { backStackEntry, content ->
OctopusTheme(
colorScheme = when {
// Post Details with branded theme
backStackEntry.destination.hasRoute<OctopusDestination.PostDetails>() -> {
octopusColorScheme().copy(
background = if (isSystemInDarkTheme()) Color.Black else Color.White
)
}
else -> octopusColorScheme()
},
topAppBar = when {
// Home screen with custom TopAppBar theme
backStackEntry.destination.hasRoute<OctopusDestination.Home>() -> {
OctopusTopAppBarDefaults.topAppBar(
title = { text ->
OctopusTopAppBarTitle(text = "My Community")
}
)
}
else -> OctopusTopAppBarDefaults.topAppBar()
}
) {
content()
}
}
)

Push Notifications ≥ 1.4.0

To increase user engagement, you can provide informations to the Octopus SDK so your users can receive push notifications when other users interact with them inside the community.

note

Octopus SDK is not asking for push notification permissions, we let you handle that part where it makes more sense in your app.

If your app does not support Push Notifications yet, you can follow the official Firebase documentation.

Our servers need your service account's private key file to be authorized to send notifications to your app on your behalf.

If you don't have this JSON file yet, follow this tutorial
  • In the Firebase console, open Settings > Service Accounts
  • Click Generate New Private Key, then confirm by clicking Generate Key.
  • Securely store the JSON file containing the key.

More information can be found in the official Firebase documentation

Please send the json file using the online form sent by the Octopus team.

warning

This JSON file is different from the one you are using to configure Firebase in your app (google-services.json)

Once your project is correctly setup for push notifications, you should forward the Firebase Cloud Messaging Token (FCM Token) to the Octopus SDK:

class MessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)

// Register the new token with Octopus
OctopusSDK.registerNotificationsToken(token)
}
}
note
You are in charge of requesting the Notification permission and referencing your messaging service
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
if (checkSelfPermission(POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// Request launcher for notification permission
registerForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
callback = {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
OctopusSDK.registerNotificationsToken(task.result)
}
}
}
).launch(POST_NOTIFICATIONS)
}
<service
android:name=".notifications.MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

When you receive a notification message, first check if it is an Octopus notification by calling:

override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)

val isOctopusNotification = remoteMessage.data.isOctopusNotification
if(isOctopusNotification) {
...
}
}

If it's the case, display the Octopus Notification using the Notification Manager:

val octopusNotification = OctopusSDK.getOctopusNotification(data = remoteMessage.data)
if(octopusNotification != null) {
notificationManager.notify(
octopusNotification.id,
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notification)
.setColor(getColor(R.color.accent))
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
// Extension function to extract the title, text and deep link from the OctopusNotification
.setOctopusContent(
context = this,
activityClass = MainActivity::class,
octopusNotification = octopusNotification
).build()
)
}
note
  • The activityClass must be the one containing the compose content with the octopusNavigation() subgraph.
  • The setOctopusContent function is based on a deep link mechanism that will automatically launch the activity and navigate to the corresponding screen within the Octopus Navigation graph.
  • You can also handle the content manually by using the OctopusNotification's title, body and linkPath fields and configuring your own PendingIntent.

Not seen notifications ≥ 1.3.0

To increase user engagement, let your users know that they have new notifications from the Octopus notification center. Displaying a badge with the number of new notifications in your app can be a great way to suggest your users to look at what's new in the community again.

To do that, the Octopus SDK exposes a val notSeenNotificationsCount: Flow<Int> that you can collect:

OctopusSDK.notSeenNotificationsCount.collect {}

If you want to update this value with the latest count, simply call:

OctopusSDK.updateNotSeenNotificationsCount()

To see a full example of how you can achieve that, you can follow how it is done in the Samples, in the octopus-auth/fullscreen app.


Analytics

Octopus Community provides analytics to help you better understand your users' behavior within the community. To improve the quality of these analytics, we offer features that allow you to provide additional information about your users.

Custom events ≥ 1.4.0

You can tell the SDK to register a custom event. This event can be merged into the reports we provide.

OctopusSDK.track(
event = TrackerEvent.Custom(
name = "Purchase",
properties = mapOf(
"price" to TrackerEvent.Custom.Property(
value = String.format(Locale.US, "%.2f", 1.99)
),
"currency" to TrackerEvent.Custom.Property(value = "EUR"),
"product_id" to TrackerEvent.Custom.Property(value = "product1")
)
)
)

Configure Community Visibility ≥ 1.3.0

If you enable access to the community for only a subset of your users and want the analytics we provide to take this into account, you can inform the SDK accordingly.

This information is reset at each SDK launch so be sure to call the function everytime and as soon as possible after SDK init.

OctopusSDK.setHasAccessToCommunity(canAccessCommunity)

Follow the Samples

Want to see a code examples on how to use the SDK, no worries, we have that for you!

  1. First, clone the OctopusSDK project

    Android SDK

  1. Add those lines to the root project local.properties file:

    OCTOPUS_API_KEY=YOUR_API_KEY
    OCTOPUS_SSO_CLIENT_USER_TOKEN_SECRET=YOUR_USER_TOKEN_SECRET

    Replace YOUR_API_KEY with your own API key and YOUR_USER_TOKEN_SECRET with your own token secret.

  2. According to your connection mode: