> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gameball.co/llms.txt
> Use this file to discover all available pages before exploring further.

# How Do I Show the Profile Widget in the Android SDK (v4)?

> Display the Gameball customer profile widget in your Android app

# Open the Gameball Profile Widget

Use the Gameball profile widget to show customers their **points, rewards, tiers, leaderboards, and history** inside your Android app.

> [!NOTE]
>
> Before showing the profile widget, make sure the SDK is initialized and the customer has been registered using [Initialize SDK](https://docs.gameball.co/installation-guides/v4/android/initialize-sdk.md) and [Initialize Customer Profile](https://docs.gameball.co/installation-guides/v4/android/initialize-profile.md).

***

## Basic Usage

Create a `ShowProfileRequest` and call `showProfile` from `GameballApp`.

🔒

**Secure API Access:** To benefit from v4.1 secure API endpoints, pass the `sessionToken` parameter when showing the profile widget (required in upcoming SDK v4). This enables automatic routing to v4.1 secure endpoints. [Learn more about v4.1 →](https://docs.gameball.co/api-reference/introduction-v4.1.md)

```kotlin Kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .build()

GameballApp.getInstance(this).showProfile(
    this,
    profileRequest
)

// With session token override
GameballApp.getInstance(this).showProfile(
    this,
    profileRequest,
    sessionToken = "customer-token"
)
```

```java Java theme={null}
ShowProfileRequest profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .build();

GameballApp.getInstance(this).showProfile(
    this,
    profileRequest
);
```

***

## Request Parameters

**customerId** `string`

Unique identifier for the customer whose profile will be displayed. Optional (omit for guest mode).

**openDetail** `string`

Specific section to open inside the widget (for example, `"details_earn"`, `"details_redeem"`). If not set, the main profile view opens by default.

**hideNavigation** `boolean`

Hides the widget navigation bar when set to `true`. Defaults to `false`.

**showCloseButton** `boolean`

Shows a close button within the widget when set to `true`. Defaults to `false`.

**closeButtonColor** `string`

Hex color string for the close button (for example, `"#FF5733"`).\
Only applies when `showCloseButton` is `true`.

**sessionToken** `string`

Optional session token to override the global token for this specific request. Required in upcoming SDK v4.

**mobile** `string`

Customer mobile number, used for channel merging.

**email** `string`

Customer email address, used for channel merging.

**externalLinkCallback** `Callback\<String\>`

Handler for links the widget tags with `gbExternalBrowser=true`. When provided, the link is delegated to your app; otherwise the SDK opens it in the system browser.

**widgetEventCallback** `Callback\<Map\<String, Object\>\>`

Receives events the widget posts, such as game completion.

### Validation Rules

No required fields (guest mode supported). Provide `customerId` for authenticated profiles.

***

## Advanced Configuration

### Open a Specific Section

Use openDetail to deep link into a specific section (for example, “earn” details):

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .openDetail("details_earn")  // Open the earn points section
    .build()
```

### Guest Mode

```kotlin theme={null}
// No customerId required
val guestRequest = ShowProfileRequest.builder()
    .showCloseButton(true)
    .build()

GameballApp.getInstance(this).showProfile(
    this,
    guestRequest
)
```

### Custom Close Button

Show a custom-colored close button on the widget:

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .showCloseButton(true)
    .closeButtonColor("#FF5733")
    .build()
```

### Hide Navigation

> [!NOTE]
>
> Often paired with `openDetail` to guide users to a specific section when navigation is hidden.

Hide the widget’s internal navigation controls:

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .openDetail("details_earn")
    .hideNavigation(true)
    .build()
```

### Handle External Links

Links the widget tags with `gbExternalBrowser=true` open in the system browser by default. Provide an `externalLinkCallback` to handle them yourself instead (for example, to open an in-app browser or route to a custom screen):

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .externalLinkCallback(object : Callback<String> {
        override fun onSuccess(url: String) {
            // Open the link your own way, e.g. an in-app browser or a custom screen
            Log.d("GameballWidget", "External link: $url")
        }

        override fun onError(error: Throwable) {
            // Handle error
        }
    })
    .build()
```

> [!NOTE]
>
> When `externalLinkCallback` is set, the SDK delegates the link to your app and does not open the system browser.

### Channel Merging

Pass the customer's `mobile` and `email` so Gameball can merge the customer's channels:

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .mobile("+201234567890")
    .email("customer@example.com")
    .build()

GameballApp.getInstance(this).showProfile(this, profileRequest)
```

***

## Widget Events

Pass a `widgetEventCallback` to react to events the widget posts, such as a customer finishing a game. Each event arrives as a `Map<String, Object>` with a top-level `type` and a nested `metadata` map:

```kotlin theme={null}
val profileRequest = ShowProfileRequest.builder()
    .customerId("customer-123")
    .widgetEventCallback(object : Callback<Map<String, Any?>> {
        override fun onSuccess(event: Map<String, Any?>) {
            val type = event["type"] as? String                        // e.g. "gameCompleted"
            val metadata = event["metadata"] as? Map<*, *>

            if (type == "gameCompleted") {
                val hasWon = metadata?.get("hasWon") as? Boolean ?: false
                val rewardName = metadata?.get("rewardName") as? String
                if (hasWon) {
                    // Refresh the customer's balance, show a win animation, etc.
                }
            }
        }

        override fun onError(error: Throwable) {
            // Handle malformed event payloads
        }
    })
    .build()

GameballApp.getInstance(this).showProfile(this, profileRequest)
```

### `gameCompleted` Metadata

| Field | Type | Description |
| :- | :- | :- |
| `hasWon` | `Boolean` | Whether the customer won a reward this round |
| `rewardType` | `String?` | Reward category: `Default`, `Friend`, `Bonus`, `CustomText`, `Streak`, `NoReward` |
| `discountType` | `String?` | Coupon kind when the win is a coupon: `Fixed`, `Percentage`, `FreeShipping`, `FreeProduct`, `Custom`, `RechargeFixed`, `RechargePercentage`, `ExternalReward`. `null` for non-coupon wins |
| `rewardName` | `String?` | Localized, human-readable reward name |
| `campaignId` | `String` | Challenge or campaign identifier |
| `campaignType` | `String?` | Game type: `spinTheWheel`, `slotMachine`, `quiz`, `scratchCard`, `matchCards`, `catcher`, `ticTacToe`, `shooter`, `puzzle`, `tapTarget`, `highwayDrive` |

> [!NOTE]
>
> All `gameCompleted` values arrive as `String` or `Boolean`. There are no numeric fields.

***

## Dismiss the Widget

Dismiss the widget programmatically from your app. The call is a no-op when no widget is showing:

```kotlin theme={null}
GameballApp.getInstance(this).hideProfile()
```

The widget can also dismiss itself from the web side by calling `window.GameballWidget.closeWidget()`.

***

## Implementation Examples

### Show Profile from a Button

Display the profile widget when the user taps a button.

```kotlin Kotlin theme={null}
class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      findViewById<Button>(R.id.btnShowProfile).setOnClickListener {
          showCustomerProfile()
      }
  }

  private fun showCustomerProfile() {
      val profileRequest = ShowProfileRequest.builder()
          .customerId("customer-123")
          .build()

      GameballApp.getInstance(this).showProfile(
          profileRequest,
          object : Callback<Boolean> {
              override fun onSuccess(success: Boolean) {
                  // Profile displayed
              }

              override fun onError(error: Throwable) {
                  Toast.makeText(
                      this@MainActivity,
                      "Error: ${error.message}",
                      Toast.LENGTH_SHORT
                  ).show()
              }
          }
      )
  }
}
```

```java Java theme={null}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btnShowProfile).setOnClickListener(v -> {
            showCustomerProfile();
        });
    }

    private void showCustomerProfile() {
        ShowProfileRequest profileRequest = ShowProfileRequest.builder()
            .customerId("customer-123")
            .build();

        GameballApp.getInstance(this).showProfile(
            profileRequest,
            new Callback<Boolean>() {
                @Override
                public void onSuccess(Boolean success) {
                    // Profile displayed
                }

                @Override
                public void onError(Throwable error) {
                    Toast.makeText(
                        MainActivity.this,
                        "Error: " + error.getMessage(),
                        Toast.LENGTH_SHORT
                    ).show();
                }
            }
        );
    }
}
```

### Bottom Navigation Integration

Open the profile widget when the user taps a “Rewards” tab:

```kotlin theme={null}
bottomNavigationView.setOnItemSelectedListener { item ->
    when (item.itemId) {
        R.id.nav_rewards -> {
            val profileRequest = ShowProfileRequest.builder()
                .customerId(getCurrentCustomerId())
                .build()

            GameballApp.getInstance(this).showProfile(profileRequest, callback)
            true
        }
        else -> false
    }
}
```

***

## What the Widget Shows

The profile widget typically includes:

> [card] Customer Info
>
> Display name, points balance, and tier level.

> [card] Reward Campaigns
>
> Available rewards, missions, and how to earn them.

> [card] Leaderboard
>
> Customer ranking and top performers.

> [card] Transaction History
>
> History of points earned and redeemed.

> [!NOTE]
>
> The content, layout, and branding of the profile widget can be configured from your Gameball dashboard.

***

## Best Practices

**1. Make Access Easy**

Place entry points to the profile widget in prominent locations (for example, main navigation, profile screen, or a dedicated Rewards tab).

**2. Use Badges**

Show badges or indicators when a customer has new rewards or achievements to view.

**3. Use Deep Linking**

Use the `openDetail` parameter to open specific sections based on context (for example, after checkout, open the rewards section).

**4. Handle Errors Gracefully**

Always implement the error callback to show friendly messages if the widget fails to load (for example, due to network issues).

> [!TIP]
>
> Consider showing the profile widget immediately after a customer earns points or unlocks a reward to reinforce engagement.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.